天天看点

96 如何创建一个设备文件

文章目录

    • 一、mknod引入
    • 二、mknod 工作流程
    • 三、要点

一、mknod引入

用于创建指定类型的特殊文件。

一般流程是创建一个设备文件,然后根据 mknod 指令中的设备号,将

struct inode

cdev_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 工作流程

96 如何创建一个设备文件

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 是字符设备通用的文件操作接口,详见下一个代码块。
		 */
		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);
		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的绑定 */
	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;
}
           

三、要点

struct inode

中的 成员变量

file_operation

一开始并不是自己构造的

file_operation

,而是字符设备通用的

def_chr_fops

在应用程序对此文件进行 open 操作后,会执行

def_chr_fops->open

,在此函数中完成对

struct inode

设备号对应cdev_map->probes 哈希表中的 cdev->file_operation

进行配对。