Linux 驱动 面试题总结

1、查看驱动模块中打印信息应该使用什么命令?如何查看内核中已有的字符设备的信息?如何查看正在使用的有哪些中断号?

1) 查看驱动模块中打印信息的命令:dmesg

2) 查看字符设备信息可以用lsmod 和modprobe,lsmod可以查看模块的依赖关系,modprobe在加载模块时会加载其他依赖的模块。

3) 显示当前使用的中断号cat /proc/interrupt

2、 Linux设备中字符设备与块设备有什么主要的区别?请分别列举一些实际的设备说出它们是属于哪一类设备。

    字符设备:字符设备是个能够像字节流(类似文件)一样被访问的设备,由字符设备驱动程序来实现这种特性。字符设备驱动程序通常至少实现open,close,read和write系统调用。字符终端、串口、鼠标、键盘、摄像头等就是典型的字符设备。

    块设备:和字符设备类似,块设备也是通过/dev目录下的文件系统节点来访问。块设备上能够容纳文件系统,如:u盘,SD卡,磁盘等。

    字符设备和块设备的区别仅仅在于内核内部管理数据的方式,也就是内核及驱动程序之间的软件接口,而这些不同对用户来讲是透明的。在内核中,和字符驱动程序相比,块驱动程序具有完全不同的接口。
3、Linux中引入模块机制有什么好处?

       首先,模块是预先注册自己以便服务于将来的某个请求,然后他的初始化函数就立即结束。换句话说,模块初始化函数的任务就是为以后调用函数预先作准备。

好处:

1) 应用程序在退出时,可以不管资源的释放或者其他的清除工作,但是模块的退出函数却必须仔细此撤销初始化函数所作的一切。

2) 该机制有助于缩短模块的开发周期。即:注册和卸载都很灵活方便。

4、copy_to_user()和copy_from_user()主要用于实现什么功能?一般用于file_operations结构的哪些函数里面?

      由于内核空间和用户空间是不能互相访问的,如果需要访问就必须借助内核函数进行数据读写。copy_to_user():完成内核空间到用户空间的复制,copy_from_user():是完成用户空间到内核空间的复制。一般用于file_operations结构里的read,write,ioctl等内存数据交换作用的函数。当然,如果ioctl没有用到内存数据复制,那么就不会用到这两个函数。

5、请简述主设备号和次设备号的用途。如果执行mknod chartest c 4 64,创建chartest设备。请分析chartest使用的是那一类设备驱动程序。

1)主设备号:主设备号标识设备对应的驱动程序。虽然现代的linux内核允许多个驱动程序共享主设备号,但我们看待的大多数设备仍然按照“一个主设备对应一个驱动程序”的原则组织。

   次设备号:次设备号由内核使用,用于正确确定设备文件所指的设备。依赖于驱动程序的编写方式,我们可以通过次设备号获得一个指向内核设备的直接指针,也可将此设备号当作设备本地数组的索引。

2)chartest 由驱动程序4管理,该文件所指的设备是64号设备。(感觉类似于串口终端或者字符设备终端)。

6、设备驱动程序中如何注册一个字符设备?分别解释一下它的几个参数的含义。

内核提供了三个函数来注册一组字符设备编号,这三个函数分别是 register_chrdev_region()、alloc_chrdev_region() 和 register_chrdev()。

(1)register_chrdev  比较老的内核注册的形式   早期的驱动
(2)register_chrdev_region/alloc_chrdev_region + cdev  新的驱动形式

区别:register_chrdev()函数是老版本里面的设备号注册函数,可以实现静态和动态注册两种方法,主要是通过给定的主设备号是否为0来进行区别,为0的时候为动态注册。register_chrdev_region以及alloc_chrdev_region就是将上述函数的静态和动态注册设备号进行了拆分的强化。

register_chrdev_region(dev_t first,unsigned int count,char *name)
First :要分配的设备编号范围的初始值, 这组连续设备号的起始设备号, 相当于register_chrdev()中主设备号
Count:连续编号范围.   是这组设备号的大小(也是次设备号的个数)
Name:编号相关联的设备名称. (/proc/devices); 本组设备的驱动名称

