LINUX字符设备驱动总结

如果你曾经学过或使用过Linux字符设备驱动,那么你能回答以下问题吗?

1.什么是Linux字符设备驱动?

2.编写一个Linux字符设备驱动需要哪些知识?

3.用户空间是如何调用到Linux字符设备驱动的?

4.编写Linux字符设备驱动的步骤?

我想如果你能把这些问题回答清楚,你就能写Linux字符设备驱动,对Linux字符设备驱动的原理机制有比较深刻的理解了。下面就让我们带着这些问题去找答案!

 

1.什么是Linux字符设备驱动?

  我们都知道Linux中设备大致可以分为3类:字符设备,块设备,网络设备。字符设备是指那些只能按顺序一个一个字节存储的设备(如:键盘,鼠标,串口,控制台等),有些地方,高级的字符设备也可以从指定的位置一次读取一块数据。每个字符设备都有一个设备号(设备号由主/次设备号构成),而且在Linux系统中把字符设备当作普通文件来管理,因此每个字符设备会在/dev下生存对应的字符设备文件(又称设备节点)。每个设备文件又有自己的设备文件操作函数组结构(struct file_operations)里面往往包含有:open,close,read,write等文件操作方法。

 LINUX字符设备驱动总结_第1张图片

 

2.编写Linux字符设备驱动的步骤?

  之所以先说编写Linux字符设备驱动的步骤,是因为有过驱动编写或学习经历的人都对这个过程耳熟能详,我们大概都知道这个过程,使用哪些API。

步骤:

·申请设备号:动态/静态申请主设备号

·定义cdev结构并初始化:cdev_init(struct cdev* ,const struct file_operations* )

·注册该cdev结构:cdev_add(struct cdev* cdev, dev_t dev, unsigned cont)

 

对于申请设备号,提供了如下API:

int register_chrdev_region(dev_t from, unsigned count, const char *name)

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)

 

对于cdev结构,可以动态申请/也可以静态定义:

struct cdev *cdev_alloc(void)

 

初始化驱动程序结构:

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

需要构造一个file_operations结构。

 

对于注册驱动程序:

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

需要给定cdev结构,起始设备号及设备号范围。

 

3.编写一个Linux字符设备驱动需要哪些知识?

  2.1 对字符设备硬件的了解(有哪些功能,如何使用这些功能?归根结底就是如何配置设备里的这些寄存器)

  2.2 熟悉Linux内核的字符设备驱动框架

  如果你对以上两点都掌握清楚了的话,编写字符设备驱动自然不在话下。如果你能更深入了解这些框架的内部实现机制,则能更好的编写驱动。

  以下将对内核中字符设备驱动中涉及的概念及数据结构,重要函数进行分析:(以Linux2.6.30内核为例,其他版本的Linux2.6内核实现的机制原理是一样的)

kobj_map:内核对象的映射结构

struct kobj_map {

    struct probe { 设备的探测结构

       struct probe *next; 指向主设备号相同或%255相同设备探测结构

       dev_t dev; 设备号(主+次)

       unsigned long range; 设备号的范围

       struct module *owner; 实现该驱动程序模块的指针

       kobj_probe_t *get; 获取该驱动程序模块的函数指针

       int (*lock)(dev_t, void *); 增强驱动程序引用计数器函数指针

       void *data; 指向拥有该设备号范围的设备驱动程序结构指针(cdev)

    } *probes[255];   含有255项探测结构指针数组

    struct mutex *lock; 映射结构的锁

};

在/fs/char_dev.c中静态定义了该结构的全局指针:

static struct kobj_map *cdev_map;

对于kobj_map结构在Linux内核中有如下调用过程:

void __init chrdev_init(void) /* 字符设备初始化 */

   cdev_map = kobj_map_init(base_probe, &chrdevs_lock);

LINUX字符设备驱动总结_第2张图片

其结构如下图所示:

LINUX字符设备驱动总结_第3张图片

 

cdev:字符设备驱动程序结构

  字符设备驱动本身需要一个结构来描述,在Linux2.6.x内核中使用struct cdev结构来描述。

 LINUX字符设备驱动总结_第4张图片

 

char_dev_struct:设备号管理结构

LINUX字符设备驱动总结_第5张图片 

下面就对上面所涉及的核心API及数据结构的调用进行分析,进一步弄清其内部的机制:

register_chrdev_region:

LINUX字符设备驱动总结_第6张图片 

alloc_chrdev_region:

LINUX字符设备驱动总结_第7张图片 

__register_chrdev_region:设备号分配的核心函数

第一步:动态分配设备号管理结构

 

第二步:填充该设备号管理结构

LINUX字符设备驱动总结_第8张图片 

第三步:将该设备号管理结构指针装入全局设备号管理结构指针数组中

  该步分成两种情况:该主设备相同的插槽中已经填入过char_dev_struct结构指针和该插槽至今为空。如果至今为空,直接插入即可;如果已经有主设备号相同或%255相同的char_dev_struct指针插入过,需进行一些列设备号范围的检查

LINUX字符设备驱动总结_第9张图片 

最终都会将当前的char_dev_struct指针填入插槽中:

 

 

cdev_init:初始化字符设备驱动程序结构

LINUX字符设备驱动总结_第10张图片 

 

cdev_add:注册字符设备驱动

LINUX字符设备驱动总结_第11张图片 

 

kobj_map:注册字符设备驱动的核心函数

LINUX字符设备驱动总结_第12张图片 

LINUX字符设备驱动总结_第13张图片

 

具体图示如下:

LINUX字符设备驱动总结_第14张图片

3.用户空间是如何调用到Linux字符设备驱动的?

经过以上的分析,已经具备编写一个简单字符设备驱动的能力。但是,我们在用户空间的open,read,write,ioctl等操作方法是如何映射到驱动中file_operations实现的方法的呢?那就跟我进行如下分析:

如果不使用自动创建设备节点的话,我们正常加载驱动程序后的第一步就是创建设备文件:

mknod /dev/xxx c[b] major minor

第二步便是对该设备文件进行各种文件操作:

open/read/write/ioctl/...

先分析第一步:创建设备文件

sys_mknod

  vfs_mknod: 为设备节点创建一个目录条目对象,调用当前文件系统的mknod方法,如果文件系统为ext2,对应的方法为ext2_mknod()。

ext2_mknod

      ext2_new_inode (dir, mode); 首先为设备节点创建新的索引节点结构对象

      init_special_inode(inode, inode->i_mode, rdev); 对索引节点进行初始化

        if(S_ISCHR(mode)){  如果是字符设备

  inode->i_fop = &def_chr_fops字符设备操作函数组用全局默认操作函数集

  inode->i_rdev = rdev; 将字符设备的设备号赋值给索引节点对象的i_rdev自段

}

执行open/read/write/ioctl文件操作:

open: 用户空间

  sys_open:系统调用

filp_open

  do_filp_open

    dentry_open

首先为设备文件申请未使用的文件句柄号,然后动态的申请一个struct file结构与其关联起来,最终最struct file进行初始化,将设备文件索引节点的文件操作函数组结构指针i_fop赋值给struct file.f_op重要struct file.f_op = &def_chr_fops。def_chr_fops中只含open方法chrdev_open

chrdev_open会根据设备文件索引节点的i_rdev从内核对象映射结构数组指针cdev_map查到i_rdev对应的字符设备驱动(struct cdev)并将设备文件操作函数组的指针赋给struct file.f_op,最后再调用struct file.f_op.open从而驱动的open方法被调用。其他设备方法的操作过程类似。

你可能感兴趣的:(Linux驱动)