linux内核read操作源代码分析

来源:互联网 发布:淘宝店铺地址格式 编辑:程序博客网 时间:2024/05/17 01:02
read操作是任何操作系统里的基本操作,我们来看一下在linux内核里,read文件是怎样实现的。
read函数在用户空间是由read系统调用实现的,由编译器编译成软中断int 0x80来进入内核空间,然后在中端门上进入函数sys_read,从而进入内核空间执行read操作。
sys_read函数定义在fs/read_write.c文件,定义如下
asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count){struct file *file;/*文件指针*/ssize_t ret = -EBADF;int fput_needed;/*轻量级的由文件描述符得到文件指针函数*/file = fget_light(fd, &fput_needed);if (file) {/*file结构体里的指示文件读写位置的int变量读取*/loff_t pos = file_pos_read(file);/*vfs虚拟文件系统实现read操作的地方*/ret = vfs_read(file, buf, count, &pos);/*file结构体里的指示文件读写位置的int变量写入*/file_pos_write(file, pos);/*释放file结构体指针*/fput_light(file, fput_needed);}return ret;}

首先看看file_pos_read和file_pos_write函数吧,定义如下
static inline loff_t file_pos_read(struct file *file){return file->f_pos;}static inline void file_pos_write(struct file *file, loff_t pos){file->f_pos = pos;}

定义很简单,读取的时候就是读出file结构体的f_pos,写入的时候就是写到对应变量。指示文件的读写位置的变量就是在file结构体里。
然后看一下fget_light和fput_light函数,定义如下
struct file fastcall *fget_light(unsigned int fd, int *fput_needed){struct file *file;/*得到当前进程的task_struct的打开的files指针*/struct files_struct *files = current->files;*fput_needed = 0;/*如果只有一个进程使用这个结构体,就不必考虑锁,否则要先得到锁才可以读取*/if (likely((atomic_read(&files->count) == 1))) {/*从files结构体的fd数组上得到file结构体*/file = fcheck_files(files, fd);} else {/*先上锁,在得到对应结构体*/rcu_read_lock();file = fcheck_files(files, fd);if (file) {if (atomic_inc_not_zero(&file->f_count))*fput_needed = 1;else/* Didn't get the reference, someone's freed */file = NULL;}rcu_read_unlock();}return file;}static inline void fput_light(struct file *file, int fput_needed){/*释放并减少使用计数*/if (unlikely(fput_needed))fput(file);}

然后返回来看我们最重要的vfs_read函数,vfs_read函数定义在fs/read_write.c,定义如下
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos){ssize_t ret;/*首先检查文件是否可以读取,否则返回坏的文件描述符标记*/if (!(file->f_mode & FMODE_READ))return -EBADF;/*如果没有对应的文件操作函数集合,也返回错误*/if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))return -EINVAL;/*检查有没有权限*/if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))return -EFAULT;/*检查当前写入的地方有没有被上锁,是否可读写*/ret = rw_verify_area(READ, file, pos, count);if (ret >= 0) {count = ret;/*安全操作*/ret = security_file_permission (file, MAY_READ);if (!ret) {/*如果file结构体里有read函数,就调用*/if (file->f_op->read)ret = file->f_op->read(file, buf, count, pos);else/*否则就调用异步读取的*/ret = do_sync_read(file, buf, count, pos);if (ret > 0) {/*成功读取以后,通知父目录已经读取,并在当前进程结构体上记录*/fsnotify_access(file->f_path.dentry);add_rchar(current, ret);}inc_syscr(current);}}return ret;}

然后我们在进入do_sync_read函数看一看异步读取是怎么实现的,do_sync_read函数定义在fs/read_write.c,定义如下
ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos){struct iovec iov = { .iov_base = buf, .iov_len = len };struct kiocb kiocb;ssize_t ret;/*初始化读写控制块*/init_sync_kiocb(&kiocb, filp);kiocb.ki_pos = *ppos;kiocb.ki_left = len;/*调用file_operation结构体的异步读取函数*/for (;;) {ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos);if (ret != -EIOCBRETRY)break;wait_on_retry_sync_kiocb(&kiocb);}/*如果没结束,就等待*/if (-EIOCBQUEUED == ret)ret = wait_on_sync_kiocb(&kiocb);*ppos = kiocb.ki_pos;return ret;}

至此,linux内核的read操作就算ok了,linux内核的sys_write和read很相似哦,只要弄明白read,write也一定是可以搞明白的。
0 0
原创粉丝点击