alloc_chrdev_region函数,来让内核自动给我们分配设备号

(1)register_chrdev_region是在事先知道要使用的主、次设备号时使用的;要先查看cat /proc/devices去查看没有使用的。
(2)更简便、更智能的方法是让内核给我们自动分配一个主设备号,使用alloc_chrdev_region就可以自动分配了。
(3)自动分配的设备号,我们必须去知道他的主次设备号,否则后面没法去mknod创建他对应的设备文件。

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

1:这个函数的第一个参数,是输出型参数,获得一个分配到的设备号。可以用MAJOR宏和MINOR宏,将主设备号和次设备号,提取打印出来,看是自动分配的是多少,方便我们在mknod创建设备文件时用到主设备号和次设备号。 mknod /dev/xxx c 主设备号 次设备号

2:第二个参数:次设备号的基准,从第几个次设备号开始分配。

3:第三个参数:次设备号的个数。

4:第四个参数:驱动的名字。

5:返回值:小于0,则错误,自动分配设备号错误。否则分配得到的设备号就被第一个参数带出来。

7、请简述中断于DMA的区别。Linux设备驱动程序中,使用哪个函数注册和注销中断处理程序?

1)DMA:是一种无须CPU的参与就可以让外设与系统内存之间进行双向数据传输的硬件机制,使用DMA可以使系统CPU从实际的I/O数据传输过程中摆脱出来,从而大大提高系统的吞吐率.

      中断:是指CPU在执行程序的过程中,出现了某些突发事件时CPU必须暂停执行当前的程序,转去处理突发事件,处理完毕后CPU又返回源程序被中断的位置并继续执行。

     所以中断和DMA的区别就是DMA不需CPU参与而中断是需要CPU参与的。

2)中断注册函数和中断注销函数

注册中断:

int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *dev_name, void *dev_id);

参数意义依次是:中断号,中断处理函数,中断管理有关的掩码,中断请求设备名,中断信号线。

过程是:dev_name设备请求中断->cpu分配中断号->设置中断管理的掩码->分配中断信号线->处理中断函数->完成之后再根据设置情况返回原处理程序处继续处理程序。

注销中断

Void free_irq(unsigned int irq, void *dev_id);

释放中断和中断信号线

8、中断和轮询哪个效率高?怎样决定是采用中断方式还是采用轮询方式去实现驱动?

     中断是CPU处于被动状态下来接受设备的信号,而轮询是CPU主动去查询该设备是否有请求。凡事都是两面性,所以,看效率不能简单的说那个效率高。如果是请求设备是一个频繁请求cpu的设备,或者有大量数据请求的网络设备,那么轮询的效率是比中断高。如果是一般设备,并且该设备请求cpu的频率比较底,则用中断效率要高一些。主要是看请求频率。

9、简单描述在网络设备cs8900的驱动设计中, 发送数据frame和接收数据frame的过程。

1)发送流程如下:

(1) 网络设备驱动程序从上层协议传递过来的sk_buff参数获得数据包的有效数据和长度,将有效数据放入临时缓冲区。
(2) 对于以太网,如果有效数据的长度小于以太网冲突检测所要求的数据桢的最小长度,则给临时缓冲区的末尾填充0
(3) 设置硬件寄存器,驱使网络设备进行数据发送操作。

2)接收流程

   网络设备接收数据主要是由中断引发设备的中断处理函数,中断处理函数判断中断类型,如果为接收中断,则读取接受到的数据,分配sk_buff数据结构和数据缓冲区,将接收到的数据复制到数据缓冲区,并调用netif_rx()函数将sk_buff传递给上层协议。


10、Cs8900.c的驱动中,发送数据frame的过程为什么需要关中断?接收数据frame的过程为什么不需要关中断?

       在发送过程中是不能被打断的,在发送的过程中,不关中断,这时候如果有一个中断到来,那么cpu有可能会去相应该中断,如果该中断需要改写的数据是发送数据的缓冲区,那么缓冲区将被改写,这样即使cpu相应完毕该中断,再发送数据,接收方也不认识该数据不能接收。

      在接收数据的时候,需要打开中断,是因为要及时的相应接收到的数据。如果关闭该中断,那么接收方有可能因为相应优先级高的中断而接收不到该数据。

