【Linux】设备驱动——设备文件操作、表示、注册

在这里插入图片描述

🔥博客主页:PannLZ
🎋系列专栏:《Linux系统之路》


设备文件操作

可以在文件上执行的操作取决于管理文件的设备驱动程序。这样的操作在内核中定义为struct file_operations的实例。struct file_operations定义了一组回调函数,用于处理文件上的所有用户空间系统调用。

举个例子,如果想让用户在设备文件上执行write操作,必须在驱动中实现与write函数对应的回调函数,并把它添加到绑定在设备上的struct file_operations中。请看下面的文件操作结构:

struct file_operations {
		struct module *owner;
		loff_t (*llseek) (struct file *, loff_t,int);
		ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
		ssize_t (*write) (struct file *, const char__user *, size_t, loff_t*);
		unsigned int (*poll) (struct file *, structpoll_table_struct *);
		int (*mmap) (struct file *, structvm_area_struct *);
		int (*open) (struct inode *, struct file *);
		long (*unlocked_ioctl) (struct file *,unsigned int, unsigned long);
		int (*release) (struct inode *, struct file*);
		int (*fsync) (struct file *, loff_t, loff_t,int datasync);
		int (*fasync) (int, struct file *, int);
		int (*lock) (struct file *, int, structfile_lock *);
		int (*flock) (struct file *, int, structfile_lock *);
		[...]
};

这些方法的完整描述请参阅内核源码include/linux/fs.h

当用户代码在指定文件上调用与文件相关的系统调用时,内核会查找负责这个文件的驱动程序(尤其是创建该文件的驱动程序),定位它的struct file_operations结构,并检查和该系统调用匹配的方法是否已经定义。如果定义了,就运行它;如果未定义,则根据系统调用不同返回不同的错误码。

内核中的文件表示

内核把文件描述为**inode结构**(不是文件结构)的实例,inode结构在include/linux/fs.h中定义:

struct inode {
		[...]
		struct pipe_inode_info *i_pipe; /* 如果这是Linux内核管道,则设置并使用 */
		struct block_device *i_bdev; /* 如果这是块设备,则设置并使用 */
		struct cdev *i_cdev; /* 如果这是字符设备,则设置并使用 */
[...]
}

struct inode是文件系统的数据结构,它只与操作系统相关,用于保存文件(无论它的类型是字符、块、管道等)或目录(从内核的角度来看,目录也是文件,是其他文件的入口点)信息。

struct file结构(也在include/linux/fs.h中定义)是更高级的文件描述,它代表内核中打开的文件,依赖于低层的struct inode数据结构:

struct file {
		[...]
		struct path f_path; /* 文件路径 */
		struct inode *f_inode; /* 与此文件相关的inode */
		const struct file_operations *f_op; /* 可以在此文件上执行的操作 */
		loff_t f_pos; /* 此文件中光标的位置 */
		/* 需要tty驱动程序等 */
		void *private_data; /* 驱动程序可以设置的私有数据,以便在文件操作之间共享,这可以指向任何结构*/
		[...]
}

struct inodestruct file的区别在于,inode不跟踪文件的当前位置和当前模式,它只是帮助操作系统找到底层文件结构的内容(管道、目录、常规磁盘文件、块/字符设备文件等)。而struct file则是一个基本结构(它实际上持有一个指向struct inode的指针),它代表打开的文件,并且提供一组函数,它们与底层文件结构上执行的方法相关,这些方法包括open、write、seek、read、select等。所有这一切都强化了UNIX系统的哲学:一切皆是文件

换句话说,struct inode代表内核中的文件,struct file描述实际打开的文件。同一个文件打开多次时,可能会有不同的文件描述符,但它们都指向同一个inode。

分配和注册字符设备

字符设备在内核中表示为struct cdev的实例。在编写字符设备驱动程序时,目标是最终创建并注册与struct file_operations关联的结构实例,为用户空间提供一组可以在该设备上执行的操作(函数)。为了实现这个目标,必须执行以下几个步骤。

(1)使用alloc_chrdev_region()保留一个主设备号和一定范围的次设备号。
(2)使用class_create()创建自己的设备类,该函数在/sys/class中定义。
(3)创建一个struct file_operation(传递给cdev_init),每一个设备都需要创建,并调用call_init和cdev_add()注册这个设备。
(4)调用device_create()创建每个设备,并给它们一个合适的名字。这样,就可在/dev目录下创建出设备。

#define EEP_NBANK 8
#define EEP_DEVICE_NAME "eep-mem"
#define EEP_CLASS "eep-class"
struct class *eep_class;
struct cdev eep_cdev[EEP_NBANK];
dev_t dev_num;
static int __init my_init(void)
{
		int i;
		dev_t curr_dev;
		/* 为EEP_NBANK设备请求内核*/
		alloc_chrdev_region(&dev_num, 0, EEP_NBANK,EEP_DEVICE_NAME);
		/* 创建设备的类,在/sys/class中可见 */
		eep_class = class_create(THIS_MODULE,EEP_CLASS);
		/* 每个eeprom bank表示为一个字符设备(cdev)*/
		for (i = 0; i < EEP_NBANK; i++) {
				/* 将file_operations绑定到cdev */
				cdev_init(&my_cdev[i], &eep_fops);
				eep_cdev[i].owner = THIS_MODULE;
				/* 用于将cdev添加到核心的设备号 */
				curr_dev = MKDEV(MAJOR(dev_num),MINOR(dev_num) + i);
				/* 让用户访问设备 */
				cdev_add(&eep_cdev[i], curr_dev, 1);
/*
* 为每个设备的/dev/ ep-mem0、/dev/ ep-mem1
创建一个设备节点。由于这里使用了类,
* 因此还可以在/sys/class/ ep-class下查看设备
*/
				device_create(eep_class,
				NULL, /* 没有父设备 */
				curr_dev,
				NULL, /* 没有额外的数据*/
				EEP_DEVICE_NAME "%d", i);
				/* eep-mem[0-7] */
		}
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值