上一篇介博客绍了文件写过程write(),接下来看一下读过程read()。在写过程中,根据打开文件的文件描述符,获取文件对象指针file*,然后调用操作表中的write()函数或write_iter()函数,完成写操作file->ops->write()。读过程与写过程类似,最终调用的是read()或read_iter()函数。
一、系统调用入口
read()函数属于系统调用,入口在read_write.c中,基本代码逻辑如下:
- 根据整型fd文件描述符获取struct fd对象(见上一篇write()过程分析)
- 调用vfs_read()完成文件读取
- 返回读取字节数
// read_write.c
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
return ksys_read(fd, buf, count);
}
ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count)
{
struct fd f = fdget_pos(fd);
ssize_t ret = -EBADF;
if (f.file) {
loff_t pos, *ppos = file_ppos(f.file);
if (ppos) {
pos = *ppos;
ppos = &pos;
}
ret = vfs_read(f.file, buf, count, ppos);
if (ret >= 0 && ppos)
f.file->f_pos = pos;
fdput_pos(f);
}
return ret;
}
二、执行读取操作
前面已经获取了文件对象file*,在读文件时,直接调用file操作表中的操作,最终完成内容的写入,主要步骤如下:
- 参数校验
- 如果f_op->read存在,则调用此函数进行读取,否则,如果file->f_op->read_iter存在,则调用new_sync_read()函数进行读取
- 调用fsnotify_access()通知父目录文件访问
- 调用add_rchar()将读取的数据量,更新到进程的struct task_io_accounting的rchar字段
- 调用inc_syscr()将写入系统调用次数,更新到进程的struct task_io_accounting的syscr字段
- 返回读取的字节数
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_mode & FMODE_CAN_READ))
return -EINVAL;
if (unlikely(!access_ok(buf, count)))
return -EFAULT;
ret = rw_verify_area(READ, file, pos, count);
if (ret)
return ret;
if (count > MAX_RW_COUNT)
count = MAX_RW_COUNT;
if (file->f_op->read)
ret = file->f_op->read(file, buf, count, pos);
else if (file->f_op->read_iter)
ret = new_sync_read(file, buf, count, pos);
else
ret = -EINVAL;
if (ret > 0) {
fsnotify_access(file);
add_rchar(current, ret);
}
inc_syscr(current);
return ret;
}
static ssize_t new_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;
struct iov_iter iter;
ssize_t ret;
init_sync_kiocb(&kiocb, filp);
kiocb.ki_pos = (ppos ? *ppos : 0);
iov_iter_init(&iter, READ, &iov, 1, len);
ret = call_read_iter(filp, &kiocb, &iter);
BUG_ON(ret == -EIOCBQUEUED);
if (ppos)
*ppos = kiocb.ki_pos;
return ret;
}
static inline ssize_t call_read_iter(struct file *file, struct kiocb *kio,
struct iov_iter *iter)
{
return file->f_op->read_iter(kio, iter);
}
参考资料:官方文档