Linux驱动基础总结

来源:互联网 发布:淘宝买家号权重是什么 编辑:程序博客网 时间:2024/05/17 08:43

文件系统

什么是文件?凡是可以产生和消耗信息的都是文件。而不仅只有存储信息的东西是文件,比如socket,它不存储信息,但是可读写信息,也可以认为也是一个文件。文件系统是个相对模糊的概念,不同的场合有不同的含义,比如:

1.       特定的文件格式。如ext2,NTFS

2.       按照特定格式格式化一块存储介质,比如安装或卸载一个文件系统。

3.       操作系统中管理文件系统以及对文件进行操作的机制及其实现

Unix系统一开始就讲所有的设备看成文件,都通过文件操作的界面进行操作。


可安装模块

可安装模块是经过编译但还没有连接的目标代码.O文件,可以动态安装到内核。安装的过程其实就是链接的过程,其实就是在内核中查找对应的变量,然后内核把变量地址返回给调用者----这其实就是链接的基本过程。

动态链接程序库,也可以动态加载,运行,但是不能在根本上解决问题,因为动态链接库程序是在用户空间运行的。可安装模块是经过编译但尚未链接的目标文件,即可重定位文件。内核模块和so不同的地方在于,内核模块自己定义自己需要暴露的接口----也就是export一些符号。使可安装模块可以在程序中调用这些内核子程序或访问这些全局变量。模块安装的时候,其实类似于dlopen,它需要寻找自己内部没有定义的符号的定义,并进行重定位。链接的本质是符号的重定位,只要一个模块的所有符号的地址都找到了,那么它也就链接成功了。

mknod

系统调用mknod是通用的,可以用来创建任何类型的文件(除目录外),包括普通文件,特殊文件和设备文件。不过,普通文件可以用open,create创建,fifo文件可以用pipe创建,所以mknod主要用于设备文件的创建。

insmod

Insmod所做的事情有哪些?

1.     打开模块并读入用户空间

2.     重定向模块中的一些符号----用户空间到内核空间的通信,需要通过系统调用query_module向内核询问这些符号在内核中的地址。内核会试图找到这些符号的地址,可能在内核中,也可能在安装好的其他模块里。

内核根本不知道这个模块的存在,它怎么去调用这个模块??

3.     系统调用create_module在内核中创建一个module数据结构。

4.     系统调用init_module把用户空间中的模块copy进入内核空间,然后调用模块中定义好的init_module函数。模块中的init_module函数。

Module_init和init_module

