深入理解Linux字符设备驱动

本文深入探讨Linux字符设备驱动,从应用程序访问设备驱动开始,分析字符设备的软件层次、设备文件创建、mdev原理,涵盖了设备驱动编写、设备文件系统、tmpfs以及设备文件的创建和管理。通过设备类、设备文件的创建、open设备文件、字符设备驱动的注册等关键步骤,阐述了Linux字符设备驱动的完整流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章从上层应用访问字符设备驱动开始,一步步地深入分析Linux字符设备的软件层次、组成框架和交互、如何编写驱动、设备文件的创建和mdev原理,对Linux字符设备驱动有全面的讲解。

本文整合之前发表的《Linux字符设备驱动剖析》和《 Linux 设备文件的创建和mdev》两篇文章,基于linux字符设备驱动的所有相关知识给读者一个完整的呈现。

一、从最简单的应用程序入手

1.很简单,open设备文件,read、write、ioctl,最后close退出。如下:


二、/dev目录与文件系统

2.     /dev是根文件系统下的一个目录文件,/代表根目录,其挂载的是根文件系统的yaffs格式,通过读取/根目录这个文件,就能分析list出其包含的各个目录,其中就包括dev这个子目录。即在/根目录(也是一个文件,其真实存在于flash介质)中有一项这样的数据:

是否目录  偏移     大小    名称 -- --

1    0xYYYY 0Xmmm   dev -- --

Ls/ 命令即会使用/挂载的yaffs文件系统来读取出根目录文件的内容,然后list出dev(是一个目录)。即这时还不需要去读取dev这个目录文件的内容。Cd dev即会分析dev挂载的文件系统的超级块的信息,superblock,而不再理会在flash中的dev目录文件的数据。


3.     /dev在根文件系统构建的时候会挂载为tmpfs. Tmpfs是一个基于虚拟内存的文件系统,主要使用RAM和SWAP(Ramfs只是使用物理内存)。即以后读写dev这个目录的操作都转到tmpfs的操作,确切地讲都是针对RAM的操作,而不再是通过yaffs文件系统的读写函数去访问flash介质。Tmpfs基于RAM,所以在掉电后回消失。因此/dev目录下的设备文件都是每次linux启动后创建的。

挂载过程:/etc/init.d/rcS

                      Mount –a 会读取/etc/fstab的内容来挂载,其内容如下:

4.     /dev/NULL和/dev/console是在制作根文件系统的时候静态创建的,其他设备文件都是系统加载根文件系统和各种驱动初始化过程中自动创建的,当然也可以通过命令行手动mknod设备文件。

 

三、设备文件的创建

5.     /dev目录下的设备文件基本上都是通过mdev来动态创建的。mdev是一个用户态的应用程序,位于busybox工具箱中。其创建过程包括:

1)  驱动初始化或者总线匹配后会调用驱动的probe接口,该接口会调用device_create(设备类, 设备号, 设备名);在/sys/class/设备类目录生成唯一的设备属性文件(包括设备号和设备名等信息),并且发送uvent事件(KOBJ_ADD和环境变量,如路径等信息)到用户空间(通过socket方式)。

2)  mdev是一个work_thread线程,收到事件后会分析出/sys/class/设备类的对应文件,最终调用mknod动态来创建设备文件,而这个设备文件内容主要是设备号(这个设备文件对应的inode会记录文件的属性是一个设备(其他属性还包括目录,一般文件,符号链接等))。应用程序open(device_name,…)最重要的一步就是通过文件系统接口来获得该设备文件的内容—设备号。

6.     如果初始化过程中没有调用device_create接口来创建设备文件,则需要手动通过命令行调用mknod接口来创建设备文件,方可在应用程序中访问。

7.     mknod接口分析,通过系统调用后对应调用sys_mknod,其是vfs层的接口。

Sys_mknod(设备名, 设备号)

vfs通过逐一路径link_path_walk,分析出dev挂载了tmpfs,所以调用tmpfs->mknod

shmem_mknod(structinode *dir, struct dentry *dentry, int mode, dev_t dev)

       inode = shmem_get_inode(dir->i_sb,dir, mode, dev, VM_NORESERVE);

              inode = new_inode(sb);

              switch (mode & S_IFMT) {

              default:

                     inode->i_op =&shmem_special_inode_operations;

                     init_special_inode(inode,mode, dev);//以下是函数展开

                           

                     break;

              case S_IFREG://file

              case S_IFDIR://DIR

              case S_IFLNK://dentry填入inode信息,这时对应的dentry和inode都已经存在于内存中。

       d_instantiate(dentry, inode);

可见,tmpfs的目录和文件都是像ramfs一样一般都存在于内存中。通过ls命令来获取目录的信息则由dentry数据结构的内容来获取,而文件的信息由inode数据结构的内容来提供。Inode包括设备文件的设备号i_rdev,文件属性(i_mode: S_ISCHR),inode操作集i_fop(对于设备文件来说就是如何open这个inode)。

 

四、open设备文件

9. open设备文件的最终目的是为了获取到该设备驱动的file_operations操作集,而该接口集是struct file的成员,open返回file数据结构指针:

struct file {

       conststruct file_operations   *f_op;

       unsignedint             f_flags;//可读,可写等

       …

};

以下是led设备驱动的操作接口。open("/dev/LED",O_RDWR)就是为了获得led_fops。

static conststruct file_operations led_fops = {

    .owner          =THIS_MODULE,

    .open            =led_open,

    .write     = led_write,

};

10. 仔细看应用程序int fd =open("/dev/LED",O_RDWR),open的返回值是int,并不是file,其实是为了操作系统和安全考虑。fd位于应用层,而file位于内核层,它们都同属进程相关概念。在Linux中,同一个文件(对应于唯一的inode)可以被不同的进程打开多次,而每次打开都会获得file数据结构。而每个进程都会维护一个已经打开的file数组,fd就是对应file结构的数组下标。因此,file和fd在进程范围内是一一对应的关系。

11. open接口分析,通过系统调用后对应调用sys_open,其是vfs层的接口

Sys_open(/dev/led)

SYSCALL_DEFINE3(open,const char __user *, filename, int, flags, int, mode)

do_sys_open(AT_FDCWD,/dev/tty, flags, mode);

               

//path_init返回时nd->dentry即为搜索路径文件名的起点

//link_path_walk一步步建立打开路径的各个目录的dentry和inode

 

      

       其中inode->i_fop在mknod的init_special_inode调用中被赋值为def_chr_fops。以下该变量的定义,因此, open(inode, f)即调用到chrdev_open。其可以看出是字符设备所对应的文件系统接口,我们姑且称其为字符设备文件系统。

conststruct file_operations def_chr_fops = {

       .open =

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值