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设备