Linux驱动:应用程序open如何调用到驱动程序的open函数

字符设备文件的打开流程

相关结构体

流程涉及相关结构体如下:

struct inode {
	……
	dev_t			i_rdev;
	const struct file_operations	*i_fop;	/* former ->i_op->default_file_ops */
	
	union {
		struct pipe_inode_info	*i_pipe;
		struct block_device	*i_bdev;
		struct cdev		*i_cdev;
		char			*i_link;
		unsigned		i_dir_seq;
	};
	……
}
	mknod /dev/hillo c 255 0	//创建设备节点 生成 inode 结构体来描述文件的静态属性
	
struct cdev {
	struct kobject kobj;
	struct module *owner;
	const struct file_operations *ops;
	struct list_head list;
	dev_t dev;
	unsigned int count;
} __randomize_layout;

// 每个文件都对应一个 struct file 结构体,打开一个文件,系统都会将 struct file 添加到 struct fdtable 的数组中
struct fdtable {
	unsigned int max_fds;
	struct file __rcu **fd;      /* current fd array */
	fd_set *close_on_exec;
	fd_set *open_fds;
	struct rcu_head rcu;
	struct fdtable *next;
};
// 返回的文件描述符就是此文件的 struct file 在数组中的下标,从3开始(stdin-0 stdout-1 stderr-2)
struct file {
	struct path		f_path;		// 文件路径
	const struct file_operations	*f_op;  // 文件操作对象
	unsigned int 		f_flags;	//文件标志
	fmode_t			f_mode;		// 文件权限
	loff_t			f_pos;		// 文件当前偏移量
	void			*private_data;	// 私有数据
	...			...
}
open函数调用过程
fd = open("/dev/hello",O_RDWR);		//应用程序调用 open函数	用户空间
	|
	---> sys_open(const char *,int ,int,);	//系统调用		内核空间
		|
		---> static int chardev_open(struct inode *inode,struct file *filp);	//VFS层
			|
			---> kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
			---> new = container_of(kobj, struct cdev, kobj);	//通过在cdev_map中查找到的cdev的成员变量 struct kobject 
																//结构体 地址,转化成字符设备 cdev 结构体的地址。
			---> inode->i_cdev = p = new;
			---> fops = fops_get(p->ops);		// 通过fops_get得到p指向的file_operations对象ops
			---> replace_fops(filp, fops);		// 通过replace_fops用它替换掉file中的f_op,后续就是通过filp来操作文件
			---> ret = filp->f_op->open(inode, filp);	//调用驱动中的open函数

linux内核源码

static int chrdev_open(struct inode *inode, struct file *filp)
{
	const struct file_operations *fops;
	struct cdev *p;
	struct cdev *new = NULL;
	int ret = 0;

	spin_lock(&cdev_lock);
	p = inode->i_cdev;
	if (!p) {
		struct kobject *kobj;
		int idx;
		spin_unlock(&cdev_lock);
		kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
		if (!kobj)
			return -ENXIO;
		new = container_of(kobj, struct cdev, kobj);
		spin_lock(&cdev_lock);
		/* Check i_cdev again in case somebody beat us to it while
		   we dropped the lock. */
		p = inode->i_cdev;
		if (!p) {
			inode->i_cdev = p = new;
			list_add(&inode->i_devices, &p->list);
			new = NULL;
		} else if (!cdev_get(p))
			ret = -ENXIO;
	} else if (!cdev_get(p))
		ret = -ENXIO;
	spin_unlock(&cdev_lock);
	cdev_put(new);
	if (ret)
		return ret;

	ret = -ENXIO;
	fops = fops_get(p->ops);
	if (!fops)
		goto out_cdev_put;

	replace_fops(filp, fops);
	if (filp->f_op->open) {
		ret = filp->f_op->open(inode, filp);
		if (ret)
			goto out_cdev_put;
	}

	return 0;

 out_cdev_put:
	cdev_put(p);
	return ret;
}

流程总结:
通过 inode 结构体中的 i_rdev 搜索对应的字符设备 i_cdev — 对应 struct cdev 结构体
查找 struct cdev 结构体的过程,是通过一个全局变量 cdev_map 来完成的。cdev_map 的成员变量 probes 是一个数组,在驱动的 init 函数中,将驱动的文件操作函数集(和其他一些数据结构)以设备号为索引塞进这个数组里面,而在(第一次)调用chrdev_open()函数的时候,再以文件号为索引,从这个数组里面找到我们需要的文件操作集(以及其他相关的数据结构)。

struct file 结构体 — 表示一个已经打开的文件,当文件关闭时,该结构体被释放
struct cdev 结构体 — 表示一个字符设备,对应一个字符设备驱动
struct inode 结构体 — 表示一个 /dev/目录下的一个字符设备驱动节点

应用程序执行open函数,将一个inode结构体(/dev/xxx)通过系统调用传入给内核,调用内核中的 chardev_open函数,在该函数中通过inode结构体中的成员变量(inode->i_rdev 包含设备号),在 cdev_map 中查找设备号所对应的字符设备驱动程序cdev 结构体的成员变量kobject,利用该成员变量找到对应的 cdev 结构体,此时,完成由设备驱动节点(inode)找到对应设备驱动(cdev)的步骤。之后inode结构体和cdev结构体完成双向链表的建立,并将cdev结构体中的file_opertion结构体赋给file结构体中的程序变量(filp->f_op)。至此,完成了从 inode结构体 --> cedv结构体 --> file结构体 的建立,之后,对设备进行的read, write等操作,就会执行cdev的相应操作。

参考路径:https://blog.csdn.net/o0o0o0D/article/details/52993674

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值