字符设备 和 input 设备--文件如何变成设备

本文介绍了Linux操作系统中设备的分类及如何将普通文件转换为字符设备或块设备的过程。重点讲解了init_special_inode函数的作用,以及不同类型的设备文件操作结构如def_chr_fops和def_blk_fops的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  Linux操作系统把设备划分为字符设备和块设备【网络设备是单独一种类型】。

  很多情况下把字符设备 当 系统控制的一种手段,通过字符设备的I/O control 函数与内涵 进行交换数据。 实际上,Linux内核系统很多时候是把字符设备当做一个框架来使用。


5.1 文件如何变成设备

  回顾aufs系统,通过aufs_get_inode为每个文件创建它的inode对象。对文件和目录都有各自的文件操作函数 和 inode 操作函数。默认情况下,我们使用init_special_inode 函数给对象赋值。

5.1.1 init_special_inode函数

  通过init_special_inode函数使文件变成设备,字符设备 和 块设备 开始浮出水面,函数代码如下

void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev )

{

  inode->i_mode = mode ;

  if (S_ISCHR(mode)) {

    inode->i_fop = &def_chr_fops;

    inode->i_rev = rdev;

   } else if ( S_ISBLK(mode)) {

    inode->i_fop = & def_blk_fops;

    inode->i_rdev = rdev;

  }else if  ( IS_ISFIFO(mode) )

    inode->i_fop = &def_fifo_fops;

  else if ( S_ISSOCK(mode))

    inode->i_fop = &bad_sock_fops;

  esle

    printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o ) \n", mode );

}

  这段代码很简单,

如果字符设备,文件操作符指针赋值为def_chr_fops; 如果是块设备,赋值为def_blk_fops。同时inode的i_rdev被赋值为 rdev ,rdev是由主设备号 和次设备号 生成的设备号

  通过init_special_inode函数,inode的文件函数指针被 i_fop 替换,从此inode不在是普通的文件inode, 而是分别代表 字符设备、块设备、 fifo 和 socket的特殊inode。

def_chr_fops 定义如下,在fs/Char_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 = {
	.open = chrdev_open,
	.llseek = noop_llseek,
};

def_blk_fops定义如下,在fs/Block_dev.c 

const struct file_operations def_blk_fops = {
	.open		= blkdev_open,
	.release	= blkdev_close,
	.llseek		= block_llseek,
	.read		= do_sync_read,
	.write		= do_sync_write,
  	.aio_read	= generic_file_aio_read,
	.aio_write	= blkdev_aio_write,
	.mmap		= generic_file_mmap,
	.fsync		= blkdev_fsync,
	.unlocked_ioctl	= block_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= compat_blkdev_ioctl,
#endif
	.splice_read	= generic_file_splice_read,
	.splice_write	= generic_file_splice_write,
};

def_fifo_fops定义如下,在fs/Fifo.c

/*
 * Dummy default file-operations: the only thing this does
 * is contain the open that then fills in the correct operations
 * depending on the access mode of the file...
 */
const struct file_operations def_fifo_fops = {
	.open		= fifo_open,	/* will set read_ or write_pipefifo_fops */
	.llseek		= noop_llseek,
};

bad_sock_fops定义如下,在net/Socket.c

const struct file_operations bad_sock_fops = {
	.owner = THIS_MODULE,
	.open = sock_no_open,
	.llseek = noop_llseek,
};



5.1.2 def_chr_fops结构

  def_chr_fops结构如下,代码清单:

const struct file_oprations def_chr_fops = {

  .open = chrdev_open,

};

/*  Called every time a character special file is opened */

每次called------字符特殊文件打开

int chrdev_open( struct inode * inode , struct file * filp)

