linux驱动开发之常见面试问题
-
-
- 新增驱动的基本操作
- 寄存器基址怎么映射?
- probe里的常规操作。
- 驱动中通常会定义一个私有结构体,里面包含一些内核结构体,但注册的时候只注册了某个成员,怎么找到这个私有结构体。
-
- 什么是container_of
- 如何给应用层提供接口
-
- 什么是ioctl?
- 如何在shell下调用驱动
- 如何解决内核启动时卡死问题
- 简述MMU的工作原理
-
新增驱动的基本操作
先在设备树里新建一个节点,填写compatible和reg属性,然后在驱动里映射寄存器基址,后续就可以操作寄存器。
如果是linux发行版中没有的驱动
需要获取驱动源代码:
下载适用于你的内核版本的驱动源代码。
解压源代码:
使用tar命令解压下载的压缩包,如tar -xzvf driver.tar.gz
。
进入驱动目录:
使用cd命令切换到解压后的驱动目录。
配置驱动:
运行make config
或make menuconfig
命令配置驱动选项。
编译驱动:
运行make命令编译驱动。
安装驱动:
使用make install或手动将生成的模块文件复制到合适的目录,通常是/lib/modules//kernel/drivers
。
加载驱动:
运行modprobe driver_name
加载驱动,或者使用insmod driver.服务器托管网ko
命令。
检查驱动加载情况:
使用lsmod
命令查看已加载的模块,确保新驱动在列表中。
配置自动加载:
将驱动名称添加到/etc/modules
文件中,以便系统在启动时自动加载。
重启系统:
为了确保新驱动在系统启动时自动加载,最好重启系统。
寄存器基址怎么映射?
A:先用platform_get_resource
获取IORESOURCE_MEM
资源,然后用devm_ioremap_resource
将基址映射为虚拟地址。
probe里的常规操作。
A:通常一个驱动都会有时钟,所以probe里映射完基址后,通常要进行时钟和复位的操作。调用devm_clk_get
获取时钟源,然后调用devm_clk_prepare_enbale
使能时钟,复位的话先调用devm_reset_control_get
获取复位源,然后用reset_control_reset
复位。
驱动中通常会定义一个私有结构体,里面包含一些内核结构体,但注册的时候只注册了某个成员,怎么找到这个私有结构体。
A:可以通过container_of
宏找到这个私有结构体的指针。
什么是container_of
container_of
是 Linux 内核中一个常用的宏,用于从一个结构体中的某个字段获取该结构体的指针。这在实现容器数据结构时非常有用,尤其是在链表中。
具体的使用格式如下:
#include
#define container_of(ptr, type, member)
({
const typeof(((type *)0)->member) *__mptr = (ptr);
(type *)((char *)__mptr - offsetof(type, member));
})
其中:
ptr
是指向结构体中某个成员的指针。
type
是结构体的类型。
member
是结构体中的成员名。
这宏的作用是返回包含给定成员的结构体的指针。
这个宏在内核中广泛用于实现各种数据结构,尤其是在实现链表时,通过链表节点的成员指针可以找到整个结构体的指针。具体详细讲解可以自行百度。
如何给应用层提供接口
A:驱动给应用层提供接口,一般都是通过ioctl
接口,应用层传入一个结构体,驱动解析
什么是ioctl?
ioctl(Input/Output Control)
是Linux系统中的一个系统调用,用于对设备进行输入/输出的控制。它允许用户空间程序通过系统调用与设备驱动程序进行通信,提供一种灵活的设备控制机制。ioctl
的使用方式是通过命令(或请求号)来指定要执行的操作,以及相应的参数。
基本的ioctl原型如下:
int ioctl(int fd, unsigned long request, ...);
服务器托管网
fd
是文件描述符,指向要进行控制的设备或文件。
request
是命令或请求号,用于指定要执行的操作。
...
是可选参数,用于传递与请求相关的参数。
一些常见的ioctl使用场景包括:
-
设备设置:
设置设备的一些特性,例如串口的波特率、数据位、停止位等。 -
IO模式切换:
控制设备的读写模式,例如切换到阻塞或非阻塞模式。 -
硬件信息查询:
获取设备的硬件信息,例如查询磁盘的几何信息。 -
字符设备操作:
对字符设备进行一些特定的操作,例如终端的清屏、设置光标位置等。 -
网络套接字设置:
配置网络套接字的选项,例如设置套接字的超时时间、设置广播选项等。 -
图形设备控制:
控制图形设备的一些操作,例如设置显示器的分辨率、颜色深度等。
使用ioctl
需要查阅相关设备或系统调用的文档,以了解支持的命令和参数。在驱动程序的开发中,通常会实现相应的ioctl
处理函数,用于处理不同的命令。
总体而言,ioctl
提供了一个灵活的接口,使用户空间程序能够与内核中的设备驱动进行通信和控制。
如何在shell下调用驱动
A:可以提供procfs
、sysfs
或者debugfs
这三个虚拟文件系统接口,根据具体用途提供
procfs
接口一般是系统性的信息,主要是用来查看的,例如内存信息。
sysfs
接口更多用于与驱动交互,可以传参给驱动,修改驱动中一些变量。
debugfs
一般是用来调试用的,需要挂载debugfs
。
如何解决内核启动时卡死问题
A:卡住或者崩了,需要分析卡在哪里,从而找到原因。
在内核的initcall
初始化函数中加打印,把initcall
的level
和函数指针打印出来,看内核跑到了哪个等级的初始化。
level
等级是知道了,但这个等级执行的函数太多,而且打印出来的是地址,怎么知道具体跑到哪个函数?
A:把内核编译出来的vmlinux
文件反汇编,反汇编文件包含函数名和对应地址,根据地址查找。
vmlinux 是 Linux 内核编译后的可执行文件,包含了完整的内核代码和数据。如果你想进行 vmlinux 文件的反汇编,可以使用工具如 objdump 或 gdb。
以下是使用 objdump 进行 vmlinux 反汇编的基本步骤:
objdump -D vmlinux > vmlinux_disassembly.txt
这将会生成一个包含反汇编代码的文本文件 vmlinux_disassembly.txt。你可以使用文本编辑器查看或搜索其中的代码。
如果你想在 gdb
中进行交互式的反汇编,可以按照以下步骤:
gdb vmlinux
在 GDB 中,你可以使用 disassemble
命令来查看反汇编代码:
(gdb) disassemble
你也可以指定地址范围来查看特定部分的代码:
(gdb) disassemble 0xffffffff81000000, 0xffffffff81010000
请注意,反汇编的结果可能会很庞大,因为它包含整个内核的代码。选择性地查看特定的函数或区域可能更有帮助。
内核调试方法?
A:printk、BUG_ON、devmem、dump_statck…
printk
有打印等级,用法和printf一样,可以用pr_info、pr_err这些不同等级的函数加打印。在shell中可以通过echo的方式控制printk等级
devmem
是一个命令,它可以在shell下直接读写寄存器
devmem应用程序会打开/dev/mem
节点,这个设备实现了mmap接口,devmem应用程序打开/dev/mem后,会调用mmap函数将寄存器物理地址映射到用户空间,所以可以直接读写寄存器
dump_stack
函数可以打印函数调用栈,可以分析函数调用关系
可以用工具链的add2line
,查找地址对应的符号,就能看到函数名
内核配置打开CONFIG_KALLSYMS
,这个配置是编入符号表,选上后,dump_stack
就可以清楚看到调用关系和符合偏移。
简述MMU的工作原理
A:在一个三级页表的内存管理系统中,MMU(内存管理单元)通过访问页表基址寄存器(Page Table Base Register,简称PTBR)获取一级页表的基地址,然后通过虚拟地址中的一级页表索引(Page Global Directory Index,简称PGD index)找到相应的二级页表。
接着,MMU利用二级页表基址和虚拟地址中的二级页表索引(Page Table Entry Index,简称PTE index)找到相应的页表项(Page Table Entry,简称PTE)。在这个例子中,PTE 存储的是物理页框号(Page Frame Number,简称PFN),即该虚拟页对应的物理页框。
最后,MMU将物理页框号(PFN)与虚拟地址中的页内偏移相加,得到实际的物理地址。
这个过程可以总结为以下步骤:
MMU根据虚拟地址的高位来查找一级页表项(PGD),获取二级页表的基地址。
MMU再根据虚拟地址的中间位来查找二级页表项(PTE),获取物理页框号(PFN)。
MMU将物理页框号与虚拟地址的低位偏移相加,得到最终的物理地址。
这样,通过多级页表的层级结构,MMU能够实现对大型地址空间的映射和管理,从而提供了一种灵活、高效的内存管理方案。
页表基址寄存器存储第一级页表的基地址。
页表是软件创建的。MMU只是通过页表,将虚拟地址转换为了物理地址。
页表在物理内存中。
CPU先访问TLB,如果TLB中存在这个地址,则直接从TLB中取地址。如果没有,再访问内存,读取页表,将虚拟地址转为物理地址,从而访问到内存。
TLB在MMU中,本质是一块cache。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
JS解混淆 最近在整理之前和一些同伴的分享资料,发现时间已经过了好久,特此整理一些有价值的分享记录。 JS混淆 学习js混淆可以逆向分析混淆和加密过程,实战可用于爬虫和渗透信息获取 本文档用于初步介绍js混淆的基础概念以及如何解混淆、调试,便于干掉反爬虫和渗透…