需要把流程讲清楚,最好结合代码来讲。

系统消息 若觉得内容不错,请点击左上角的"赞"图标,以优化网站的内容呈现。 另外,请及时验证注册邮箱,否则收不到21QA发出的红包。 官方Q群:250203055

asked 13 Aug '14, 11:28

%E8%B7%AF%E4%BA%BA%E7%94%B2's gravatar image

路人甲
131502581596


Linux字符设备驱动程序工作机理分析


1 本文主题
    本文主要分析Linux字符设备驱动程序的工作机理。主要内容以及代码片段来源于《LDD3》,俺只是从另外一个角度来讲述。
    见过很多关于驱动程序的书,基本上都是告诉你怎么做,然后你STEP BY STEP,然后运行完后结果就出来了,可是其背后到底是如何工作的呢?虽说《LDD3》也讲了很多原理性的东西,但是我觉得这个问题其描述得并不明确。

2 关于scull
    scull是《LDD3》的一个字符设备驱动程序,其加载之后会在文件系统下生成/dev/scull文件,在shell下可以对其进行一系列的操作,例如可以使用cp、dd或者输入输出重定向等命令来访问这个文件,也就是访问这个字符设备。

3 /dev/scull是如何生成的
    该文件是在驱动程序模块加载时生成的,具体实现是在scull_load(可以到www.oreilly.com下载,或者《LDD3_中文》的P51)这个脚本里。
    在shell下insmod了scull.ko之后,系统会在/proc/devices文件里生成设备名以及与之对应的主设备号信息,scull_load脚本根据该信息mknod了/dev/scull文件节点。
    注意下mknod时给的参数信息,其中注明了主设备号以及次设备号信息。

4 shell下操作/dev/scull是如何关联到我们自己编写的scull模块的
    这个问题一直困扰了我很久,不过现在我基本上算是弄明白了。

4.1 先回顾下scull模块程序的初始化操作里做了什么事情。

    搞驱动的都知道,初始化的时候需要进行设备注册,核心代码如下:
    static void scull_setup_cdev(struct scull_dev *dev, int index)
    {
        int err, devno = MKDEV(scull_major, scull_minor + index);

        cdev_init(&dev->cdev, &scull_fops);
        dev->cdev.owner = THIS_MODULE;
        dev->cdev.ops = &scull_fops;
        err = cdev_add(&dev->cdev, devno, 1);
        ...
    }

    scull_fops是一个数组,其中保存了我们的scull实际的操作:
    struct file_operation scull_fops = {
        ...
        .open = scull_open,
        .read = scull_read,
        .write = scull_write,
        .release = scull_release,
        ...
    }

    cdev是struct cdev类型(该类型由系统定义)的变量,其嵌入在struct scull_dev(该类型由scull模块定义)中:
    struct scull_dev{
        ...
        struct cdev cdev;
        ...
    }

    从这句代码
    cdev_add(&dev->cdev, devno, 1);
    我们可以发现,设备号devno已经和内核内部结构cdev关联起来了。

4.2 /dev/scull设备的打开操作

    /dev/scull是文件系统中的一个设备文件,其在内核中由sturct inode结构表示。

    一般说来,inode结构下由两个元素和驱动程序有关:
    dev_t i_rdev;                 这个是设备号
    struct cdev *i_cdev;          这里是一个指针,struct cdev是字符设备在内核里的表示,注意我们在4.1小节里也提到了这个结构。

    也就是说,系统在打开/dev/scull之前仅知道inode信息。在4.1小节里已经说过了,设备号和内核内部结构cdev关联起来了,因此cdev下的ops也和设备号关联起来了,ops已经初始化为scull_fops。所以我们才能够通过inode中的设备号i_rdev定位到此时打开的是哪一个设备,从而去调用相应设备的open操作。

    看看open函数的原型吧:
    int (*open)(struct inode*, struct file*);
    第一个参数是struct inode类型的指针,该指针由内核传递过来,是设备文件在内核中的表示。
    第二个参数struct file类型的指针,open之后,设备文件在内核中就以struct file结构来表示了。

    看看open操作在scull里的实现吧:
    int scull_open(struct inode *inode, struct file *filp)
    {
        struct scull_dev *dev;

        dev = container_of(inode->i_cdev, struct scull_dev, cdev);
        filp->private_data = dev;
        ...
    }
    第一句代码通过宏container_of把包裹inode->i_cdev的结构提取出来了,该结构就是在scull模块里定以的struct scull_dev,其中包含了scull模块所需要的私有信息。
    第二句代码把该私有信息加入内核数据结构struct file,之前说过了,open后的文件在内核里以struct file结构表示。

4.3 /dev/scull的读写操作

    先看下读写操作的函数原型:
    ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write)(struct file*, char __user *, size_t, loff_t *);
    其参数内容我不做过多解释,注意其第一个参数是struct file类型的指针,该指针由内核传入,也就是open之后的那个文件指针。

    通过这个指针,能够找到scull模块的私有数据结构struct scull_dev,并对其操作,我们可以从read/write操作在scull里的实现看到这个做法:
    ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
    {
        sturct scull_dev *dev = filp->private_data;
        int quantum = dev->quantum, qset = dev->qset;
        ...
    }

    ssize_t scull_write(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
    {
        sturct scull_dev *dev = filp->private_data;
        int quantum = dev->quantum, qset = dev->qset;
        ...
    }

5 联系我
    作者:  原野之狼
    Email:  y_y_z_l AT 163.com  (AT换成@,无空格)

6 版权声明
    欢迎转载,但是务必保留本文的完整性。
系统消息 若觉得内容不错,请点击左上角的"赞"图标,以优化网站的内容呈现。 另外,请及时验证注册邮箱,否则收不到21QA发出的红包。 官方Q群:250203055
permanent link

answered 13 Aug '14, 11:30

%E5%8E%9F%E9%87%8E%E4%B9%8B%E7%8B%BC's gravatar image

原野之狼
1.9k9399116

struct file_operations driver_fops = { read: driver_read, write: driver_write, open: driver_open, release: driver_release };

/ Registering device / result = register_chrdev(driver_major, "mysampledriver", &driver_fops); if (result < 0) { printk( "<1>driver: cannot obtain major number %d\n", driver_major); return result; }

系统消息 若觉得内容不错,请点击左上角的"赞"图标,以优化网站的内容呈现。 另外,请及时验证注册邮箱,否则收不到21QA发出的红包。 官方Q群:250203055
permanent link

answered 13 Aug '14, 14:17

htt's gravatar image

htt
26557

没有看明白,想表达什么?

(13 Aug '14, 14:30) 原野之狼 %E5%8E%9F%E9%87%8E%E4%B9%8B%E7%8B%BC's gravatar image
Your answer
toggle preview

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Markdown Basics

  • *italic* or _italic_
  • **bold** or __bold__
  • link: [text](http://url.com/ "title")
  • image: ![alt](/path/img.jpg "title")
  • numbered list: 1. Foo 2. Bar
  • to add a line break simply add two spaces to where you would like the new line to be.
  • basic HTML tags are also supported

Question tags:

×319
×12

question asked: 13 Aug '14, 11:28

question was seen: 2,055 times

last updated: 13 Aug '14, 14:30

powered by O*S*Q*A

粤ICP备14040061号-1