linux分类驱动对字符设备框架压力的卸载

晨曦之光 发布于 2012/04/10 14:59
阅读 118
收藏 0

2.6内核引入了input字系统,usb子系统,misc子系统等一系列字符设备子系统,在熟练掌握这些子系统之后,我们来看一下linux内核设计这么些子系统的意义何在?可以连接的设备越来越多,这些设备的种类也越来越杂,传统的字符设备/块设备的分类已经不能满足要求,以字符设备为例,现在的linux字符设备体系已经不能代表所有支持的字符设备的最小交集,况且,在随着设备的增多,越来越长的线性设备链表给管理带来了诸多不便,因此“需要增加一个层”来给字符设备体系卸载一些压力了,于是就将所有的“一类”设备总结成“一个”设备,然后将这一个设备作为字符设备加入链表,而真正的字符设备需要新设计的“层”来管理了,在重新设计之后,被总结出来的“那个”字符设备已经不再是传统的设备了,而是“设备类”了,同时,/dev目录下也有了相应的“设备类”,比如/dev/input,这个下面有一系列文件,其中有些文件就代表上面所说的“设备类”,比如mice就是所有的鼠标,而还有些文件代表真实的设备,比如mouse0,mouse1之类的。从应用的角度来看,input子系统确实卸载了字符设备系统的压力,只因新增了一个层,至于这种层该如何实现,还是要看代码:
static int __init input_init(void)
{
    int err;
    err = class_register(&input_class);
    err = input_proc_init();
    err = register_chrdev(INPUT_MAJOR, "input", &input_fops); //只需要初始化一个字符设备即可,主设备号为0xd
    return 0;
}
static const struct file_operations input_fops = {
    .owner = THIS_MODULE,
    .open = input_open_file,
};
static int input_open_file(struct inode *inode, struct file *file)
{
    struct input_handler *handler;
    const struct file_operations *old_fops, *new_fops = NULL;
    handler = input_table[iminor(inode) >> 5]; //可看出,目前input子系统只支持8类设备
    new_fops = fops_get(handler->fops); //得到input子系统“层”中设置的真正的fops
    old_fops = file->f_op;
    file->f_op = new_fops;
    err = new_fops->open(inode, file);
    ...
}
由上述字符设备体系的代码可见,被卸载的真正的字符设备需要input层来集中实现。可以想见,input子系统中一个设备类就应该有一个handler,由设备文件inode得到的minor来索引,我们看一个例子:
# stat mice
File: `mice'
Size: 0               Blocks: 0          IO Block: 4096   character special file
Device: 1607h/5639d     Inode: 23921566    Links: 1     Device type: d,3f  #主设备号为0xd,次设备号为0x3f
...
注意主次设备号,input子系统将整个设备号空间按照高3位分割成了8个子空间,每一个子空间代表input子系统中一类设备的所有设备。
static int __init mousedev_init(void)
{
    mousedev_mix = mousedev_create(NULL, &mousedev_handler, MOUSEDEV_MIX); //建立了/dev/input/mice文件
    input_register_handler(&mousedev_handler);
    return 0;
}
mousedev_init只是初始化了input子系统中鼠标设备的框架,然后通过mousedev_create创建了一个通用的鼠标类,那么真实的鼠标类文件何时建立呢?每一类input设备都有一个input_handler,对于鼠标就是mousedev_handler,它通过mousedev_init注册进系统,所有的handler形成一个链表,同时系统中input子系统还拥有另一条链表,那就是input设备,每当有新的设备插入机器时,在更底层的设备驱动知道这是一个input设备时,就将之纳入input子系统的管理,也就是初始化一个input_dev,然后将之链接进input_dev链表,随后遍历input_handler链表,在找到“可以处理该设备”的input_handler之后调用其connect方法,对于鼠标那就是mousedev_connect,在mousedev_connect中会初始化一个鼠标设备:mousedev_create:
static struct mousedev *mousedev_create(struct input_dev *dev, struct input_handler *handler, int minor)
{
    struct mousedev *mousedev;
    mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL);
    ...
    if (minor == MOUSEDEV_MIX)
        strlcpy(mousedev->name, "mice", sizeof(mousedev->name));
    else
        snprintf(mousedev->name, sizeof(mousedev->name), "mouse%d", minor);
...
    mousedev->dev.devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor);//用于创建/dev/input下面的文件
...
}
input子系统中input_dev链表和input_handler链表相认的过程和底层bus中的的device链表和device_driver链表相认的过程几乎是一样的,运用了相同的思想。在device和device_driver的层次,顶多在pci的层次,由probe函数调用input子系统的初始化规程,比如分配input_dev,注册中断等等,注册好input_dev之后,该设备就被纳入input子系统的管辖了,整个层次是device--pci...--input_dev--input_handler--input字符设备--/dev/input/xxx,在中断发生的时候,中断处理程序会从参数中得到input_dev,然后就可以用input_report_XX来报告了,该报告可以顺着刚刚说过的层次线索一直到达用户空间需要该中断结果的地方,比如移动了一下鼠标的中断会被Xwindow来使用。
     input子系统仅仅是一个卸载原始结构压力的一个例子,诸如usb子系统,sound子系统以及misc子系统都是这样实现的,给你的感觉并没有什么不一样,如果所有的字符设备统统直接在chrdev框架下实现,/dev下将被创建所有的字符设备,如果很多字符被分类了,比如分到了input类中,或者分到了usb类中,那么每一个设备依然拥有一个/dev下的字符设备文件,只是位置不同了,可能被放入了input,usb子目录下,stat的结果仍然是character special file,没有什么不同,在使用sysfs/udevd的情况下,你甚至可以不建立input,usb等子目录,依然将所有文件建立在/dev目录下也没什么不可。注意,input/usb/misc等子系统仅仅规整了内核设备驱动的结构,这种规整对于用户空间是透明的。我感觉,linux内核的这种规整最能体现计算机业的一句真理,那就是“加一个层次”,linux内核通过引入新的层次,在不改变原有字符驱动结构的情况下,成功将几个新的分类驱动层次插入到了既有的框架,这同时也说明了linux内核中字符设备框架原本的灵活性。


原文链接:http://blog.csdn.net/dog250/article/details/5676429
加载中
返回顶部
顶部