linux驱动面试题目汇总
1、linux驱动分类
2、信号量与自旋锁
3、platform总线设备及总线设备如何编写
4、kmalloc和vmalloc的区别
5、module_init的级别
6、添加驱动
7、IIC原理,总线框架,设备编写方法,i2c_msg
8、kernel panic
9、USB总线,USB传输种类,urb等
10、android boot 流程
11、android init解析init.rc
12、同步和互斥
答案:
1、http://baike.baidu.com/view/5363967.htm
Linux设备驱动的分类
(1)字符设备。
2、http://www.cnblogs.com/linxinshuo/archive/2009/12/08/1619771.html
自旋锁
自旋锁是专为防止多处理器并发而引入的一种锁,它应用于中断处理等部分。对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,不需要自旋锁。
自旋锁最多只能被一个内核任务持有,如果一个内核任务试图请求一个已被争用(已经被持有)的自旋锁,那么这个任务就会一直进行忙循环——旋转——等待锁重新可用。要是锁未被争用,请求它的内核任务便能立刻得到它并且继续进行。自旋锁可以在任何时刻防止多于一个的内核任务同时进入临界区,因此这种锁可有效地避免多处理器上并发运行的内核任务竞争共享资源。
事实上,自旋锁的初衷就是:在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持有时间过长。如果需要长时间锁定的话, 最好使用信号量。但是自旋锁节省了上下文切换的开销。
自旋锁的基本形式如下:
spin_lock(&mr_lock);
//临界区
spin_unlock(&mr_lock);
因为自旋锁在同一时刻只能被最多一个内核任务持有,所以一个时刻只有一个线程允许存在于临界区中。这点很好地满足了对称多处理机器需要的锁定服务。在单处理器上,自旋锁仅仅当作一个设置内核抢占的开关。如果内核抢占也不存在,那么自旋锁会在编译时被完全剔除出内核。
简单的说,自旋锁在内核中主要用来防止多处理器中并发访问临界区,防止内核抢占造成的竞争。另外自旋锁不允许任务睡眠(持有自旋锁的任务睡眠会造成自死锁——因为睡眠有可能造成持有锁的内核任务被重新调度,而再次申请自己已持有的锁),它能够在中断上下文中使用。
死锁:假设有一个或多个内核任务和一个或多个资源,每个内核都在等待其中的一个资源,但所有的资源都已经被占用了。这便会发生所有内核任务都在相互等待,但它们永远不会释放已经占有的资源,于是任何内核任务都无法获得所需要的资源,无法继续运行,这便意味着死锁发生了。自死琐是说自己占有了某个资源,然后自己又申请自己已占有的资源,显然不可能再获得该资源,因此就自缚手脚了。递归使用一个自旋锁就会出现这种情况。
信号量
信号量是一种睡眠锁。如果有一个任务试图获得一个已被持有的信号量时,信号量会将其推入等待队列,然后让其睡眠。这时处理器获得自由去执行其它代码。当持有信号量的进程将信号量释放后,在等待队列中的一个任务将被唤醒,从而便可以获得这个信号量。
信号量的睡眠特性,使得信号量适用于锁会被长时间持有的情况;只能在进程上下文中使用,因为中断上下文中是不能被调度的;另外当代码持有信号量时,不可以再持有自旋锁。
信号量基本使用形式为:
static DECLARE_MUTEX(mr_sem);//声明互斥信号量
if(down_interruptible(&mr_sem))
//可被中断的睡眠,当信号来到,睡眠的任务被唤醒
//临界区
up(&mr_sem);
信号量和自旋锁区别
从严格意义上讲,信号量和自旋锁属于不同层次的互斥手段,前者的实现有赖于后者。
注意以下原则:
如果代码需要睡眠——这往往是发生在和用户空间同步时——使用信号量是唯一的选择。由于不受睡眠的限制,使用信号量通常来说更加简单一些。如果需要在自旋锁和信号量中作选择,应该取决于锁被持有的时间长短。理想情况是所有的锁都应该尽可能短的被持有,但是如果锁的持有时间较长的话,使用信号量是更好的选择。另外,信号量不同于自旋锁,它不会关闭内核抢占,所以持有信号量的代码可以被抢占。这意味者信号量不会对影响调度反应时间带来负面影响。
自旋锁对信号量
需求 建议的加锁方法
低开销加锁 优先使用自旋锁
短期锁定 优先使用自旋锁
长期加锁 优先使用信号量
中断上下文中加锁 使用自旋锁
持有锁是需要睡眠、调度 使用信号量
3、http://www.cnblogs.com/noaming1900/archive/2010/10/26/1861177.html
http://blog.chinaunix.net/space.php?uid=11909535&do=blog&id=2801676
http://slhieanng.blog.163.com/blog/static/1932833562011731982291/
4、http://blog.163.com/liuqiang_mail@126/blog/static/109968875201241961116110/
kmalloc()和vmalloc()介绍
kmalloc()
用于申请较小的、连续的物理内存
1. 以字节为单位进行分配,在<linux/slab.h>中
2. void *kmalloc(size_t size, int flags) 分配的内存物理地址上连续,虚拟地址上自然连续
3. gfp_mask标志:什么时候使用哪种标志?如下:
———————————————————————————————-
情形 相应标志
———————————————————————————————-
进程上下文,可以睡眠 GFP_KERNEL
进程上下文,不可以睡眠 GFP_ATOMIC
中断处理程序 GFP_ATOMIC
软中断 GFP_ATOMIC
Tasklet GFP_ATOMIC
用于DMA的内存,可以睡眠 GFP_DMA | GFP_KERNEL
用于DMA的内存,不可以睡眠 GFP_DMA | GFP_ATOMIC
———————————————————————————————-
4. void kfree(const void *ptr)
释放由kmalloc()分配出来的内存块
vmalloc()
用于申请较大的内存空间,虚拟内存是连续的
1. 以字节为单位进行分配,在<linux/vmalloc.h>中
2. void *vmalloc(unsigned long size) 分配的内存虚拟地址上连续,物理地址不连续
3. 一般情况下,只有硬件设备才需要物理地址连续的内存,因为硬件设备往往存在于MMU之外,根本不了解虚拟地址;但为了性能上的考虑,内核中一般使用 kmalloc(),而只有在需要获得大块内存时才使用vmalloc(),例如当模块被动态加载到内核当中时,就把模块装载到由vmalloc()分配 的内存上。
4.void vfree(void *addr),这个函数可以睡眠,因此不能从中断上下文调用。
malloc(), vmalloc()和kmalloc()区别
[*]kmalloc和vmalloc是分配的是内核的内存,malloc分配的是用户的内存
[*]kmalloc保证分配的内存在物理上是连续的,vmalloc保证的是在虚拟地址空间上的连续,malloc不保证任何东西(这点是自己猜测的,不一定正确)
[*]kmalloc能分配的大小有限,vmalloc和malloc能分配的大小相对较大
[*]内存只有在要被DMA访问的时候才需要物理上连续
[*]vmalloc比kmalloc要慢
5、http://blog.163.com/xinbuqianjin@126/blog/static/167563447201010221231507/
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall("6",fn,6)
可知module_init()的级别为6
在vmlinux.lds中的
.initcall.init : AT(ADDR(.initcall.init) - (0xc0000000 -0x00000000)) {
__initcall_start = .;
*(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)
__initcall_end = .;
}
可以看出initcall总共分为0~7八个级别,表示系统在启动过程中调用do_initcalls()时,会根据它们的级别顺序调用。可知模块中的module_init中的初始化函数何时被调用了:在系统启动过程中start_kernel()->rest_init()->kernel_init()->do_basic_setup()->do_initcalls()。
6、
就静态加载和动态加载:
静态加载是系统启动的时候由内核自动加载的,这个要事先将驱动编译进内核才行;
动态加载,也就是模块加载方式,这种方式下驱动以模块的形式存放在文件系统中,需要时动态载入内核,这种主要用在调试的时候,比较方便灵活。insmod module.ko
7、
http://liu1227787871.blog.163.com/blog/static/2053631972012521191287/ IIC 总线基础知识
http://blog.163.com/liuqiang_mail@126/blog/static/109968875201243094211492/ IIC总线驱动程序架构分析
http://liu1227787871.blog.163.com/blog/static/20536319720125333531134/ IIC驱动程序分析
http://liu1227787871.blog.163.com/blog/static/2053631972012539436224/
http://liu1227787871.blog.163.com/blog/static/2053631972012531048573/
http://liu1227787871.blog.163.com/blog/static/2053631972012531620760/
8、
http://blog.51osos.com/linux/linux-kernel-panic/
有两种主要类型kernel panic:
1.hard panic(也就是Aieee信息输出)
2.soft panic (也就是Oops信息输出)
9、
USB总线:
USB总线属于一种轮询式总线,主机控制端口初始化所有的数据传输。每一总线动作最多传送三个数据包,包括令牌(Token)、数据(Data)、联络(HandShake)。按照传输前制定好的原则,在每次传送开始时,主机送一个描述传输动作的种类、方向、USB设备地址和终端号的USB数据包,这个数据包通常被称为令牌包(TokenPacket)。USB设备从解码后的数据包的适当位置取出属于自己的数据。数据传输方向不是从主机到设备就是从设备到主机。在传输开始时,由标志包来标志数据的传输方向,然后发送端开始发送包含信息的数据包或表明没有数据传送。接收端也要相应发送一个握手的数据包表明是否传送成功。发送端和接收端之间的USB数据传输,在主机和设备的端口之间,可视为一个通道。USB中有一个特殊的通道一缺省控制通道,它属于消息通道,设备一启动即存在,从而为设备的设置、状态查询和输入控制信息提供一个入口。
USB总线的四种传输类型:
1、中断传输:由OUT事务和IN事务构成,用于键盘、鼠标等HID设备的数据传输中 2、批量传输:由OUT事务和IN事务构成,用于大容量数据传输,没有固定的传输速率,也不占用带宽,当总线忙时,USB会优先进行其他类型的数据传输,而暂时停止批量转输。 3、同步传输:由OUT事务和IN事务构成,有两个特别地方,第一,在同步传输的IN和OUT事务中是没有返回包阶段的;第二,在数据包阶段任何的数据包都为DATA0 4、控制传输:最重要的也是最复杂的传输,控制传输由三个阶段构成(初始配置阶段、可选数据阶段、状态信息步骤),每一个阶段能够看成一个的传输,也就是说控制传输其实是由三个传输构成的,用来于USB设备初次加接到主机之后,主机通过控制传输来交换信息,设备地址和读取设备的描述符,使得主机识别设备,并安装相应的驱动程式,这是每一个USB研发者都要关心的问题。
URB:
USB请求块(USB request block,urb)是USB设备驱动中用来描述与USB设备通信所用的基本载体和核心数据结构,非常类似于网络设备驱动中的sk_buff结构体,是USB主机与设备通信的“电波”。
http://book.51cto.com/art/200803/66930.htm
10、
http://www.cnblogs.com/idiottiger/archive/2012/05/22/2513001.html android boot process from power on
http://www.cnblogs.com/idiottiger/archive/2012/05/23/2513494.html android boot 代码流程 1
http://www.cnblogs.com/idiottiger/archive/2012/05/25/2516295.html android boot 代码流程 2
11、
http://ytydyd.blog.sohu.com/136255592.html
Android初始化语言(Android Init Language
Android初始化脚本语言包含四种类型的语句:
- 动作(Actions)
- 指令(Commands)
- 服务(Services)
- 选项(Options)
该语言的语法包括下列约定:
- 所有类型的语句都是基于行(line-oriented)的, 一个语句包含若干个tokens,token之间通过空格字符分隔. 如果一个token中需要包含空格字符,则需要通过C语言风格的反斜线('\')来转义,或者使用双引号把整个token引起来。反斜线还可以出现在一行的末尾,表示下一行的内容仍然属于当前语句。
- 以'#'开始的行是注释行。
- 动作(Actions)和服务(Services)语句隐含表示一个新的段落(section)的开始。 所有的指令(commands)和选项(options)归属于上方最近的一个段落。在第一个段落之前的指令(commands)和选项(options)是无效的。
- 动作(Actions)和服务(Services)拥有唯一性的名字。如果出现重名,那么后出现的定义将被作为错误忽略掉。
动作(Actions)是一个有名字的指令(commands)序列。每个动作(Actions)都定义一个触发条件(trigger),用于指示什么时候执行这个动作。当与动作的触发器匹配的事件发生时,该动作将被添加到一个即将被执行的队列的队尾(除非它已经在队列中)。
队列中的每一个动作被依次取出执行,动作中的每一个指令也将依次执行。初始化程序(Init)在执行一个动作的各项指令的期间,还需要处理其它操作(比如,设备创建/销毁,属性设置,进程重启)。
一个动作定义的形式如下:
on <trigger> <command> <command> <command>
服务(Services)
服务是初始化程序需要启动的一些程序,初始化程序还有可能会在这些程序退出之后重启它们。Services take 一个服务定义的形式如下:service <name> <pathname> [ <argument> ]* <option> <option> ...
选项(Options)
选项将影响控制初始化程序运行服务的时机和方法。可能的选项如下表。
选项 | 说明 |
disabled |
This service will not automatically start with its class. It must be explicitly started by name. |
socket <name> <type> <perm> [ <user> [ <group> ] ] |
Create a unix domain socket named /dev/socket/<name> and pass its fd to the launched process. Valid <type> values include dgram and stream . user and group default to 0. |
user <username> |
Change to username before exec'ing this service. Currently defaults to root. |
group <groupname> [ <groupname> ]* |
Change to groupname before exec'ing this service. Additional groupnames beyond the first, which is required, are used to set additional groups of the process (withsetgroups() ). Currently defaults to root. |
capability [ <capability> ]+ |
Set linux capability before exec'ing this service |
oneshot |
Do not restart the service when it exits. |
class <name> |
Specify a class name for the service. All services in a named class must start and stop together. A service is considered of class "default" if one is not specified via the class option. |
触发器(Triggers)
触发器是一个字符串,用于匹配特定的事件,这些事件将触发触发器所属动作(Actions)的执行。
触发器 | 说明 |
boot |
This is the first trigger that occurs when init starts (after /init.conf is loaded). |
<name>=<value> |
Triggers of this form occur when the property <name> is set to the specific value<value> . |
device-added-<path> |
Triggers of these forms occur when a device node is added or removed. |
service-exited-<name> |
Triggers of this form occur when the specified service exits. |
指令(Commands)
Command | Description |
exec <path> [ <argument> ]* |
Fork and execute a program (<path> ). This will block until the program completes execution. Try to avoid exec. Unlike the builtin commands, it runs the risk of getting init "stuck". |
export <name> <value> |
Set the environment variable <name> equal to <value> in the global environment (which will be inherited by all processes started after this command is executed). |
ifup <interface> |
Bring the network interface <interface> online. |
import <filename> |
Parse an init config file, extending the current configuration. |
hostname <name> |
Set the host name. |
class_start <serviceclass> |
Start all services of the specified class if they are not already running. |
class_stop <serviceclass> |
Stop all services of the specified class if they are currently running. |
domainname <name> |
Set the domain name. |
insmod <path> |
Install the module at <path> . |
mkdir <path> |
Make a directory at <path> . |
mount <type> <device> <dir> [ <mountoption> ]* |
Attempt to mount the named device at the directory <dir> <device> . This may be of the form mtd@name to specify a mtd block device by name. |
setkey |
- currenlty undefined - |
setprop <name> <value> |
Set system property <name> to <value> . |
setrlimit <resource> <cur> <max> |
Set the rlimit for a resource. |
start <service> |
Start a service running if it is not already running. |
stop <service> |
Stop a service from running if it is currently running. |
symlink <target> <path> |
Create a symbolic link at <path> with the value <target> . |
write <path> <string> [ <string> ]* |
Open the file at <path> and write one or more strings to it with write(2). |
属性(Properties)
初始化程序(Init)可以根据需要修改一些系统的属性。
属性 | 说明 |
init.action |
Equal to the name of the action currently being executed or "" if none. |
init.command |
Equal to the command being executed or "" if none. |
init.svc.<name> |
State of a named service ("stopped", "running", or "restarting"). |
init.rc文件示例
on boot export PATH /sbin:/system/sbin:/system/bin export LD_LIBRARY_PATH /system/lib mkdir /dev mkdir /proc mkdir /sys mount tmpfs tmpfs /dev mkdir /dev/pts mkdir /dev/socket mount devpts devpts /dev/pts mount proc proc /proc mount sysfs sysfs /sys write /proc/cpu/alignment 4 ifup lo hostname localhost domainname localhost mount yaffs2 mtd@system /system mount yaffs2 mtd@userdata /data import /system/etc/init.conf class_start default service adbd /sbin/adbd user adb group adb service usbd /system/bin/usbd -r user usbd group usbd socket usbd 666 service zygote /system/bin/app_process -Xzygote /system/bin --zygote socket zygote 666 service runtime /system/bin/runtime user system group system on device-added-/dev/compass start akmd on device-removed-/dev/compass stop akmd service akmd /sbin/akmd disabled user akmd group akmd
12、http://www.cnblogs.com/linxinshuo/archive/2009/12/09/1620413.html
同步和互斥
相交进程之间的关系主要有两种,同步与互斥。所谓互斥,是指散步在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。所谓同步,是指散步在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。
显然,同步是一种更为复杂的互斥,而互斥是一种特殊的同步。
也就是说互斥是两个线程之间不可以同时运行,他们会相互排斥,必须等待一个线程运行完毕,另一个才能运行,而同步也是不能同时运行,但他是必须要安照某种次序来运行相应的线程(也是一种互斥)!总结:
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源