96 如何创建一个设备文件

一、mknod引入

用于创建指定类型的特殊文件。
一般流程是创建一个设备文件,然后根据 mknod 指令中的设备号,将 struct inodecdev_map->probes->cdev->file_operations 绑定起来。

查看 mknod 指令使用帮助:mknod --help

用法:mknod [选项]... 文件名称 类型 [主设备号 次设备号]
Create the special file NAME of the given TYPE.
...
当类型为"p"时可不指定主设备号和次设备号,否则它们是必须指定的。
如果主设备号和次设备号以"0x""0X"开头,它们会被视作十六进制数来解析;
如果以"0"开头,则被视作八进制数;其余情况下被视作十进制数。
可用的类型包括:

  b      创建(有缓冲的)区块特殊文件
  c, u   创建(没有缓冲的)字符特殊文件
  p      创建先进先出(FIFO)特殊文件

如:

mkmod /dev/test c 2 0

可以自己编写一个设备驱动模块,加载后使用 cat /proc/devices 查看主设备号。
再使用 mknod 创建一个文件,要求主设备号相同,次设备号相同或者在范围内。 file_operation 复制成功。

二、mknod 工作流程

在这里插入图片描述

ext4_mknod:新创建一个 inode 节点。

init_special_inode函数分析

定义在: /fs/inode.c

函数主要操作:判断文件类型,如果是 字符设备类型 ,则把 def_chr_fops 作为该文件的操作接口,并把设备号记录在 inode->i_rdev

/* inode:新创建的 inode 节点。 
 * mode:文件的读写权限。
 * rdev:设备文件关联的主次设备号,是在shell调用mknod命令传参进来的。
 */
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
	inode->i_mode = mode;
	if (S_ISCHR(mode)) { /* 判断文件是否属于字符设备,可由 mknod 指定 */
		/* 设置文件的操作接口。
		 * def_chr_fops 是字符设备通用的文件操作接口,详见下一个代码块。
		 * struct file_operations 类型
		 */
		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);
}
EXPORT_SYMBOL(init_special_inode);

结构体 def_chr_fops 定义在 fs/chr_dev.c

*
 * Dummy default file-operations: the only thing this does
 * is contain the open that then fills in the correct operations
 * depending on the special file...
 */
const struct file_operations def_chr_fops = {
	/* 在此函数绑定自定义的file_operations到文件中 */
	.open = chrdev_open,
	.llseek = noop_llseek,
};

函数 chrdev_open 定义在 fs/chr_dev.c

/*
 * Called every time a character special file is opened
 */
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_map中寻找对应的cdev
		// cdev_map 中的链表都是一个节点代表一段次设备号,函数里判断这个设备对应的设备号是否在某一节点所表示的范围中。
		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;
	// 把fops赋值给file对应的成员
	replace_fops(filp, fops);
	// 默认的open函数里面,会调用另一个open
	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;
}

三、要点

struct inode 中的 成员变量 file_operation 一开始并不是自己构造的 file_operation ,而是字符设备通用的 def_chr_fops
在应用程序对此文件进行 open 操作后,会执行 def_chr_fops->open,在此函数中完成对 struct inode设备号对应cdev_map->probes 哈希表中的 cdev->file_operation 进行配对。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值