天天看點

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

繼續閱讀