首先,需要明白驱动程序的工作流程。
正常我们在操作设备时的顺序是这样的。(1)在应用层,直接通过open、read、或者说是write等函数。(2)这些函数都是在C库中实现的,可以肯定的是C库中对其反应是发生异常SWI,触发异常swi val。根据val的不同进入不同的异常处理。(3)进入驱动函数,内核空间()
然后,需要明白设备如何告知linux内核?即驱动程序的工作流程:1)定义一个file_operation结构体 2)注册(主设备号,名字,file_operation)。就是将file_operation放到file_operation字符数组的主设备号项中。注册函数有registre_chrdev这是注册到字符数组中。 3)谁来调用它??驱动入口函数 module_init 4)修饰module_init(函数指针,指下入口函数),定义一个结构体,这个结构体里有一个函数指针,指向入口函数。当加载或安装一个驱动时(insmod),内核会自动找到一个这样的结构体,调用它指向的函数。
open("/dev/xxx"),注意这里的"xxx"是怎么来的?也就是我们常说的,生成驱动设备节点文件的方法
1)手工建立
mknode /dev/xxx c 主设备号 次设备号
这里的主设备号是通过cat /proc/device,而次设备号是自己定的
2)自动创建:udev(对于busybox就是mdev)。mdev会根据系统信息,在/sys/注册一个驱动程序时会在该系统下生成注册信息。而mdev会根据系统信息自动创建设备节点。
insmod进去加载一驱动之后,肯定会更改信息,为什么一更改信息,mdev就会自动运行生成呢?是因为脚本文件/etc/init.d。“echo /sbin/mdev > /proc/sys/hotplug”。
方法创建一个类,然后再在这个类下创建一个设备。
另外,写驱动程序和普通的骒驱有什么区别?
在单片机里我们直接对物理地址操作,而在驱动程序中我们需要对地址重映射之后才行。
混杂设备()在Linux系统中,存在一类字符设备,它们共享一个主设备号(10),但次设备号不同,我们称这类设备为混杂设备(miscdevice)。所有的混杂设备形成一个链,对设备访问时内核根据次设备号查找到相应的miscdevice设备。
module_init()和init_module()这两个加载函数有什么区别吗?
init_module是默认的模块的入口,如果你想指定其他的函数作为模块的入口就需要module_init函数来指定。
init_module()是真正的入口,module_init是宏,如果在模块中使用,最终还是要转换到init_module()上。如果不是在模块中使用,module_init可以说没有什么作用。总之,使用module_init方便代码在模块和非模块间移植
linux kernel中有很大一部分代码是设备驱动代码,这些驱动代码都有初始化和反初始化函数,这些代码一般都只执行一次,为了有更有效的利用内存,这些代码所占用的内存可以释放出来。
linux就是这样做的,对只需要初始化运行一次的函数都加上__init属性。在kernel初始化后期,释放所有这些函数代码所占的内存空间。它是怎么做到的呢?看过module_init和module_exit 的人知道,连接器把带__init属性的函数放在同一个section里,在用完以后,把整个section释放掉。
口说无凭,我们看源码,init/main.c中start_kernel是进入kernel的第一个c函数,在这个函数的最后一行是
rest_init();
static void rest_init(void)
{
kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
unlock_kernel();
cpu_idle();
}
创建了一个内核线程,主函数init,代码如下:
static int init(void * unused)
{
lock_kernel();
do_basic_setup();
prepare_namespace();
/*
* Ok, we have completed the initial bootup, and
* we're essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff..
*/
free_initmem();
unlock_kernel();
红色那行代码就是用来释放初始化代码和数据的。
void free_initmem(void)
{
#ifndef CONFIG_XIP_ROM
if (!machine_is_integrator()) {
free_area((unsigned long)(&__init_begin),
(unsigned long)(&__init_end),
"init");
}
#endif
}