11、简单描述skbuff这个数据结构在网络结构中所起到的作用,为什么需要一个skbuff,它的分配和释放主要都在什么部位

        sk_buff结构非常重要,它的含义为“套接字缓冲区”,用于在linux网络子系统中的各层之间传递数据。

当发送数据包时,linux内核的网络处理模块必须建立一个包含要传输的数据包的sk_buff,然后将sk_buff递交给下层,各层在sk_buff中添加不同的协议头直至交给网络设备发送。同样的,当网络设备从网络媒介上接受到数据包后,它必须将接受到的数据转换为sk_buff数据结构并传递给上层,各层抛去相应的协议头直至交给用户。分配sk_buff在接受一开始就应该分配,在发送完毕数据之后可以释放sk_buff

12、字符型驱动设备怎么创建设备文件

        手动创建:mknod /dev/led c 250 0    其中dev/led 为设备节点 c 代表字符设备 250代表主设备号 0代表次设备号

       还有UDEV/MDEV自动创建设备文件的方式,UDEV/MDEV是运行在用户态的程序,可以动态管理设备文件,包括创建和删除设备文件,运行在用户态意味着系统要运行之后。在  /etc/init.d/rcS 脚本文件中会执行 mdev -s 自动创建设备节点。

13、写一个中断服务需要注意哪些?如果中断产生之后要做比较多的事情你是怎么做的?

      中断处理例程应该尽量短,把能放在后半段(tasklet,等待队列等)的任务尽量放在后半段。

第一:写一个中断服务程序要注意快进快出,在中断服务程序里面尽量快速采集信息,包括硬件信息,然后退出中断,要做其它事情可以使用工作队列或者tasklet方式。也就是中断上半部和下半部。

第二:中断服务程序中不能有阻塞操作。应为中断期间是完全占用CPU的(即不存在内核调度),中断被阻塞住,其他进程将无法操作;

第三:中断服务程序注意返回值,要用操作系统定义的宏做为返回值,而不是自己定义的OK,FAIL之类的。

14、自旋锁和信号量在互斥使用时需要注意哪些?在中断服务程序里面的互斥是使用自旋锁还是信号量?还是两者都能用?为什么?

      使用自旋锁的进程不能睡眠,使用信号量的进程可以睡眠。

      中断服务例程中的互斥使用的是自旋锁,原因是在中断处理例程中,硬中断是关闭的;但是要注意这样会丢失可能到来的中断。

15、原子操作你怎么理解?为了实现一个互斥,自己定义一个变量作为标记来作为一个资源只有一个使用者行不行?

        原子操作指的是无法被打断的操作。

        第二句话的意思是:

  定义一个变量,比如 int flag =0;

   if(flag == 0)
  {
       flag = 1;


       操作临界区;
      flag = 0;
   }

16、insmod 一个驱动模块,会执行模块中的哪个函数?rmmod呢?这两个函数在设计上要注意哪些?遇到过卸载驱动出现异常没?是什么问题引起的?

        insmod调用init函数,rmmod调用exit函数。这两个函数在设计时要注意什么?卸载模块时曾出现卸载失败的情形,原因是存在进程正在使用模块,检查代码后发现产生了死锁的问题。

      要注意在init函数中申请的资源在exit函数中要释放,包括存储,ioremap,定时器,工作队列等等。也就是一个模块注册进内核,退出内核时要清理所带来的影响,带走一切不留下一点痕迹。

17、驱动中操作物理绝对地址为什么要先ioremap?

        因为内核没有办法直接访问物理内存地址,必须先通过ioremap获得对应的虚拟地址。

18、设备驱动模型三个重要成员是?platfoem总线的匹配规则是?在具体应用上要不要先注册驱动再注册设备?有先后顺序没?

         设备驱动模型三个重要成员是 总线、设备、驱动;

         platfoem总线的匹配规则是:要匹配的设备和驱动都要注册;

