您的位置:首页 > 运维架构 > Linux

linux内核read操作源代码分析

2016-03-06 09:08 627 查看
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也一定是可以搞明白的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息