Linux 字符设备驱动

1. cdev 结构体

(1)在 Linux 内核中,使用 cdev 结构体描述一个字符设备,定义如下:

struct cdev {
	struct kobject kobj;					/* 内嵌的 kobject 对象 */
	struct module *owner;					/* 所属模块 */
	const struct file_operations *ops;			/* 文件操作结构体 */
	struct list_head list;
	dev_t dev;						/* 设备号 */
	unsigned int count;
} __randomize_layout;

    cdev 结构体的 dev_t 成员定义了设备号,为 32 位,其中 12 位为主设备号,20 位为次设备号。使用下列宏可以从 dev_t 获得主设备号和次设备号:

MAJOR(dev_t dev)
MINOR(dev_t dev)

而使用下列宏则可以通过主设备号和次设备号生成 dev_t:

MKDEV(int major, int minor)

    cdev 结构体的另一个重要成员 file_operations 定义了字符设备驱动提供给虚拟文件系统的接口函数。

(2)Linux 内核提供了一组函数用于操作 cdev 结构体:

void cdev_init(struct cdev *, const struct file_operations *);
struct cdev *cdev_alloc(void);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);

    cdev_init() 函数用于初始化 cdev 的成员,并建立 cdev 和 file_operations 之间的连接,其源代码如下所示:

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
	memset(cdev, 0, sizeof *cdev);
	INIT_LIST_HEAD(&cdev->list);
	kobject_init(&cdev->kobj, &ktype_cdev_default);
	cdev->ops = fops;	/* 将传入的文件操作结构体指针赋值给 cdev 的 ops */
}

    cdev_alloc() 函数用于动态申请一个 cdev 内存,其源代码如下所示:

struct cdev *cdev_alloc(void)
{
	struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
	if (p) {
		INIT_LIST_HEAD(&p->list);
		kobject_init(&p->kobj, &ktype_cdev_dynamic);
	}
	return p;
}

    cdev_add() 函数和 cdev_del() 函数分别向系统添加和删除一个 cdev,完成字符设备的注册和注销。对 cdev_add() 的调用通常发生在字符设备驱动模块加载函数中,而对 cdev_del() 函数的调用则通常发生在字符设备驱动模块卸载函数中。


2. 分配和释放设备号

    在调用 cdev_add() 函数向系统注册字符设备之前,应首先调用 register_chrdev_region() 或 alloc_chrdev_region() 函数向系统申请设备号,这两个函数的原型如下:

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);

    register_chrdev_region() 函数用于已知起始设备的设备号的情况,而 alloc_chrdev_region() 用于设备号未知,向系统动态申请未被占用的设备号的情况,函数调用成功之后,会把得到的设备号放入第一个参数 dev 中。alloc_chrdev_region() 相比于 register_chrdev_region() 的优点在于它会自动避开设备号重复的冲突。

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

void unregister_chrdev_region(dev_t from, unsigned count)


3. file_operations 结构体

    file_operations 结构体中的成员函数是字符设备驱动程序设计的主体内容,这些函数实际会在应用程序进行 Linux 的 open()、write()、read()、close() 等系统调用时最终被内核调用。

    

猜你喜欢

转载自blog.csdn.net/linuxweiyh/article/details/79379590