19、linux内核里面,内存申请有哪几个函数,各自的区别?

         Kmalloc()   __get_free_page()  mempool_create()  

20、 IRQ和FIQ有什么区别,在CPU里面是是怎么做的?

        简单的对比的话就是FIQ比IRQ快,FIQ比IRQ有更高优先级,如果FIQ和IRQ同时产生,那么FIQ先处理。

        当CPU处于FIQ模式处理FIQ中断的过程中,预取指令异常,未定义指令异常,软件中断全被禁止,所有的中断被屏蔽。所以FIQ就会很快执行,不会被其他异常或者中断打断,所以它又比IRQ快了。而IRQ不一样,当ARM处理IRQ模式处理IRQ中断时,如果来了一个FIQ中断请求,那正在执行的IRQ中断处理程序会被抢断,ARM切换到FIQ模式去执行这个FIQ,所以FIQ比IRQ快多了。另外FIQ的入口地址是0x1c,IRQ的入口地址是0x18。

21    int *a;     char *b;    a 和 b本身是什么类型?

a和b本身就是个int型,表示一个地址。


22、 中断的上半部分和下半部分的问题:讲下分成上半部分和下半部分的原因,为何要分?讲下如何实现?

         上半部分执行与硬件相关的处理要求快, 而有些驱动在中断处理程序中又需要完成大量工作,这构成矛盾,所以Linux有所谓的bottom half机制,中断处理程序中所有不要求立即完成的,在开中断的环境下,由底半程序随后完成.

         Linux的底半处理实际上是建立在内核的软中断机制上的.如何实现该机制?

两种方式

【tasklet  工作队列】

1.定义和初始化

struct tasklet_struct tlet;

tasklet_init(&tlet, jit_tasklet_fn, (unsigned long) data);
参数
第一个:定义的tasklet变量
第二个:函数
第三个:数据  传递给回调函数的数据

2. 定义函数

void jit_tasklet_fn(unsigned long arg)
{
        //中断的底半部 执行该函数的时候,已经出中断了
        printk("in jit_tasklet_fn  jiffies=%ld\n",jiffies);
}

3. 在需要调度的地方调用以下函数

tasklet_schedule(&tlet);

   一般在中断函数当中调度在不晚于下一个时钟滴答之前执行
【tasklet 和定期器的区别】

1. 执行时间

定时器的执行:时间是确定的
tasklet       :不确定的

2.tasklet 执行耗时的操作的

23、内核函数mmap的实现原理,机制?

         mmap函数实现把一个文件映射到一个内存区域,从而我们可以像读写内存一样读写文件,他比单纯调用read/write也要快上许多。在某些时候我们可以把内存的内容拷贝到一个文件中实现内存备份,当然,也可以把文件的内容映射到内存来恢复某些服务。另外,mmap实现共享内存也是其主要应用之一,mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。

24、驱动里面为什么要有并发、互斥的控制?如何实现?讲个例子?

       并发(concurrency)指的是多个执行单元同时、并行被执行,而并发的执行单元对 共 享资源(硬件资源和软件上的全局变量、静态变量等)的访问则很容易导致竞态(race conditions) 。 解决竞态问题的途径是保证对共享资源的互斥访问, 所谓互斥访问就是指一个执行单 元 在访问共享资源的时候,其他的执行单元都被禁止访问。 访问共享资源的代码区域被称为临界区, 临界区需要以某种互斥机 制加以保护, 中断屏蔽, 原子操作,自旋锁,和信号量都是 linux 设备驱动中可采用的互斥途径。

25、spinlock自旋锁是如何实现的?

       自旋锁在同一时刻只能被最多一个内核任务持有,所以一个时刻只有一个线程允许存在于临界区中。这点可以应用在多处理机器、或运行在单处理器上的抢占式内核中需要的锁定服务。

26、信号量简介

        这里也介绍下信号量的概念,因为它的用法和自旋锁有相似的地方。

       Linux中的信号量是一种睡眠锁。如果有一个任务试图获得一个已被持有的信号量时,信号量会将其推入等待队列,然后让其睡眠。这时处理器获得自由去执行其它代码。当持有信号量的进程将信号量释放后,在等待队列中的一个任务将被唤醒,从而便可以获得这个信号量。

27、 任务调度的机制?

       Linux 根据policy从整体上区分实时进程和普通进程,因为实时进程和普通进程度调度是不同的,它们两者之间,实时进程应该先于普通进程而运行,然后,对于同一类型的不同进程,采用不同的标准来选择进程: 

policy的取值会有以下可能:

SCHED_OTHER 分时调度策略,(默认的)
SCHED_FIFO实时调度策略,先到先服务
SCHED_RR实时调度策略,时间片轮转 实时进程将得到优先调用,实时进程根据实时优先级决定调度权值,分时进程则通过nice和counter值决定权值,nice越小,counter越大,被调度的概率越大,也就是曾经使用了cpu最少的进程将会得到优先调度。 
SHCED_RR和SCHED_FIFO的不同:当采用SHCED_RR策略的进程的时间片用完,系统将重新分配时间片,并置于就绪队列尾。放在队列尾保证了所有具有相同优先级的RR任务的调度公平。  
SCHED_FIFO一旦占用cpu则一直运行。一直运行直到有 更高优先级任务到达或自己放弃 。 
如果有相同优先级的实时进程(根据优先级计算的调度权值是一样的)已经准备好,FIFO时必须等待该进程主动放弃后才可以运行这个优先级相同的任务。而RR可以让每个任务都执行一段时间。
 

28、什么是GPIO?

         general purpose input/output  

         GPIO是相对于芯片本身而言的,如某个管脚是芯片的GPIO脚,则该脚可作为输入或输出高或低电平使用,当然某个脚具有复用的功能,即可做GPIO也可做其他用途。 也就是说你可以把这些引脚拿来用作任何一般用途的输入输出,例如用一根引脚连到led的一极来控制它的亮灭,也可以用一根(一些)引脚连到一个传感器上以获得该传感器的状态,这给cpu提供了一个方便的控制周边设备的途经。如果没有足够多的gpio管脚,在控制一些外围设备时就会力有不逮,这时可采取的方案是使用CPLD来帮助管理。

29、在Linux C中,ls这个命令是怎么被执行的?

        使用fork创建一个进程或exec函数族覆盖原进程。

30、LINUX下的Socket套接字和Windows下的WinSock有什么共同点?请从C/C++语言

a)都基于TCP/IP协议,都提供了面向连接的TCP SOCK和无连接的UDP SOCK。

b)都是一个sock结构体。

c)都是使用sock文件句柄进行访问。

d)都具有缓冲机制。

                
31、一个计划跑LINUX系统的ARM系统把bootloader烧录进去后,上电后串口上没有任何输出,硬件和软件各应该去检查什么?   

 提示: 1.跑LINUX的系统一般都需要外扩DRAM,一般的系统也经常有NOR或NAND FLASH          

        bootloader一般是由汇编和C编写的程序

32.  怎样申请大块内核内存?

       vmalloc

33.linux编译时用到的参数含义及?

    https://blog.csdn.net/taoyanqi8932/article/details/51758722

34. 内核配置编译及Makefile?

      https://www.cnblogs.com/CrazyCatJack/p/6121231.html

35.谈谈对Volatile关键字的理解?

https://blog.csdn.net/kai_zone/article/details/77965302

36.  framebuffer机制?

      Linux抽象出FrameBuffer这个设备来供用户态进程实现直接写屏。Framebuffer机制模仿显卡的功能,将显卡硬件结构抽象掉,可以通过Framebuffer的读写直接对显存进行操作。用户可以将Framebuffer看成是显示内存的一个映像,通过mmap将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节,这些都是由Framebuffer设备驱动来完成的。通过mmap调用把显卡的物理内存空间映射到用户空间
 

Guess you like

Origin blog.csdn.net/ggggyj/article/details/86238395