嵌⼊式常见⾯试题总结100题(上)
⽬录
1,字符型驱动设备是怎么创建设备⽂件的,就是/dev/下⾯的设备⽂件,供上层应⽤程序打开使⽤的⽂件?
答:⽅式⼀(⼿动):mknod命令结合设备的主设备号和次设备号,可创建⼀个设备⽂件;
⽅式⼆(⾃动):UDEV/MDEV⾃动创建设备⽂件的⽅式,UDEV/MDEV是运⾏在⽤户态的程序,可以动态管理设备⽂件,包括创建和删除设备⽂件,运⾏在⽤户态意味着系统要运⾏之后;
⽅式三(⾃动):在系统启动期间还有devfs创建了设备⽂件。
2,写⼀个中断服务需要注意哪些?如果中断产⽣之后要做⽐较多的事情你是怎么做的?
答:(1)中断处理例程应该尽量短,注意快进快出,在中断服务程序⾥⾯尽量快速采集信息,包括硬件信息,然后推出中断,要做其它事情可以使⽤⼯作队列或者tasklet⽅式。也就是中断上半部和下半部;
(2)中断服务程序中不能有阻塞操作;
(3)中断服务程序注意返回值,要⽤操作系统定义的宏做为返回值,⽽不是⾃⼰定义的FAIL,OK,之类的。
嵌入式系统是什么意思
中断服务程序(ISR,Interrupt Service Routines):处理器处理"急件",可理解为是⼀种服务,是通过执⾏事先编好的某个特定的程序来完成的。
3,⾃旋锁和信号量在互斥使⽤时需要注意哪些?在中断服务程序⾥⾯的互斥是使⽤⾃旋锁还是信号量?还是两者都能⽤?为什么?
答:使⽤⾃旋锁的进程不能睡眠,使⽤信号量的进程可以睡眠。中断服务例程中的互斥使⽤的是⾃旋锁,原因是在中断处理例程中,硬中断是关闭的,这样会丢失可能到来的中断。
⾃旋锁:它是为实现保护共享资源⽽提出⼀种锁机制。其实,⾃旋锁与⽐较类似,它们都是为了解决对某项资源的互斥使⽤。⽆论是互斥锁,还是⾃旋锁,在任何时刻,最多只能有⼀个保持者,也就说,在任何时刻最多只能有⼀个执⾏单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占⽤,资源申请者只能进⼊睡眠状态。但是⾃旋锁不会引起调⽤者睡眠,如果⾃旋锁已经被别的执⾏单元保持,调⽤者就⼀直循环在那⾥看是否该⾃旋锁的保持者已经释放了锁,"⾃旋"⼀词就是因此⽽得名。
信号量:有时被称为信号灯,是在多线程环境下使⽤的⼀种设施,是可以⽤来保证两个或多个关键代码段不被调⽤。在进⼊⼀个关键代码段之前,线程必须获取⼀个信号量;⼀旦该关键代码段完成了,那么该线程必须释放信号量。其它想进⼊该关键代码段的线程必须等待直到第⼀个线程释放信号量。为了完成这个过程,需要创建⼀个信号量VI,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的⾸末端。确认这些信号量VI引⽤的是初始创建的信号量。
互斥锁:在多线程开发中,我们采⽤@synchronized来创建⼀个互斥锁,保证在同⼀时刻只有⼀个线程对其进⾏操作。
4,原⼦操作你怎么理解?
答:原⼦操作,即不可分割开的操作,该操作⼀定是在同⼀个cpu时间⽚中完成,这样即使线程被切换,多个线程也不会看到同⼀块内存中不完整的数据。如果这个操作所处的层(layer)的更⾼层不能发现其内部实现与结构,那么这个操作是⼀个原⼦(atomic)操作。原⼦操作可以是⼀个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割⽽只执⾏其中的⼀部分。将整个操作视作⼀个整体是原⼦性的核⼼特征。
5,nsmod ⼀个驱动模块,会执⾏模块中的哪个函数?rmmod呢?这两个函数在设计上要注意哪些?遇到过卸载驱动出现异常没?是什么问题引起的?
答:insmod调⽤init函数,rmmod调⽤exit函数。
卸载模块时曾出现卸载失败的情形,原因是存在进程正在使⽤模块,检查代码后发现产⽣了死锁的问题。
要注意在init函数中申请的资源在exit函数中要释放,包括存储,ioremap,定时器,⼯作队列等等。也就是⼀个模块注册进内核,退出内核时要清理所带来的影响,带⾛⼀切不留下⼀点痕迹。
6,在驱动调试过程中遇到过oops没?你是怎么处理的?
答:什么是Oops?从语⾔学的⾓度说,Oops应该是⼀个拟声词。当出了点⼩事故,或者做了⽐较尴尬的事之后,你可以说"Oops",翻译成中国话就叫做“哎呦”。“哎呦,对不起,对不起,我真不是故意打碎您的杯⼦的”。看,Oops就是这个意思。
在Linux内核开发中的Oops是什么呢?其实,它和上⾯的解释也没什么本质的差别,只不过说话的主⾓变成了Linux。当某些⽐较致命的问题出现时,我们的Linux内核也会抱歉的对我们说:“哎呦(Oops),对不起,我把事情搞砸了”。Linux内核在发⽣kernel panic时会打印出Oops信息,把⽬前的寄存器状态、堆栈内容、以及完整的Call trace都show给我们看,这样就可以帮助我们定位错误。
7,ioctl和unlock_ioctl有什么区别?
答:ioctl是设备驱动程序中对设备的I/O通道进⾏管理的函数。所谓对I/O通道进⾏管理,就是对设备的⼀些特性进⾏控制,例如串⼝的传输波特率、马达的转速等等。它的调⽤个数如下:
int ioctl(int fd, ind cmd, …);
其中fd是⽤户程序打开设备时使⽤open函数返回的⽂件标⽰符,cmd是⽤户程序对设备的控制命令,⾄于后⾯的省略号,那是⼀些补充参数,⼀般最多⼀个,这个参数的有⽆和cmd的意义相关。ioctl函数是⽂件结构中的⼀个属性分量,就是说如果你的驱动程序提供了对ioctl的⽀持,⽤户就可以在⽤户程序中使⽤ioctl函数来控制设备的I/O通道。
在驱动程序中这个指针函数变了之后最⼤的影响是参数中少了inode ,所以应⽤程序ioctl是兼容的,但驱动程序中我们的ioctl函数必须变化,否则就会发⽣cmd参数的变化。
8,驱动中操作物理绝对地址为什么要先ioremap?
答:因为内核没有办法直接访问物理内存地址,必须先通过ioremap获得对应的虚拟地址。
ioremap将⼀个IO映射到内核的空间上去,便于访问。
9,设备驱动模型三个重要成员是?platfoem总线的匹配规则是?在具体应⽤上要不要先注册驱动再注册设备?有先后顺序没?
答:三个重要成员:设备(device),驱动(deriver),总线(bus)。
匹配的原理就是去遍历总线下的相应的链表来到挂接在他下⾯的设备或者设备驱动。platform总线下设备与设备驱动的匹配原理就是通过名字进⾏匹配的,先去匹配platform_driver中的id_table表中的各个名字与platform_device->name名字是否相同,如果相同表⽰匹配成功直接返回,否则直接匹配platform_driver->name与platform_driver->name是否相同,相同则匹配成功,否则失败。
相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是⼀种虚拟、抽象出来的总线,实际中并不存在这样的总线。那为什么需要platform总线呢?其实是Linux设备驱动模型为了保持设备驱动的统⼀性⽽虚拟出来的总线。因为对于usb设备、i2c设备、pci设备、spi设备等等,他们与cpu的通信都是直接挂在相应的总线下⾯与我们的cpu进⾏数据交互的,但是在我们的嵌⼊式系统当中,并不是所有的设备都能够归属于这些常见的总线,在嵌⼊式系统⾥⾯,SoC系统中集成的独⽴的外设控制器、挂接在SoC内存空间的外设却不依附与此类总线。所以Linux驱动模型为了保持完整性,将这些设备挂在⼀条虚拟的总线上(platform总线),⽽不⾄于使得有些设备挂在总线上,另⼀些设备没有挂在总线上。
10,linux中内核空间及⽤户空间的区别?⽤户空间与内核通信⽅式有哪些?
答:Linux 操作系统和驱动程序运⾏在内核空间,应⽤程序运⾏在⽤户空间,两者不能简单地使⽤指针传递数据,因为Linux使⽤的虚拟内存机制,⽤户空间的数据可能被换出,当内核空间使⽤⽤户空间指
针时,对应的数据可能不在内存中。
通常32位Linux内核地址空间划分0~3G为⽤户空间,3~4G为内核空间。注意这⾥是32位内核地址空间划分,64位内核地址空间划分是不同的。
⽤户空间和内核通信⽅式:
使⽤API;
使⽤proc⽂件系统;
使⽤sysfs⽂件系统+kobject;
Netlink:netlink socket提供了⼀组类似于BSD风格的API,⽤于⽤户态和内核态的IPC;
⽂件:当处于内核空间的时候,直接操作⽂件,将要传递的信息写⼊⽂件,然后⽤户空间可以直接读取这个⽂件便可以得到想要的数据了;使⽤mmap系统调⽤:可以将内核额空间的地址映射到⽤户空间;
信号:从内核空间向进程发送信号。
为什么不能直接进⾏互相访问?
Linux 操作系统和驱动程序运⾏在内核空间,应⽤程序运⾏在⽤户空间,两者不能简单地使⽤指针传递数据,因为Linux使⽤的虚拟内存机制,⽤户空间的数据可能被换出,当内核空间使⽤⽤户空间指针时,对应的数据可能不在内存中。⽤户空间的内存映射采⽤段页式,⽽内核空间有⾃⼰的规则 。
11,linux中内存划分及如何使⽤?虚拟地址及物理地址的概念及彼此之间的转化,⾼端内存概念?⾼端内存和物理地址、逻辑地址、线性地址的关系?
答:⾼端内存只和逻辑地址有关系,和逻辑地址、物理地址没有直接关系。
虚拟地址:CPU启动保护模式后,程序运⾏在虚拟中。注意,并不是所有的"程序"都是运⾏在虚拟地址中。CPU在启动的时候是运⾏在实模式的,Bootloader以及内核在初始化页表之前并不使⽤虚拟地址,⽽是直接使⽤物理地址的。
物理地址:放在寻址总线上的地址。放在寻址总线上,如果是读,电路根据这个地址每位的值就将相应地址的中的数据放到中传输。如果是写,电路根据这个地址每位的值就在相应地址的物理内存中放⼊数据总线上的内容。物理内存是以(8位)为单位的。
12,linux中中断的实现机制,tasklet与workqueue的区别及底层实现区别?为什么要区分上半部和下
半部?
答:底半部机制主要有tasklet、⼯作队列和软中断。
softirq和tasklet都属于软中断,tasklet是softirq的特殊实现;
workqueue是普通的⼯作队列。
什么情况下使⽤⼯作队列,什么情况下使⽤tasklet。如果推后执⾏的任务需要睡眠,那么就选择⼯作队列。如果推后执⾏的任务不需要睡眠,那么就选择tasklet。另外,如果需要⽤⼀个可以重新调度的实体来执⾏你的下半部处理,也应该使⽤⼯作队列。它是唯⼀能在进程上下⽂运⾏的下半部实现的机制,也只有它才可以睡眠。这意味着在需要获得⼤量的内存时、在需要获取信号量时,在需要执⾏阻塞式的I/O操作时,它都会⾮常有⽤。如果不需要⽤⼀个内核线程来推后执⾏⼯作,那么就考虑使⽤tasklet。
tasklet基于softirq实现,所以两者很相近。work queue与它们完全不同,它靠内核线程实现。
与⼀般的软中断不同,某⼀段tasklet代码在某个时刻只能在⼀个CPU上运⾏,但不同的tasklet代码在同⼀时刻可以在多个CPU上并发地执⾏。
Linux将中断分为:顶半部(top half)和底半部(bottom half)
顶板部:完成尽可能少的⽐较紧急的功能,它往往只是简单的读取寄存器中的中断状态并清除中断标志后就进⾏
“登记中断”(也就是将底半部处理程序挂在到设备的底半部执⾏队列中)的⼯作
特点:响应速度快
底半部:中断处理的⼤部分⼯作都在底半部,它⼏乎做了中断处理程序的所有事情。
特点:处理相对来说不是⾮常紧急的事件
13,linux中断的响应执⾏流程?中断的申请及何时执⾏(何时执⾏中断处理函数)?
1,中断初始化流程;
2,中断注册流程;
3,中断的处理流程。
14,linux中的同步机制?spinlock(⾃旋锁)与信号量的区别?
答:同步机制主要有⾃旋锁和信号量。详细答案在问题3.
15、linux中RCU原理?
答:RCU(Read-Copy Update),顾名思义就是读-拷贝修改,它是基于其原理命名的。对于被RCU保护的共享数据结构,读者不需要获得任何锁就可以访问它,但写者在访问它时⾸先拷贝⼀个副本,然后对副本进⾏修改,最后使⽤⼀个回调(callback)机制在适当的时机把指向原来数据的指针重新指向新的被修改的数据。这个时机就是所有引⽤该数据的CPU都退出对共享数据的操作。
16,linux中软中断的实现原理?
答:中断服务程序往往都是在CPU关中断的条件下执⾏的,以避免中断嵌套⽽使控制复杂化。但是CPU关中断的时间不能太长,否则容易丢失中断信号。为此, Linux将中断服务程序⼀分为⼆,各称作“Top Half”和“Bottom Half”。前者通常对时间要求较为严格,必须在中断请求发⽣后⽴即或⾄少在⼀定的时间限制内完成。因此为了保证这种处理能原⼦地完成,Top Half通常是在CPU关中断的条件下执⾏的。具体地说,Top Half的范围包括:从在IDT中登记的中断⼊⼝函数⼀直到驱动程序注册在中断服务队列中的ISR。⽽Bottom Half则是Top Half根据需要来调度执⾏的,这些操作允许延迟到稍后执⾏,它的时间要求并不严格,因此它通常是在CPU开中断的条件下执⾏的。 但是, Linux的这种Bottom Half(以下简称BH)机制有两个缺点,也即:(1)在任意⼀时刻,系统只能有⼀个CPU可以执⾏Bottom Half代码,以防⽌两个或多个CPU同时来执⾏Bottom Half函数⽽相互⼲扰。因此BH代码的执⾏是严格“串⾏化”的。(2)BH函数不允许嵌套。
这两个缺点在单CPU系统中是⽆关紧要的,但在SMP系统中却是⾮常致命的。因为BH机制的严格串⾏化执⾏显然没有充分利⽤SMP系统的多CPU特点。为此,Linux2.4内核在BH机制的基础上进⾏了扩展,这就是所谓的“软中断请求”(softirq)机制。
Linux 的softirq机制是与SMP紧密不可分的。为此,整个softirq机制的设计与实现中⾃始⾃终都贯彻了⼀个思想:“谁触发,谁执
⾏”(Who marks,Who runs),也即触发软中断的那个CPU负责执⾏它所触发的软中断,⽽且每个CPU都由它⾃⼰的软中断触发与控制机制。这个设计思想也使得softirq 机制充分利⽤了SMP系统的性能和特点。
17,linux系统实现原⼦操作有哪些⽅法?
答:有3种吧!
从理论上来说,最简单的⽅法就是加锁:在任何时间点上,只有⼀个处理器被允许执⾏⼀个原⼦操作。这个处理器在做原⼦操作之前,必须先获得锁,并且在操作完成后释放它。这就是x86的LOCK前缀的作⽤(⼤致如此;这⾥我略去了细节)。这⾥,获得锁的操作意味着向总线发送⼀条消息,说“好吧,我要占⽤总线⼀会⼉,⼤家都退后”(根据我们的⽬的,这就意味着“请不要再做内存操作了”)。
然后发出请求的处理器要先等其他处理器完成它们正在进⾏的内存操作,之后才会得到确认。只有等到其他所有处理器都确认了以后,请求锁的处理器才能开始处理内存操作。最后,⼀旦锁被释放,它还需要发送⼀条信息给总线上的其他处理器“我的⼯作完成,你们可以继续向总线发送请求了”。
18,MIPS Cpu中空间地址是怎么划分的?如在uboot中如何操作设备的特定的寄存器?
答:⾸先需要明确的是CPU物理地址空间不仅仅包括RAM物理内存的空间,还包括CPU内部的⼀些总线、寄存器的编址。
⼀个MIPS CPU可以运⾏在两种优先级别上, ⽤户态和核⼼态。MIPS CPU从核⼼态到⽤户态的变化并不是CPU⼯作不⼀样,⽽是对于有些操作认为是⾮法的。在⽤户态,任何⼀个程序地址的⾸位是1的话,这个地址是⾮法的,对 其存取将会导致异常处理。另外,在⽤户态下,⼀些特殊的指令将会导致CPU进⼊异常状态。
19,linux中系统调⽤过程?如:应⽤程序中read()在linux中执⾏过程即从⽤户空间到内核空间?
linux的系统调⽤过程:
层次例如以下:
⽤户程序------>C库(即API):INT 0x80 ----->system_call------->系统调⽤服务例程-------->内核程序
先说明⼀下,我们常说的⽤户API事实上就是系统提供的C库。
系统调⽤是通过软中断指令 INT 0x80 实现的,⽽这条INT 0x80指令就被封装在C库的函数中。
(软中断和我们常说的硬中断不同之处在于,软中断是由指令触发的,⽽不是由硬件外设引起的。)
INT 0x80 这条指令的运⾏会让系统跳转到⼀个预设的内核空间地址,它指向系统调⽤处理程序。即system_call函数。
20,linux内核的启动过程(源代码级)?
答:1,head_armv.S启动;
2,start_kernel()函数;
2.1,lock_kernel()进⼊内核态;
2.2,setup_arch()初始化函数;
2.2.1,setup_architecture,结构;
2.2.2,内存设置代码;
2.2.3,内核内存空间管理;
2,2,4,内存结构初始化;
2,2,5,paging_init,创建内核页表。
21,linux调度原理?
答:进程提供了两种优先级,⼀种是普通的进程优先级,第⼆个是实时优先级。前者适⽤SCHED_NORMAL调度策略,后者可选
SCHED_FIFO或SCHED_RR调度策略。任何时候,实时进程的优先级都⾼于普通进程,实时进程只会被更⾼级的实时进程抢占,同级实时进程之间是按照FIFO(⼀次机会做完)或者RR(多次轮转)规则调度的。
22,linux⽹络⼦系统的认识?
23,linux内核⾥⾯,内存申请有哪⼏个函数,各⾃的区别?
Kmalloc()  __get_free_page()  mempool_create()
24,IRQ和FIQ有什么区别,在CPU⾥⾯是是怎么做的?
答:快速中断请求(Fast Interrupt Request,FIQ)
中断控制器去中断ARM核⼼,可以选择fiq和irq两种⽅式:
irq发⽣时,ARM处于irq模式。在irq模式期间,不可以再次被irq中断打断,也就是不能嵌套;但是可以被fiq打断;
fiq发⽣时,ARM处于fiq模式,在fiq模式期间,不可以再次被fiq中断打断,更不可能被irq模式打断。
在ARM11及以前版本中,⼀个中断控制器中只有⼀个中断能被设为fiq ;
综上所述,两个区别:
fiq的优先更⾼⼀些(跟irq相⽐);
fiq 的r8 r9 r10 r11 r12寄存器物理上是独⽴,进⼊fiq保护现场时,少保护这⼏个寄存器(我拷,这能节约多少时间?)
另外,linux直接没有⽤到ARM的fiq.
25,中断的上半部分和下半部分的问题:讲下分成上半部分和下半部分的原因,为何要分?讲下如何实现?
答:上半部分执⾏与硬件相关的处理要求快, ⽽有些驱动在中断处理程序中⼜需要完成⼤量⼯作,这构成⽭盾,所以Linux有所谓的bottom half机制,中断处理程序中所有不要求⽴即完成的,在开中断的环境下,由底半程序随后完成.
Linux的底半处理实际上是建⽴在内核的软中断机制上的.
Linux 的底半 机制主要有Tasklet 和 work queue 以及 softirq ( 2.4内核则有BH , Task queue , softirq , tasklet 没有work queue),其实底半可以理解成⼀种⼯作的延迟。所以实际使⽤时跟timer机制基本上⼀个意思。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。