概述:
linux下一切皆文件,设备文件也不例外,作为文件系统中代表设备的特殊文件,和普通文件相比不需要存放数据的记录块与之联系,原因在于设备文件的目的不在于存储和读取数据而只在于为应用程序提供一条通向具体设备的访问通道,是应用程序可以和具体的设备建立连接.
基于此,我们查看内核系统调用针对两者的区别:
asmlinkage long sys_open(const char * filename, int flags, int mode)
asmlinkage long sys_creat(const char * pathname, int mode)
asmlinkage long sys_mknod(const char * filename, int mode, dev_t dev)
普通文件的创建方式:sys_open(O_CREAT=1),create,sys__creat实则也是调用sys_open来实现
设备文件:sys_mknod多出了设备号参数,这也是区分设备必要的
总结:sys_mknod是通用的,可以创建除目录外的任何文件,sys_open/sys_creat用来创建普通文件,sys_pipe用来创建FIFO文件,通常来讲sys_mknod主要用于创建设备文件,只是纯粹创建而非像sys_open/sys_creat集创建打开于一体
sys_mknod(const char * filename, int mode, dev_t dev)原型
asmlinkage long sys_mknod(const char * filename, int mode, dev_t dev)
{
int error = 0;
char * tmp;
struct dentry * dentry;
struct nameidata nd;
if (S_ISDIR(mode))
return -EPERM;
tmp = getname(filename);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
if (path_init(tmp, LOOKUP_PARENT, &nd))
error = path_walk(tmp, &nd);
if (error)
goto out;
dentry = lookup_create(&nd, 0);
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
switch (mode & S_IFMT) {
case 0: case S_IFREG:
error = vfs_create(nd.dentry->d_inode,dentry,mode);//普通文件创建
break;
case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK:
error = vfs_mknod(nd.dentry->d_inode,dentry,mode,dev);//字符设备,块设备,fifo,socket设备文件创建
break;
case S_IFDIR:
error = -EPERM;
break;
default:
error = -EINVAL;
}
dput(dentry);
}
up(&nd.dentry->d_inode->i_sem);
path_release(&nd);
out:
putname(tmp);
return error;
}
int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
int error = -EPERM;
mode &= ~current->fs->umask;
down(&dir->i_zombie);
if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
goto exit_lock;
error = may_create(dir, dentry);
if (error)
goto exit_lock;
error = -EPERM;
if (!dir->i_op || !dir->i_op->mknod)
goto exit_lock;
DQUOT_INIT(dir);
lock_kernel();
error = dir->i_op->mknod(dir, dentry, mode, dev);//只想实际的文件系统
unlock_kernel();
exit_lock:
up(&dir->i_zombie);
if (!error)
inode_dir_notify(dir, DN_CREATE);
return error;
}
//实际文件系统接口
static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
{
struct inode * inode = ext2_new_inode (dir, mode);
int err = PTR_ERR(inode);
if (IS_ERR(inode))
return err;
inode->i_uid = current->fsuid;
init_special_inode(inode, mode, rdev);//针对不同设备绑定对应fop
err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len,
inode);
if (err)
goto out_no_entry;
mark_inode_dirty(inode);
d_instantiate(dentry, inode);
return 0;
out_no_entry:
inode->i_nlink--;
mark_inode_dirty(inode);
iput(inode);
return err;
}
void init_special_inode(struct inode *inode, umode_t mode, int rdev)
{
inode->i_mode = mode;
if (S_ISCHR(mode)) {
inode->i_fop = &def_chr_fops;
inode->i_rdev = to_kdev_t(rdev);
} else if (S_ISBLK(mode)) {
inode->i_fop = &def_blk_fops;
inode->i_rdev = to_kdev_t(rdev);
inode->i_bdev = bdget(rdev);
} else if (S_ISFIFO(mode))
inode->i_fop = &def_fifo_fops;
else if (S_ISSOCK(mode))
inode->i_fop = &bad_sock_fops;
else
printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", mode);
}
以字符设备hello.ko为例:
filp->f_op->open实际指向了hello_mod_open
int chrdev_open(struct inode * inode, struct file * filp)
{
int ret = -ENODEV;
filp->f_op = get_chrfops(MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
if (filp->f_op) {
ret = 0;
if (filp->f_op->open != NULL) {
lock_kernel();
ret = filp->f_op->open(inode,filp);
unlock_kernel();
}
}
return ret;
}