linux系统将设备分为3类:字符设备、块设备、网络设备。可以把一个系统划分为应用、文件系统、设备驱动结构示意图如下:

linux内核驱动面试题(linux设备驱动)(1)

设备分层示意图

字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据。字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台和LED设备等。

块设备:是指可以从设备的任意位置读取一定长度数据的设备。块设备包括硬盘、磁盘、U盘和SD卡等。

每一个字符设备或块设备都在/dev目录下对应一个设备文件。linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作字符设备和块设备

字符设备驱动模型

linux内核驱动面试题(linux设备驱动)(2)

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

功能:申请使用从from开始的count 个设备号(主设备号不变,次设备号增加)

静态申请相对较简单,但是一旦驱动被广泛使用,这个随机选定的主设备号可能会导致设备号冲突,而使驱动程序无法注册。

动态方法如下

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

功能:请求内核动态分配count个设备号,且次设备号从baseminor开始。

动态申请简单,易于驱动推广,但是无法在安装驱动前创建设备文件(因为安装前还没有分配到主设备号)。

静态申请是已知起始设备号的情况,如先使用cat /proc/devices 命令查得哪个设备号未事先使用(不推荐使用静态申请);动态申请是由系统自动分配,只需设置major = 0即可

2 初始化cdev

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

该函数用于初始化 cdev 的成员,并建立 cdev 和 file_operations 之间的连接。

struct cdev *cdev_alloc(void);

用于动态分配一个cdev结构

int cdev_add(struct cdev *, dev_t, unsigned);

向内核注册一个cdev结构

void cdev_del(struct cdev *);

向内核注销一个cdev结构

3 注册cdev

int cdev_add(struct cdev *, dev_t, unsigned);

用于向系统添加一个 cdev,完成字符设备的注册。

(二)设备实现操作

用户空间的程序以访问文件的形式访问字符设备,通常进行open、read、write、close等系统调用。而这些系统调用的最终落实则是file_operations结构体中成员函数,它们是字符设备驱动与内核的接口

这个结构体是字符设备当中最重要的结构体之一,file_operations 结构体中的成员函数指针是字符设备驱动程序设计的主体内容。

static struct file_operations globalmem_fops = {

.owner = THIS_MODULE,

.open = globalmem_open,

.release = globalmem_close,

.read = globalmem_read

};

代码中的globalmem_open、globalmem_close、globalmem_read是要在具体的驱动中自己实现的。

(三)驱动注销

1 删除 cdev

在字符设备驱动模块卸载函数中通过cdev_del()函数向系统删除一个cdev,完成字符设备的注销。

void cdev_del(struct cdev *);

2 释放设备号

在调用cdev_del()函数从系统注销字符设备之后,unregister_chrdev_region()应该被调用以释放原先申请的设备号

void unregister_chrdev_region(dev_t from, unsigned count);

,