Linux设备驱动–(五)字符设备驱动-上
一、字符设备驱动基础
-
基本概念
在前面几篇博客里,已经基本介绍了Linux的驱动的的分类情况。所谓的字符设备就是指其数据的处理是以字节流的形式。
我们都知道有一句经典的话"linux下一切皆文件",这句话的意思意思是什么呢?这就意味着,我们的设备最终会议文件的形式被上层去调用(当然啦,网络设备是一个意外)。这样做的好处呢,就是为了统一上层接口。设想一下,字符设备多种多样,如果我们能够将其统一以文件的形式呈现给应用程序,那么这岂不是很方便,而且也很便于管理。设备文件通常位于/dev/目录下。我们可以使用一下指令渠道该目录下面去查看相关的设备文件。
ls -l /dev
虽说设备也被当做文件来看待,但是呢。涉备文件比普通文件多路两个数字。这两个数字就是主设备号和次设备号。这两个号是设备在内核中的身份标志,是内核区分不同设备的唯一信息。
主设备号:用来区别一类设备
次设备号:用于区分同一类设备的不同个体
路径名:用户层用来区别设备信息的
-
创建设备
设备文件可以自动创建,有可以手动创建。
-
手动创建设备号
手动创建设备文件使用命令mknod,其含义使make node,即创建一个设备结点,也说创建一个设备文件。(这里的节点是存在于磁盘上面的,也有存在于内存上面的节点)
# mknode /dev/hello c 256 0 @/dev/hello :设备文件的路径 @c : 代表字符设备,另外b代表块设备。你会发现没有网络设备的没有代表符啊,仔细看上面,网络设备是不能当做文件来看的,关于网络设备的驱动后米娜会介绍到,不着急。 @256 :主设备号 @0:次设备号 //mknod命令将文件名,文件类型和主次设备号都保存到磁盘上。
- 自动创建
struct class *class_create(owner, name)//创建一个类 参数1: THIS_MODULE 参数2: 字符串名字,自定义 返回一个class指针 //创建一个设备文件 struct device *device_create(struct class * class, struct device * parent, dev_t devt, void * drvdata, const char * fmt,...) 参数1: class结构体,class_create调用之后到返回值 参数2:表示父亲,一般直接填NULL 参数3: 设备号类型 dev_t dev_t devt #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) 参数4:私有数据,一般直接填NULL 参数5和6:表示可变参数,字符串,表示设备节点名字 销毁动作: void device_destroy(devcls, MKDEV(dev_major, 0)); 参数1: class结构体,class_create调用之后到返回值 参数2: 设备号类型 dev_t void class_destroy(devcls); 参数1: class结构体,class_create调用之后到返回值
-
二、字符设备驱动框架
-
应用程序与驱动程序间联系
我们知道,我们的设备文件最终是要被应用程序调用的。那么应用程序最终有时怎样访问到我们的设备的呢?
前面讲到过,在Linux中,每个文件都有一个struct inode结构体来描述,该结构体中记录了文件的所有信息,比如文件名,文件类型,访问权限等。
另外还要提到一点,每当我们打开一个文件,Linux操作系统就会在VFS层为其分配一个struct file结构体来描述打开的的这个文件。
-
当我们打开设备文件时,会根据相应的设备文件相对应的struct inode结构体的描述信息,获取该设备的相关信息。如其实一个字符设备还是以个块设备以及其文件权限。同时Linux还会分配一个struct file结构体来描述我们打开的设备文件。
-
根据struct inode结构体重还存放着描述字符设备的结构体struct cdev。struct cdev函数中有一个struct file_operations结构体记载着设备操作函数的接口。
-
而我们struct file结构体中也有一个,struct file_operations结构体,当然了该结构体也是用来存放社诶相关操作函数的入口(其实就是相关操作函数的入口地址)。那么这个地址怎么来呢?我们通过open打开这个设备之后,Linux系统就会将struct inode结构体中记录的设备的操作函数的入口地址给拷贝到struct file结构体中。如此一来上层设备就获得的相关操作函数的地址了,就可以去操作相关的设备文件了。当然啦,实际上open函数系统调用底层还是很复杂的,这里将其做了非常大的简化。只是为了讲清楚其大体的框架。
如果要追究其深层的原因,建议可以去看一下《Linux设备驱动程序》。
当然啦,因为我个人比较懒,又没有找到一个很好的画图工具。说以就没有没有画图,只看文字的话可能初学者还是有点儿蒙的。所以呢,所以将之前在网上看到的一个讲字符驱动讲的很好的博客。链接如下: