字符设备的内核抽象

字符设备的内核抽象

顾名思义,字符设备驱动程序管理的核心对象是字符设备。从字符设备驱动程序的设计框架角度出发,内核为字符设备抽象出了一个具体的数据结构struct cdev,其定义如下:

<include/linux/cdev.h>

struct cdev {

       struct kobject kobj;

       struct module *owner;

       const struct file_operations *ops;

       struct list_head list;

       dev_t dev;

       unsigned int count;

};

在本章后续的内容中将陆续看到它们的实际用法,这里只把这些成员的作用简单描述如下:

struct kobject kobj

内嵌的内核对象,其用途将在“Linux设备驱动模型一章中讨论。

struct module *owner

字符设备驱动程序所在的内核模块对象指针。

const struct file_operations *ops

字符设备驱动程序中一个极其关键的数据结构,在应用程序通过文件系统接口呼叫到设备驱动程序中实现的文件操作类函数的过程中,ops指针起着桥梁纽带的作用。

struct list_head list

用来将系统中的字符设备形成链表。

dev_t dev

字符设备的设备号,由主设备号和次设备号构成。

unsigned int count

隶属于同一主设备号的次设备号的个数,用于表示由当前设备驱动程序控制的实际同类设备的数量。

设备驱动程序中可以用两种方式来产生struct cdev对象。一是静态定义的方式,比如在前面的那个示例程序中,通过下列代码静态定义了一个struct cdev对象:

static struct cdev chr_dev;

另一种是在程序的执行期通过动态分配的方式产生,比如:

static struct cdev *p = kmalloc(sizeof(struct cdev), GFP_KERNEL);

其实Linux内核源码中提供了一个函数cdev_alloc,专门用于动态分配struct cdev对象。cdev_alloc不仅会为struct cdev对象分配内存空间,还会对该对象进行必要的初始化:

<fs/char_dev.c>

struct cdev *cdev_alloc(void)

{

       struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);

       if (p) {

              INIT_LIST_HEAD(&p->list);

              kobject_init(&p->kobj, &ktype_cdev_dynamic);

       }

       return p;

}

需要注意的是,内核引入struct cdev数据结构作为字符设备的抽象,仅仅是为了满足系统对字符设备驱动程序框架结构设计的需要,现实中一个具体的字符硬件设备的数据结构的抽象往往要复杂得多,在这种情况下struct cdev常常作为一种内嵌的成员变量出现在实际设备的数据机构中,比如:

struct my_keypad_dev{

       //硬件相关的成员变量

       int a;

       int b;

       int c;

      

       //内嵌的struct cdev数据结构

       struct cdev cdev;

}

在这样的情况下,如果要动态分配一个struct real_char_dev对象,cdev_alloc函数显然就无能为力了,此时只能使用下面的方法:

static struct real_char_dev *p = kzalloc(sizeof(struct real_char_dev), GFP_KERNEL);

前面讨论了如何分配一个struct cdev对象,接下来的一个话题是如何初始化一个cdev对象,内核为此提供的函数是cdev_init

<fs/char_dev.c>

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

{

       memset(cdev, 0, sizeof *cdev);

       INIT_LIST_HEAD(&cdev->list);

       kobject_init(&cdev->kobj, &ktype_cdev_default);

       cdev->ops = fops;

}

函数的代码非常直白,不再赘述。一个struct cdev对象在被最终加入系统前,都应该被初始化,无论是直接通过cdev_init或者是其他途径。理由很简单,这是Linux系统中字符设备驱动程序框架设计的需要。

照理在谈完cdev对象的分配和初始化之后,下面应该讨论如何将一个cdev对象加入到系统了,但是由于这个过程需要用到设备号相关的技术点,所以暂且先来探讨设备号的问题。

 

——本段文字节选自《深入Linux设备驱动程序内核机制》

图书详细信息:

http://www.cnblogs.com/broadview/archive/2012/02/21/2361867.html

原文地址:https://www.cnblogs.com/broadview/p/2373945.html