| |
|
|
|
分类: LINUX
2010-10-12 10:40:39
在Linux底下写过driver模块的对这个宏一定不会陌生。
module_init宏在MODULE宏有没有定义的情况下展开的内容是不同的,如果这个宏没有定义,基本上表明阁下的模块是要编译进内核的(obj-
y)。 1.在MODULE没有定义这种情况下,module_init定义如下: #define module_init(x) __initcall(x); 因为 #define __initcall(fn) device_initcall(fn) #define device_initcall(fn) __define_initcall("6",fn,6) #define __define_initcall(level,fn,id) \ static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(".initcall" level ".init"))) = fn 所以,module_init(x)最终展开为: static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(".initcall" level ".init"))) = fn 更直白点,假设阁下driver所对应的模块的初始化函数为int gpio_init(void),那么module_init(gpio_init)实际上等于: static initcall_t __initcall_gpio_init_6 __used __attribute__((__section__(".initcall6.init"))) = gpio_init; 就是声明一类型为initcall_t(typedef int (*initcall_t)(void))函数指针类型的变量__initcall_gpio_init_6并将gpio_init赋值与它。 这里的函数指针变量声明比较特殊的地方在于,将这个变量放在了一名为".initcall6.init"节中。接下来结合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 = .; } 以及do_initcalls: static void __init do_initcalls(void) { initcall_t *call; for (call = __initcall_start; call < __initcall_end; call++) do_one_initcall(*call); /* Make sure there is no pending stuff from the initcall sequence */ flush_scheduled_work(); } 那么就不难理解阁下模块中的module_init中的初始化函数何时被调用了:在系统启动过程中start_kernel()->rest_init()->kernel_init()->do_basic_setup()->do_initcalls()。 |
|
|
|
|
主 设备号驱动程序在初始化时,会注册它的驱动及对应主设备号到系统中,这样当应用程序访问设备节点时,系统就 知道它所访问的驱动程序了。你可以通过/proc/devices文 件来驱动系统设备的主设备号。
次设备号 驱 动程序遍历设备时,每发现一个它能驱动的设备,就创建一个设备对象,并为其分配一个次设备号以区分不同的设备。这样当应用程序访问设备节点时驱动程序就可 以根据次设备号知道它说访问的设备了。
系统中的每一个字符设备和块设备(网络接口没有设备号)都有一个设备号, 传统的UNIX以及早期版本Linux中 的设备号是16位的,主次设备号都是8位 的,低8位为次设备号,高8位为 主设备号,因此系统最多分别支持65536个字符设备和65536个 块设备,这个限制已经不能满足当前层出不穷的各种新设备的需要,所以Linux2.6中对 设备号已经进行了扩展,一个设备号为32位,主设备号为12位, 次设备号为20位,但是这32位 设备号的编码方式有新旧两种,旧的设备编号格式为:最高12位为主设备号,最低20位 为次设备号;新的设备编号格式为:bit[19:8]是主设备号,bit[31:20]是 次设备号的高12位,bit[7:0]是 次设备号的低8位。如果知道了一个设备的主设备号major和 次设备号minor,那么用MKDEV(major,minor)生 成是该设备的旧格式的设备号,用new_encode_dev(MKDEV(major,minor))生 成的则是新格式的设备号。Linux支持的各种设备的主设备号定义在include/linux/major.h文 件中,而已经在官方注册的主设备号和次设备号在Documentation/devices.txt文件中 可以找到。
老式16位设备 号、32位旧格式设备号以及32位新格式设备号的转换操作函数如下:
将32位旧格式设备号dev转换成32位 新格式设备号。
将32位新格式设备号转换成32位旧格式设备号。
将32位旧格式设备号转换成 老式16位设备号。
dev_t old_decode_dev(u16 val)函 数
将老式16位设备号转换成32位 旧格式设备号。
Linux中设备节点是通过“mknod”命 令来创建的。一个设备节点其实就是一个文件,Linux中 称为设备文件。有一点必要说明的是,在Linux中,所有的设备访 问都是通过文件的方式,一般的数据文件程序普通文件,设备节点称为设备文件。在Linux内 核中网络设备也是通过文件操作的,称为网络设备文件,在用户空间是通过socket接 口来访问的。socket号就是网络设备文件描述符。
如:mknod /dev/mydevice c 254 0
(c代 表子都设备,254为 主设备号,0为 次设备号)
Open,close等 操作/dev/下 设备文件,内核根据文件的主设备号找到对应的设备驱动
主 设备号可以分为动态和静态申请。
设备文件Linux使用对文件一样管理方式来管理设备,所以对于系统中的每个字符设备或者块设备都必须 为其创建一个设备文件,这个设备文件就是放在/dev/目 录下的设备节点,它包含了该设备的设备类型(块设备或字符设备)、设备号(主设备号和次设备号)以及设备访问控制属性等。设备文件可以通过手工用mknod命令生成也可以由udev用 户工具软件在系统启动后根据/sys目录下每个设备的实际信息创 建,使用后一种方式可以为每个设备动态分配设备号,而不必分配固定的设备号,如果系统中的设备不多,而且设备类型又是常见的,可以使用手工方式生成设备文 件,为常用设备创建一个已经分配号的设备号对应的设备文件,这样比较方便。如果系统很大,系统中的设备太多,那么最好动态分配设备号,由udev在系统启动之后根据设备实际信息自动创建设备文件。