嵌入式常见面试题总结100题(上)
Posted 行稳方能走远
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了嵌入式常见面试题总结100题(上)相关的知识,希望对你有一定的参考价值。
参考:嵌入式常见面试题总结(1)
作者:天泉证道
发布时间: 2018-11-08 09:33:43
网址:https://guoyanzhang.blog.csdn.net/article/details/83855895
目录
- 1,字符型驱动设备是怎么创建设备文件的,就是/dev/下面的设备文件,供上层应用程序打开使用的文件?
- 2,写一个中断服务需要注意哪些?如果中断产生之后要做比较多的事情你是怎么做的?
- 3,自旋锁和信号量在互斥使用时需要注意哪些?在中断服务程序里面的互斥是使用自旋锁还是信号量?还是两者都能用?为什么?
- 4,原子操作你怎么理解?
- 5,nsmod 一个驱动模块,会执行模块中的哪个函数?rmmod呢?这两个函数在设计上要注意哪些?遇到过卸载驱动出现异常没?是什么问题引起的?
- 6,在驱动调试过程中遇到过oops没?你是怎么处理的?
- 7,ioctl和unlock_ioctl有什么区别?
- 8,驱动中操作物理绝对地址为什么要先ioremap?
- 9,设备驱动模型三个重要成员是?platfoem总线的匹配规则是?在具体应用上要不要先注册驱动再注册设备?有先后顺序没?
- 10,linux中内核空间及用户空间的区别?用户空间与内核通信方式有哪些?
- 11,linux中内存划分及如何使用?虚拟地址及物理地址的概念及彼此之间的转化,高端内存概念?高端内存和物理地址、逻辑地址、线性地址的关系?
- 12,linux中中断的实现机制,tasklet与workqueue的区别及底层实现区别?为什么要区分上半部和下半部?
- 13,linux中断的响应执行流程?中断的申请及何时执行(何时执行中断处理函数)?
- 14,linux中的同步机制?spinlock(自旋锁)与信号量的区别?
- 15、linux中RCU原理?
- 16,linux中软中断的实现原理?
- 17,linux系统实现原子操作有哪些方法?
- 18,MIPS Cpu中空间地址是怎么划分的?如在uboot中如何操作设备的特定的寄存器?
- 19,linux中系统调用过程?如:应用程序中read()在linux中执行过程即从用户空间到内核空间?
- 20,linux内核的启动过程(源代码级)?
- 21,linux调度原理?
- 22,linux网络子系统的认识?
- 23,linux内核里面,内存申请有哪几个函数,各自的区别?
- 24,IRQ和FIQ有什么区别,在CPU里面是是怎么做的?
- 25,中断的上半部分和下半部分的问题:讲下分成上半部分和下半部分的原因,为何要分?讲下如何实现?
- 26,内核函数mmap的实现原理,机制?
- 27,驱动里面为什么要有并发、互斥的控制?如何实现?讲个例子?
- 28,spinlock自旋锁是如何实现的?
- 29,任务调度的机制?
- 30,嵌入式linux和wince操作系统的特点和特性?
- 31,嵌入式linux中tty设备驱动的体系结构?
- 32,嵌入式设备,为加快启动速度,可以做哪些方面的优化?
- 33,USB设备的枚举过程?
- 34,PSRAM、SDRAM、DDR、DDR2的时序特性?
- 35,什么是GPIO?
- 36,触摸屏的硬件原理?
- 37,在Linux C中,ls这个命令是怎么被执行的?
- 38,在一个只有128M内存并且没有交换分区的机器上,说说下面两个程序的运行结果?
- 39,请定义一个宏,比较两个数a、b的大小,不能使用大于、小于、if语句?
- 40,LINUX下的Socket套接字和Windows下的WinSock有什么共同点?请从C/C++语言开发的角度描述,至少说出两点共同点?
- 41,请编写一个标准Shell脚本testd,实现如下功能:
- 42.你平常是怎么用C写嵌入式系统的死循环的?
- 43,写一条命令,实现在dir以及其子目录下找出所有包含“hello world”字符串的文件?
- 44,下面的两段程序中,循环能否执行?为什么?
- 45,一个计划跑LINUX系统的ARM系统把bootloader烧录进去后,上电后串口上没有任何输出,硬件和软件各应该去检查什么?
- 46,列举最少3种你所知道的嵌入式的体系结构,并请说明什么是ARM体系结构。
- 47,请简述下面这段代码的功能?
- 48,嵌入式中常用的文件系统有哪些?说出它们的主要特点和应用场合?
- 49,某外设寄存器rGpioBase的地址是0x56000000,寄存器的0~15位有效,请写出给外设寄存器高八位(8~`15位)设置成0xc3的代码?
- 50,如何编写一个LINUX驱动?
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位内核地址空间划分是不同的。
参考:http://blog.chinaunix.net/uid-15007890-id-3415331.html
用户空间和内核通信方式:
参考:https://www.cnblogs.com/dchipnau/p/5043591.html
使用API;
使用proc文件系统;
使用sysfs文件系统+kobject;
Netlink:netlink socket提供了一组类似于BSD风格的API,用于用户态和内核态的IPC;
文件:当处于内核空间的时候,直接操作文件,将要传递的信息写入文件,然后用户空间可以直接读取这个文件便可以得到想要的数据了;
使用mmap系统调用:可以将内核额空间的地址映射到用户空间;
信号:从内核空间向进程发送信号。
为什么不能直接进行互相访问?
Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数据可能不在内存中。用户空间的内存映射采用段页式,而内核空间有自己的规则 。
11,linux中内存划分及如何使用?虚拟地址及物理地址的概念及彼此之间的转化,高端内存概念?高端内存和物理地址、逻辑地址、线性地址的关系?
答:高端内存只和逻辑地址有关系,和逻辑地址、物理地址没有直接关系。
虚拟地址:CPU启动保护模式后,程序运行在虚拟地址空间中。注意,并不是所有的"程序"都是运行在虚拟地址中。CPU在启动的时候是运行在实模式的,Bootloader以及内核在初始化页表之前并不使用虚拟地址,而是直接使用物理地址的。
物理地址:放在寻址总线上的地址。放在寻址总线上,如果是读,电路根据这个地址每位的值就将相应地址的物理内存中的数据放到数据总线中传输。如果是写,电路根据这个地址每位的值就在相应地址的物理内存中放入数据总线上的内容。物理内存是以字节(8位)为单位编址的。
参考1:https://baike.so.com/doc/889976-940779.html
12,linux中中断的实现机制,tasklet与workqueue的区别及底层实现区别?为什么要区分上半部和下半部?
答:底半部机制主要有tasklet、工作队列和软中断。
softirq和tasklet都属于软中断,tasklet是softirq的特殊实现;
workqueue是普通的工作队列。
什么情况下使用工作队列,什么情况下使用tasklet。如果推后执行的任务需要睡眠,那么就选择工作队列。如果推后执行的任务不需要睡眠,那么就选择tasklet。另外,如果需要用一个可以重新调度的实体来执行你的下半部处理,也应该使用工作队列。它是唯一能在进程上下文运行的下半部实现的机制,也只有它才可以睡眠。这意味着在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,它都会非常有用。如果不需要用一个内核线程来推后执行工作,那么就考虑使用tasklet。
tasklet基于softirq实现,所以两者很相近。work queue与它们完全不同,它靠内核线程实现。
与一般的软中断不同,某一段tasklet代码在某个时刻只能在一个CPU上运行,但不同的tasklet代码在同一时刻可以在多个CPU上并发地执行。
参考:https://blog.csdn.net/cupidove/article/details/49927259
Linux将中断分为:顶半部(top half)和底半部(bottom half)
顶板部:完成尽可能少的比较紧急的功能,它往往只是简单的读取寄存器中的中断状态并清除中断标志后就进行
“登记中断”(也就是将底半部处理程序挂在到设备的底半部执行队列中)的工作
特点:响应速度快
底半部:中断处理的大部分工作都在底半部,它几乎做了中断处理程序的所有事情。
特点:处理相对来说不是非常紧急的事件
13,linux中断的响应执行流程?中断的申请及何时执行(何时执行中断处理函数)?
答:参考:https://blog.csdn.net/pandy_gao/article/details/79309725
1,中断初始化流程;
2,中断注册流程;
3,中断的处理流程。
参考:https://blog.csdn.net/yimu13/article/details/6803957
14,linux中的同步机制?spinlock(自旋锁)与信号量的区别?
答:同步机制主要有自旋锁和信号量。详细答案在问题3.
15、linux中RCU原理?
答:RCU(Read-Copy Update),顾名思义就是读-拷贝修改,它是基于其原理命名的。对于被RCU保护的共享数据结构,读者不需要获得任何锁就可以访问它,但写者在访问它时首先拷贝一个副本,然后对副本进行修改,最后使用一个回调(callback)机制在适当的时机把指向原来数据的指针重新指向新的被修改的数据。这个时机就是所有引用该数据的CPU都退出对共享数据的操作。
参考:http://www.360doc.com/content/12/1125/10/11169997_250078652.shtml
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系统的性能和特点。
参考:https://blog.csdn.net/liangjingbo/article/details/2817939
17,linux系统实现原子操作有哪些方法?
答:有3种吧!
从理论上来说,最简单的方法就是加锁:在任何时间点上,只有一个处理器被允许执行一个原子操作。这个处理器在做原子操作之前,必须先获得锁,并且在操作完成后释放它。这就是x86的LOCK前缀的作用(大致如此;这里我略去了细节)。这里,获得锁的操作意味着向总线发送一条消息,说“好吧,我要占用总线一会儿,大家都退后”(根据我们的目的,这就意味着“请不要再做内存操作了”)。然后发出请求的处理器要先等其他处理器完成它们正在进行的内存操作,之后才会得到确认。只有等到其他所有处理器都确认了以后,请求锁的处理器才能开始处理内存操作。最后,一旦锁被释放,它还需要发送一条信息给总线上的其他处理器“我的工作完成,你们可以继续向总线发送请求了”。
18,MIPS Cpu中空间地址是怎么划分的?如在uboot中如何操作设备的特定的寄存器?
答:首先需要明确的是CPU物理地址空间不仅仅包括RAM物理内存的空间,还包括CPU内部的一些总线、寄存器的编址。
一个MIPS CPU可以运行在两种优先级别上, 用户态和核心态。MIPS CPU从核心态到用户态的变化并不是CPU工作不一样,而是对于有些操作认为是非法的。在用户态,任何一个程序地址的首位是1的话,这个地址是非法的,对 其存取将会导致异常处理。另外,在用户态下,一些特殊的指令将会导致CPU进入异常状态。
参考1:http://blog.chinaunix.net/uid-20564848-id-74684.html
参考2:http://www.360doc.com/content/12/0816/10/7775902_230452499.shtml
19,linux中系统调用过程?如:应用程序中read()在linux中执行过程即从用户空间到内核空间?
linux的系统调用过程:
层次例如以下:
用户程序------>C库(即API):INT 0x80 ----->system_call------->系统调用服务例程-------->内核程序
先说明一下,我们常说的用户API事实上就是系统提供的C库。
系统调用是通过软中断指令 INT 0x80 实现的,而这条INT 0x80指令就被封装在C库的函数中。
(软中断和我们常说的硬中断不同之处在于,软中断是由指令触发的,而不是由硬件外设引起的。)
INT 0x80 这条指令的运行会让系统跳转到一个预设的内核空间地址,它指向系统调用处理程序。即system_call函数。
参考:https://www.cnblogs.com/yfceshi/p/6885322.html
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,创建内核页表。
参考:https://www.linuxidc.com/Linux/2014-10/108034.htm
21,linux调度原理?
答:进程提供了两种优先级,一种是普通的进程优先级,第二个是实时优先级。前者适用SCHED_NORMAL调度策略,后者可选SCHED_FIFO或SCHED_RR调度策略。任何时候,实时进程的优先级都高于普通进程,实时进程只会被更高级的实时进程抢占,同级实时进程之间是按照FIFO(一次机会做完)或者RR(多次轮转)规则调度的。
参考:https://yq.aliyun.com/articles/363020
22,linux网络子系统的认识?
答:参考:https://blog.csdn.net/ylyuanlu/article/details/7707877
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机制基本上一个意思。
26,内核函数mmap的实现原理,机制?
答:mmap函数实现把一个文件映射到一个内存区域,从而我们可以像读写内存一样读写文件,他比单纯调用read/write也要快上许多。在某些时候我们可以把内存的内容拷贝到一个文件中实现内存备份,当然,也可以把文件的内容映射到内存来恢复某些服务。另外,mmap实现共享内存也是其主要应用之一,mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。
27,驱动里面为什么要有并发、互斥的控制?如何实现?讲个例子?
答:并发(concurrency)指的是多个执行单元同时、并行被执行,而并发的执行单元对 共 享资源(硬件资源和软件上的全局变量、静态变量等)的访问则很容易导致竞态(race conditions) 。 解决竞态问题的途径是保证对共享资源的互斥访问, 所谓互斥访问就是指一个执行单 元 在访问共享资源的时候,其他的执行单元都被禁止访问。 访问共享资源的代码区域被称为临界区, 临界区需要以某种互斥机 制加以保护, 中断屏蔽, 原子操作,自旋锁,和信号量都是 linux 设备驱动中可采用的互斥途径。
28,spinlock自旋锁是如何实现的?
答:自旋锁在同一时刻只能被最多一个内核任务持有,所以一个时刻只有一个线程允许存在于临界区中。这点可以应用在多处理机器、或运行在单处理器上的抢占式内核中需要的锁定服务。
这里也介绍下信号量的概念,因为它的用法和自旋锁有相似的地方。linux中的信号量是一种睡眠锁。如果有一个任务试图获得一个已被持有的信号量时,信号量会将其推入等待队列,然后让其睡眠。这时处理器获得自由去执行其它代码。当持有信号量的进程将信号量释放后,在等待队列中的一个任务将被唤醒,从而便可以获得这个信号量。
29,任务调度的机制?
答:linux进程的调度时机大致分为两种情况:
一种是进程自愿调度;
另一种是发生强制性调度。
首先,自愿的调度随时都可以进行。在内核空间中,进程可以通过schedule()启动一次调度;在用户空间中,可以通过系统调用pause()达到同样的目的。如果要为自愿的暂停行为加上时间限制,在内核中使用schedule_time(),而在用户空间则使用nanosleep()系统调用。
参考:http://www.360doc.com/content/14/1216/10/14855936_433298555.shtml
30,嵌入式linux和wince操作系统的特点和特性?
答:支持多种硬件平台,wince差一点;
占有较少的硬件资源,wince对资源的要求更高;
高可定制性,wince做不到;
具有实时处理能力;
具备强大的网络功能,wince没有;
高安全性和高可靠性;
具有完善的嵌入式GUI和嵌入式浏览器;
实现嵌入式日志文件系统,具备断电保护能力;
能够提供完善的开发工具集;
能够快速启动。
参考:http://www.elecfans.com/emb/xitong/20111114244549.html
31,嵌入式linux中tty设备驱动的体系结构?
答:tty这个名称源于电传打字节的简称。在linux表示各种终端。终端通常都跟硬件相对应。比如对应于输入设备键盘鼠标。输出设备显示器的控制 终端和串口终端.也有对应于不存在设备的pty驱动。在如此众多的终端模型之中,linux是怎么将它们统一建模的呢?这就是我们今天要讨论的问题。
Linux内核中tty的层次结构包含tty核心、tty线路规程和tty驱动;
tty设备发送数据的流程为:tty核心从一个用户获取将要发送给一个tty设备的数据,tty核心将数据传递给tty线路规程驱动,接着数据被传递到tty驱动,tty驱动将数据转换为可以发送给硬件的格式。
接收数据的流程为: 从tty硬件接收到的数据向上交给tty驱动,进入tty线路规程驱动,再进入 tty 核心,在这里它被一个用户获取。尽管大多数时候tty核心和tty之间的数据传输会经历tty线路规程的转换,但是tty驱动与tty核心之间也可以直接传输数据。
参考:http://www.uml.org.cn/embeded/201209071.asp
32,嵌入式设备,为加快启动速度,可以做哪些方面的优化?
答:linux默认的安装内核相当庞大,为了保证系统的兼容性和灵活性,支持热插拔操作,内核启动时要进行大量的硬件检测和初始化工作,而嵌入式的硬件都是固定的,只需要选择需要的硬件驱动就可以,不需要全部的硬件驱动都检测;因此可以进行适当的裁剪内核达到缩小启动linux系统的目的;同时可以统计驱动模块的耗时时间,对耗时较长的模块驱动加以分析,优化。
33,USB设备的枚举过程?
答:(1) Get Device Descriptor。主机的第一个命令要求得到设备描述符,此SETUP 包为8 个字节数据(80,06,00,01,00,00,40,00),发向地址0,端口0。“40”表示返回数据长度最大为40H 个字节。实际上,只返回一个包,即数组DEV_DESC[ ]中的前8 个字节,用于说明设备的描述符的真实长度和设备的类型。
(2) Set Address。接着是设置设备地址处理事件,主机发送一个含有指定地址的数据包(00,05,02,00,00,00,00,00),在主机只有一个USB 设备的时候,这个地址一般会是2,最大地址127,USB 协议中可以连接127 个设备。设置地址事件处理结束后,设备进入地址状态,主机以后会在新的指定地址处访问设备。
(3) Get Device Descriptor。主机再次发送请求得到设备描述符的数据包(80,06,00,01,00,00,12,00),与上次不同的是,要求的数据的长度是实际的数据长度,同时是发送到Set Address命令所设置的地址。
(4) 读取全部Configuration Descriptor。接着主机要求得到设备全部的配置描述符、接口描述符和节点描述符(80,06,00,02,00,00,40,00),由于主机不知道设备描述符的真实长度,因此它要求得到64个字节。
(5) Set Interface,主机发送数据包(01,0B,00,00,00,00,00,00),设置接口值为0。
(6) Set Conifguration,确定USB设备工作在哪一个配置下。对于U盘设备来说,一般只有1个配置值,其值为01。主机发送数据包(00,09,01,00,00,00,00,00)。
(7) 如果以上步骤都正确,主机将找到新设备,并且配置成功,该设备可以正常使用,可以进行后续的U盘枚举过程了。
(8) 用busHound观察计算机对于U盘的枚举过程,发现上述步骤后还有一个GetMaxLun的操作,但是实际上对于U盘来说忽略该步骤也没有问题。
34,PSRAM、SDRAM、DDR、DDR2的时序特性?
答:PSRAM,全称Pseudo static random access memory。指的是伪静态随机存储器。
SDRAM:Synchronous Dynamic Random Access Memory,同步动态随机存储器,同步是指 Memory工作需要同步时钟,内部的命令的发送与数据的传输都以它为基准;动态是指存储阵列需要不断的刷新来保证数据不丢失;随机是指数据不是线性依次存储,而是自由指定地址进行数据读写。
DDR=Double Data Rate双倍速率同步动态随机存储器。DDR SDRAM是Double Data Rate SDRAM的缩写,是双倍速率同步动态随机存储器的意思。
在同等核心频率下,DDR2的实际工作频率是DDR的两倍。这得益于DDR2内存拥有两倍于标准DDR内存的4BIT预读取能力。换句话说,虽然DDR2和DDR一样,都采用DDR2内存的频率了在时钟的上升延和下降延同时进行数据传输的基本方式,但DDR2拥有两倍于DDR的预读取系统命令数据的能力。也就是说,在同样100MHz的工作频率下,DDR的实际频率为200MHz,而DDR2则可以达到400MHz。
35,什么是GPIO?
答:general purpose input/output
GPIO是相对于芯片本身而言的,如某个管脚是芯片的GPIO脚,则该脚可作为输入或输出高或低电平使用,当然某个脚具有复用的功能,即可做GPIO也可做其他用途。
也就是说你可以把这些引脚拿来用作任何一般用途的输入输出,例如用一根引脚连到led的一极来控制它的亮灭,也可以用一根(一些)引脚连到一个传感器上以获得该传感器的状态,这给cpu提供了一个方便的控制周边设备的途经。如果没有足够多的gpio管脚,在控制一些外围设备时就会力有不逮,这时可采取的方案是使用CPLD来帮助管理。
36,触摸屏的硬件原理?
答:触摸屏的主要三大种类是:电阻技术触摸屏、 表面声波技术触摸屏、 电容技术触摸屏。
电阻触摸屏的主要部分是一块与显示器表面非常配合的电阻薄膜屏, 这是一种多层的复合薄膜,它以一层玻璃或硬塑料平板作为基层,表面图有一层透明氧化金属 (ITO氧化铟,透明的导电电阻) 导电层,上面在盖有一层外表面硬化处理、光滑防擦的塑料层 、它的内表面也涂有一层ITO涂层 、在他们之间有许多细小的(小于1/1000英寸)的透明隔离点把两层导电层隔开绝缘 。当手指触摸屏幕时,两层导电层在触摸点位置就有了接触,控制器侦测到这一接触并计算出(X,Y )的位置,再根据模拟鼠标的方式运作。这就是电阻技术触摸屏的最基本的原理。
表面声波技术是利用声波在物体的表面进行传输,当有物体触摸到表面时,阻碍声波的传输,换能器侦测到这个变化,反映给计算机,进而进行鼠标的模拟。
电容技术触摸屏利用人体的电流感应进行工作 。用户触摸屏幕时 ,由于人体电场,用户和触摸屏表面形成以一个耦合电容, 对于高频电流来说,电容是直接导体,于是手指从接触点吸走一个很小的电流。
37,在Linux C中,ls这个命令是怎么被执行的?
答:使用fork创建一个进程或exec函数族覆盖原进程。
38,在一个只有128M内存并且没有交换分区的机器上,说说下面两个程序的运行结果?
答:1,#define MEMSIZE 1024*1024
int count = 0;
void *p = NULL;
2,while(1) {
p = (void *)malloc(MEMSIZE);
if (!p) break;
printf("Current allocation %d MB\\n", ++count);
}
while(1) {
p = (void *)malloc(MEMSIZE);
if (!p) break;
memset(p, 1, MEMSIZE);
printf("Current allocation %d MB\\n", ++count);
}
第一道程序分配内存但没有填充,编译器可能会把内存分配优化掉,程序死循环;第二道,程序分配内存并进行填充,系统会一直分配内存,直到内存不足,退出循环。
39,请定义一个宏,比较两个数a、b的大小,不能使用大于、小于、if语句?
答:搞的比较复杂。主要思想就是a-b的值的最高位是否为0;但是又得考虑整数溢出的问题,所以很复杂。不知道哪位大侠有更好的办法,指点指点。
#include<stdio.h>
#define ZHENG(i)((i>> 31)== 0)
#define FU(i)((i>> 31)!= 0)
#define COMPARE(a,b)((ZHENG(a)&& FU(b))||(((ZHENG(a)&& ZHENG(b))||(FU(a)&&FU(b)))&&((((a)-(b))>> 31)== 0)))
void main()
{
int a = 0x80000001;
int b = 0x6FFFFFFF;
if(COMPARE(a,b))
{
printf(“a >= b\\n”);
}
else
{
printf(“a < b\\n”);
}
}
40,LINUX下的Socket套接字和Windows下的WinSock有什么共同点?请从C/C++语言开发的角度描述,至少说出两点共同点?
答:a)都基于TCP/IP协议,都提供了面向连接的TCP SOCK和无连接的UDP SOCK。
b)都是一个sock结构体。
c)都是使用sock文件句柄进行访问。
d)都具有缓冲机制。
41,请编写一个标准Shell脚本testd,实现如下功能:
A、在Linux操作系统启动的时候,自动加载/mnt/test/test程序。
B、当test异常退出之后,自动重新启动。
C、当test程序重启次数超过100次,自动复位操作系统。
答:假设你所拥有的资源:
A、目标机器是一台具有标准shell的嵌入式计算机,CPU为ARM7 56MB,内存16MB,软件环境基于Linux2.6.11和BusyBox1.2构建。
B、当前已有11个用户进程在运行,占用了大部分的CPU时间和内存,你可使用的内存只有2MB左右,CPU时间由系统分派。
本题是考查LINUX和嵌入式编程功底的,写出程序来的不少,但是95%以上的人竟无视我假设的资源,不知道在重启test程序的时候需要加上一个适当的掩饰时间,以便资源紧张的操作系统有时间回收资源。85%的人不知道写完testd之后,要在init里边加载这个脚本,才能实现启动时自动加载的功能。
参考答案:
########################################
#testd is a daemon script to start an watch the program test
########################################
#!/bin/sh
#load *.so that may need
if [ -r /sbin/ldconfig ]; then
ldconfig
fi
#add the libs PATH that may need
export LD_LIBRARY_PATH="/lib"
#count is the counter of test started times
count=0
#main loop
while [ 1 ] ;do
#add execute property for /mnt/test/test
chmod +x /mnt/test/test
#start test
/mnt/test/test
#the running times counter
let count=count+1
echo “test running times is KaTeX parse error: Expected 'EOF', got '#' at position 12: count"<br> #̲Is test running…count” -gt 100 ]; then
echo “Will reboot because of test running too many times”
reboot
fi
#wait for test stoping…
sleep 3
done
#########################################
42.你平常是怎么用C写嵌入式系统的死循环的?
答:While(1);for(;;)
43,写一条命令,实现在dir以及其子目录下找出所有包含“hello world”字符串的文件?
答:grep -r "hello world" ./dir或者grep -rHn "hello
44,下面的两段程序中,循环能否执行?为什么?
A: unsigned short i; unsigned short index = 0; for(i = 0; i <index-1; i++){ printf(“a\\n”); }
B: unsigned short i; unsigned long index = 0; for(i = 0; i <index-1; i++){ printf(“b\\n”); }
45,一个计划跑LINUX系统的ARM系统把bootloader烧录进去后,上电后串口上没有任何输出,硬件和软件各应该去检查什么?
提示: 1.跑LINUX的系统一般都需要外扩DRAM,一般的系统也经常有NOR或NAND FLASH
46,列举最少3种你所知道的嵌入式的体系结构,并请说明什么是ARM体系结构。
答:ARM7/ARM9/ARM11
参考:https://www.cnblogs.com/PengfeiSong/p/6295151.html
47,请简述下面这段代码的功能?
mov r12, #0x0
ldr r13, =0x30100000
mov r14, #4096
loop:
ldmia r12!, {r0-r11}
stmia r13!, {r0-r11}
cmp r12, r14
bl loop
答案:借助r0~r11,将内存地址0x0开始的4KB数据拷贝到0x30100000
48,嵌入式中常用的文件系统有哪些?说出它们的主要特点和应用场合?
答:只读文件系统
cramfs: 压缩的只读文件系统
特点: 启动快,文件最大支持256MB,单个文件最大16MB
squashfs: 只读文件系统
特点: 压缩比最大,启动比cramfs慢
案例:路由器,ubuntu的发行光盘 可结合LZMA压缩算法
可读写的文件系统:
JFFS2: 支持NOR 和NAND FLASH (对NAND的支持天生不足)
参考:https://www.cnblogs.com/feige1314/p/7402144.html
49,某外设寄存器rGpioBase的地址是0x56000000,寄存器的015位有效,请写出给外设寄存器高八位(8`15位)设置成0xc3的代码?
答:#define rGpioBase (*((volatile unsigned int *)0x56000000))
rGpioBase &= ~0xff00;
rGpioBase |= 0xc300;
50,如何编写一个LINUX驱动?
答:一.在系统的资源文件代码中定义platform_device,里面填写对应设备的外设IO起始地址,地址长度,中断,DMA资源等信息资源信息,并把资源信息添加到系统启动初始化流程里面;
二. 通过module_init(xxx_init)和moule_exit(xxx_init)定义驱动入口和出口函数;
三.写出模块加载xxx_init()和退出的实际处理函数xxx_exit(),这里以xxx_init()为例:
在里面调用platform_driver_resigter()注册一个platform_driver结构体,实现其中的probe()和remove()函数以及driver成员结构体中name和owner成员。
以上是关于嵌入式常见面试题总结100题(上)的主要内容,如果未能解决你的问题,请参考以下文章