/* Each module must use one module_init(). */
#define module_init(initfn)                 \
static inline initcall_t __inittest(void)       \
{ return initfn; }                  \
int init_module(void) __attribute__((alias(#initfn)));
 
/* This is only required if you want to be unloadable. */
#define module_exit(exitfn)                 \
static inline exitcall_t __exittest(void)       \
{ return exitfn; }                  \
void cleanup_module(void) __attribute__((alias(#exitfn)));

模块堆栈

驱动模块堆栈,往上是内核,往下是物理器件,中间是一堆先入后出的驱动模块。

init_module。每个模块都有一个init_module,但是如果是按照模块编译,没有问题,如果不是按照模块编译,静态编译的话,就不能用同样的函数名了。所以,如果要静态编译进内核的话,需要换个名字。

上层模块提供一个函数可以让下层登记到上层,下层模块提供一个标准接口集可以让上层可以调用到。下层模块向上层登记的是file_operations结构。内核有个device_struct的结构数组chrdevs,每个元素都是一个device_struct数据结构,所谓登记,就是把内容填充进这个数组:

staticstruct device_struct chrdevs[MAX_CHRDEV]。

还有可能需要在文件系统中为它创建一个节点。

Kobject

Kobject是一个基本的数据结构,一个sys目录对应一个Kobject。Kobject可以看做是所有设备对象的基类,是用C语言实现的面向对象。是用把kobject内嵌到其他结构体里来实现基类的功能。Kobject为linux设备模型提供了很多有用的功能,比如引用计数,接口抽象,父子关系等。内核中的设备是树状结构的,对应于sysfs里的上级目录和下级目录之间的关系。Kobject类中,name表示其再sysfs中的名字,指针parent表示它的父对象,kref表示引用计数,kset指向这个kobject所属的kset,ktype表示kobject的类型。

Kobject的作用:

Sysfs表述

数据结构关联。设备模型很复杂,但是kobject可以通过大量链接构成一个多层次的体系结构。

热插拔事件处理。Kobject子系统将产生的热插拔事件通知到用户空间

一个使用kobject的例子

MODULE_AUTHOR("DavidXie");MODULE_LICENSE("DualBSD/GPL");voidobj_test_release(struct kobject *kobject);ssize_tkobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf);ssize_tkobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf,size_t count);structattribute test_attr = {        .name = "kobj_config",        .mode = S_IRWXUGO,};staticstruct attribute *def_attrs[] = {        &test_attr,        NULL,};  structsysfs_ops obj_test_sysops ={        .show = kobj_test_show,        .store = kobj_test_store,}; structkobj_type ktype = {        .release = obj_test_release,        .sysfs_ops=&obj_test_sysops,        .default_attrs=def_attrs,}; voidobj_test_release(struct kobject *kobject){        printk("eric_test: release.\n");} ssize_tkobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf){        printk("have show.\n");        printk("attrname:%s.\n",attr->name);       sprintf(buf,"%s\n",attr->name);        return strlen(attr->name)+2;}ssize_tkobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf,size_t count){        printk("havestore\n");        printk("write: %s\n",buf);        return count;}structkobject kobj;staticint kobj_test_init(void){        printk("kboject test init.\n");       kobject_init_and_add(&kobj,&ktype,NULL,"kobject_test");        return 0;}staticvoid kobj_test_exit(void){        printk("kobject testexit.\n");        kobject_del(&kobj);}
module_init(kobj_test_init);module_exit(kobj_test_exit);kobject_init_and_add         kobject_init         kobject_add_varg                   kobject_add_internal//看上去像是把kobject添加到kobject树上staticint kobject_add_internal(struct kobject *kobj){         int error = 0;         struct kobject *parent;         if (!kobj)                   return -ENOENT;         if (!kobj->name ||!kobj->name[0]) {                   WARN(1, "kobject: (%p):attempted to be registered with empty "                             "name!\n", kobj);                   return -EINVAL;         }         parent = kobject_get(kobj->parent);         /* join kset if set, use it as parentif we do not already have one */         if (kobj->kset) {                   if (!parent)                            parent =kobject_get(&kobj->kset->kobj);                   kobj_kset_join(kobj);                   kobj->parent = parent;         }         pr_debug("kobject: '%s' (%p): %s:parent: '%s', set: '%s'\n",                    kobject_name(kobj), kobj, __func__,                    parent ? kobject_name(parent) :"<NULL>",                    kobj->kset ? kobject_name(&kobj->kset->kobj): "<NULL>");         error = create_dir(kobj);         if (error) {                   kobj_kset_leave(kobj);                   kobject_put(parent);                   kobj->parent = NULL;                   /* be noisy on error issues*/                   if (error == -EEXIST)                            WARN(1, "%sfailed for %s with "                                 "-EEXIST, don't try to registerthings with "                                 "the same name in the samedirectory.\n",                                 __func__, kobject_name(kobj));                   else                            WARN(1, "%sfailed for %s (error: %d parent: %s)\n",                                 __func__, kobject_name(kobj), error,                                 parent ? kobject_name(parent) :"'none'");         } else                   kobj->state_in_sysfs = 1;         return error;}


总线和设备

Abus is a channel between the processor and one or more devices. For thepurposes of the device model, all devices are connected via a bus, even if itis an internal, virtual, "platform" bus. Buses can plug into eachother.A USB controller is usually a PCI device, for example. The devicemodelrepresents the actual connections between buses and the devices they control.Abus is represented by the bus_type structure. It contains the name, thedefaultattributes, the bus' methods, PM operations, and the driver core's private data

bus_register是把bus注册到内核中

这里要描述的上层容器包括总线类型(bus_type)、设备(device)和驱动(device_driver),这3个模型环环相扣。为何称为容器?因为bus_type/device/device_driver结构都内嵌了Linux设备的底层模型(kobject结构)。为什么称为上层而不是顶层?因为实际的驱动设备结构往往内嵌bus_type/device/device_driver这些结构,比如pci,usb等。

Platform驱动

从 Linux 2.6 起引入了一套新的驱动管理和注册机制 :Platform_device 和 Platform_driver 。Linux 中大部分的设备驱动,都可以使用这套机制 ,  设备用 Platform_device 表示,驱动用 Platform_driver 进行注册。定义好了 platform_device 结构体后就可以调用函数 platform_add_devices 向系统中添加该设备了,之后可以调用 platform_driver_register()进行设备注册,最后调用platform_device_add添加设备。insert_resource是插入一个resource到内核的resource树。。。又是红黑树??device_add是把设备添加到一一棵设备树

在platform_device注册完成后,就可以进行platform_driver注册了,在驱动初始化函数中调用函数platform_driver_register() 注册 platform_driver ,需要注意的是 s3c_device_i2c 结构中 name 元素和 s3c6410_i2c_driver 结构中 driver.name 必须是相同的,这样在 platform_driver_register()注册时会对所有已注册的所有 platform_device 中的 name 和当前注册的 platform_driver 的 driver.name 进行比较,只有找到相同的名称的 platfomr_device 才能注册成功。bus_add_driver是添加驱动到驱动树。Device和driver本是不相干的东西,因为bus联系到了一起。

0 0