linux内核read操作源代码分析

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也一定是可以搞明白的。

你可能感兴趣的:(c,linux,linux,kernel,内核,文件系统)