linux驱动之open

linux驱动之open

open()系统调用->sys_open()

SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
	...
	return do_sys_open(AT_FDCWD, filename, flags, mode);
}
long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
	struct open_flags op;
	//设置一些标志
	int fd = build_open_flags(flags, mode, &op);
	struct filename *tmp;

	//根据用户空间的filename,设置文件名
	//用户空间的指针,在内核不能直接使用
	//参考copy_to_user()/copy_form_user()
	tmp = getname(filename);
	if (IS_ERR(tmp))
		return PTR_ERR(tmp);

	//获取没有使用的fd,用户进程每打开一个文件,就会占用一个fd,
	fd = get_unused_fd_flags(flags);
	struct file *f = do_filp_open(dfd, tmp, &op);
	...
	//关联fd和file结构体,这样就可以通过fd调用file->fops
	fd_install(fd, f);
	//释放内存
	putname(tmp);
	return fd;
}

do_filp_open()函数调用get_empty_filp()获取一个file结构体,最终调用
do_dentry_open()

static int do_dentry_open(struct file *f,
			  struct inode *inode,
			  int (*open)(struct inode *, struct file *),
			  const struct cred *cred)
{
	...
	//file->f_op赋值为inode->i_fop
	//inode->i_fop == def_chr_fops,这个是通用字符设备的fops
	f->f_op = fops_get(inode->i_fop);
	...
	if (!open)
		open = f->f_op->open;
	if (open) {
		error = open(inode, f);
	}
	...
}
const struct file_operations def_chr_fops = {
	.open = chrdev_open,
	.llseek = noop_llseek,
};
//每次字符设备打开时,都会调用
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);
		//根据设备号查找cdev->kobj
		//cdev保存在cdev_map中
		kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);

		//container_of是一个宏,通过结构体成员的指针获取结构体本身的指针
		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记录cdev
			inode->i_cdev = p = new;
			//inode挂在cdev链表上
			list_add(&inode->i_devices, &p->list);
			new = NULL;
		}
	}
	...
	// == cdev->fops
	fops = fops_get(p->ops);

	//替换file->fops为cdev->fops
	replace_fops(filp, fops);
	if (filp->f_op->open) {
		//调用设备的fops
		ret = filp->f_op->open(inode, filp);
	}
	...
}

查找cdev->kobj,cdev_map保存有cdev和对应设备号(在字符设备注册函数
cdev_add()中保存的),kobj_lookup()就是去cdev_map->probes[]这个哈希表中查找

struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)
{
	struct kobject *kobj;
	struct probe *p;
	unsigned long best = ~0UL;

retry:
	mutex_lock(domain->lock);
	//根据设备号查找cdev
	for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) {
		struct kobject *(*probe)(dev_t, int *, void *);
		struct module *owner;
		void *data;

		if (p->dev > dev || p->dev + p->range - 1 < dev)
			continue;
		if (p->range - 1 >= best)
			break;
		...
		//data就是cdev
		data = p->data;
		probe = p->get;
		best = p->range - 1;
		*index = dev - p->dev;
		...
		mutex_unlock(domain->lock);
		//获取kobj, probe == exact_match
		kobj = probe(dev, index, data);
		...
	}
	mutex_unlock(domain->lock);
	return NULL;
}
static struct kobject *exact_match(dev_t dev, int *part, void *data)
{
	struct cdev *p = data;
	return &p->kobj;
}

创建特殊文件时,会调用init_special_inode函数设置inode->i_fop和
inode->i_rdev,也就是通用操作接口和设备号

void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
	inode->i_mode = mode;
	//如果是字符设备文件,操作接口就是def_chr_fops
	//在调用open()时会调用
	if (S_ISCHR(mode)) {
		inode->i_fop = &def_chr_fops;
		inode->i_rdev = rdev;
	} else if (S_ISBLK(mode)) {
		inode->i_fop = &def_blk_fops;
		inode->i_rdev = rdev;
	} else if (S_ISFIFO(mode))
		inode->i_fop = &pipefifo_fops;
	else if (S_ISSOCK(mode))
		;	/* leave it no_open_fops */
	else
		printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"
				  " inode %s:%lu\n", mode, inode->i_sb->s_id,
				  inode->i_ino);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值