4. 关于系统调用分析
4.1 创建字符设备文件节点--mknode
在使用字符设备之前通常要创建字符设备文件节点--例如:mknod /dev/char_key c 255 0
-->在/dev/目录下创建char_key字符设备文件,主设备号255,次设备号0
mknod-->sys_mknodat(AT_FDCWD, filename, mode, dev)
//namei.c
-->SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, int, mode, unsigned, dev)
{
int error;
char *tmp;
struct dentry *dentry;
struct nameidata nd;
if (S_ISDIR(mode))
return -EPERM;
error = user_path_parent(dfd, filename, &nd, &tmp); //取得当前进程的文件系统,和节点
if (error)
return error;
switch (mode & S_IFMT) {
case 0: case S_IFREG:
error = vfs_create(nd.path.dentry->d_inode,dentry,mode,&nd);
/* 通过相应的文件系调用相应地mknod创建节点
比如ext2的文件系统ext2_mknod; 赋值操作i_op
例如在init_special_inode ---->字符设备节点i_fop = &def_chr_fops;--------->(重要的***)
----->块设备节点i_fop = &def_blk_fops
----->fifo节点i_fop = &def_fifo_fops;
----->sock节点i_fop = &bad_sock_fops
case default:
init_special_inode(inode, mode, dev);
break;
case S_IFREG:
inode->i_fop = &default_file_ops;
break;
case S_IFDIR:
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
break;
*/
case S_IFCHR: case S_IFBLK:
error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,
new_decode_dev(dev));
break;
case S_IFIFO: case S_IFSOCK:
error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,0);
break;
}
总结:对于字符设备,当调用mknod后:--->字符设备节点i_fop = &def_chr_fops;
4.1 打开字符设备--open
--int fd = open("dev/char_key", O_RDWR);
--sys_open
-->SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
---->long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
----->struct file *do_filp_open(int dfd, const char *pathname,int open_flag, int mode, int acc_mode)
------>static struct file *finish_open(struct nameidata *nd, int open_flag, int acc_mode)
------->struct file *nameidata_to_filp(struct nameidata *nd)
--------->__dentry_open
{
/*当打开文件时,从将节点中的ops(def_chr_fops)给新申请的file的f_op*/
f->f_op = fops_get(inode->i_fop);
open = f->f_op->open; /*字符设备会调用def_chr_fops->chrdev_open*/
}
/**************接下来分析chrdev_open***************/
static int chrdev_open(struct inode *inode, struct file *filp)
{
struct cdev *p;
struct cdev *new = NULL;
int ret = 0;
spin_lock(&cdev_lock);
p = inode->i_cdev;
if (!p) {
kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx); //通过设备号查找kobj
new = container_of(kobj, struct cdev, kobj); //通过kobj查找到cdev
p = inode->i_cdev; //先看是否设备已经存在
if (!p) {
inode->i_cdev = p = new; //将cdev --> inode->i_cdev
list_add(&inode->i_devices, &p->list);
new = NULL;
}
}
filp->f_op = fops_get(p->ops); //将设备的fops赋值给file->f_op
if (filp->f_op->open) {
ret = filp->f_op->open(inode,filp); //调用设备的fops中的open函数
}
}
总结:最后调用到设备中fops中的open函数
4.2 往字符设备读写数据--read/write
--read(fd, rbuf, sizeof(rbuf));
--->sys_write-->SYSCALL_DEFINE3(read,...)-->vfs_read
---->file->f_op->read(file, buf, count, pos); /*调用字符设备中的read函数*/
--write(fd, buf, sizeof(buf));
-->sys_write-->SYSCALL_DEFINE3(write,...)-->vfs_write
---->file->f_op->write(file, buf, count, pos); /*调用字符设备中的write函数*/
其他:进程为打开的文件维护一个文件表,通过文件描述符fd做索引
/* open file information
struct files_struct *files;*/