{

  struct cdev *p;

  struct cdev *next = NULL;

  int ret = 0;


  spin_lock(&cdev_lock);

  p = inode->i_cdev;


  /* 如果字符设不存在 */

  if ( !p ) {

    sgruct kobject * kobj;

    int idx;

    /* 通过kobj_lookup 查找字符设备的kobj结构 */

    kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);

    if ( !kobj )    

      return -ENXIO;

    /* 调用container方法, 获得 cdev对象 */

    new = container_of(kobj, struct cdev, kobj);

    spin_lock(&cdev_lock);

    /*  再次检查 p  */

    p = inode->i_cdev;

    if ( !p ) {

      /* 赋值,inode的字符设备指针指向发现的字符设备  */

      inode->i_cdev = p = new ;

      inode->i_cindex = idx;

      list_add(&inode->i_devices, &p->list);

      new = NULL;

    }else if (!cdev_get(p))

      ret = -ENXIO;

    spin_unlock(&cdev_lock);

    cdev_put( new );

    if (ret)

      return ret;

    /* 获得设备的函数指针,对input设备来说,就是input_fops  */

      flip->f_op = fops_get( p->ops );

      if ( !filp->f_op ) {

        cdev_put(p);

        return -ENXIO;

      }

    /* 这个open函数就是 input 设备的input_open_file */

      if (filp->f_op->open ) {

        /* 大内核锁 */

        lock_kernel();

        ret = filp->f_op->open(inode, filp);

        unlock_kernel();

      }

      if ( ret )

        cdev_put( p );

      return ret;

  }

}

  chrdev_open 函数,每次打开字符设备的时候,都调用这个函数。首先根据设备号调用kobj_lookup搜索注册的字符设备对象,如果找到字符设备,执行字符设备的open函数,找不到则返回错误。

  Linux系统提供 mknod 程序,使用这个程序用户可以根据主从设备创建特殊文件,比如字符设备文件 或者 块设备文件。 从内核角度分析,mknod为特殊文件创建了一个inode结构和 dentry 结构,inode结构的成员包含主从设备 和设备类型,然后调用init_special_inode函数为设备文件的inode设置不同的函数指针。打开设备文件时,通过chrdev_open函数真正调用设备驱动本身的open函数,前提是已经注册了设备驱动函数。

kobj_lookup 函数如下:

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);

        //  从链表头开始查找
	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;
		if (!try_module_get(p->owner))
			continue;
		owner = p->owner;
		data = p->data;
		probe = p->get;
		best = p->range - 1;
		*index = dev - p->dev;
		if (p->lock && p->lock(dev, data) < 0) {
			module_put(owner);
			continue;
		}
		mutex_unlock(domain->lock);
		kobj = probe(dev, index, data);
		/* Currently ->owner protects _only_ ->probe() itself. */
		module_put(owner);
		if (kobj)
			return kobj;
		goto retry;
	}
	mutex_unlock(domain->lock);
	return NULL;
}

ENXIO定义:
#define	EPERM		 1	/* Operation not permitted */
#define	ENOENT		 2	/* No such file or directory */
#define	ESRCH		 3	/* No such process */
#define	EINTR		 4	/* Interrupted system call */
#define	EIO		 5	/* I/O error */
#define	ENXIO		 6	/* No such device or address */
#define	E2BIG		 7	/* Argument list too long */
#define	ENOEXEC		 8	/* Exec format error */
#define	EBADF		 9	/* Bad file number */
#define	ECHILD		10	/* No child processes */
#define	EAGAIN		11	/* Try again */
#define	ENOMEM		12	/* Out of memory */
#define	EACCES		13	/* Permission denied */
#define	EFAULT		14	/* Bad address */
#define	ENOTBLK		15	/* Block device required */
#define	EBUSY		16	/* Device or resource busy */
#define	EEXIST		17	/* File exists */
#define	EXDEV		18	/* Cross-device link */
#define	ENODEV		19	/* No such device */
#define	ENOTDIR		20	/* Not a directory */
#define	EISDIR		21	/* Is a directory */
#define	EINVAL		22	/* Invalid argument */
#define	ENFILE		23	/* File table overflow */
#define	EMFILE		24	/* Too many open files */
#define	ENOTTY		25	/* Not a typewriter */
#define	ETXTBSY		26	/* Text file busy */
#define	EFBIG		27	/* File too large */
#define	ENOSPC		28	/* No space left on device */
#define	ESPIPE		29	/* Illegal seek */
#define	EROFS		30	/* Read-only file system */
#define	EMLINK		31	/* Too many links */
#define	EPIPE		32	/* Broken pipe */
#define	EDOM		33	/* Math argument out of domain of func */
#define	ERANGE		34	/* Math result not representable */




5.3 input设备架构

5.3.1 注册input设备的驱动

5.3.2 匹配input管理的设备和驱动

5.3.3 注册input设备






















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值