013 Linux一次文件读过程read()

上一篇介博客绍了文件写过程write(),接下来看一下读过程read()。在写过程中,根据打开文件的文件描述符,获取文件对象指针file*,然后调用操作表中的write()函数或write_iter()函数,完成写操作file->ops->write()。读过程与写过程类似,最终调用的是read()或read_iter()函数。

读文件read()
读文件read()

一、系统调用入口

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);
}

上一篇:Linux一次文件打开过程open()

参考资料:官方文档

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注