作者:gfree.wind@gmail.com
博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net
博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
在前面的博文中,学习了创建socket时,该socket与VFS的关联。今天看看在创建socket成功以后,应用层拿到的是一个socket文件描述符。而后应用层如何利用该socket文件描述符,通过VFS层进行write操作。
文件fs/read_write.c
进入fget_light
- SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
- size_t, count)
- {
- struct file *file;
- ssize_t ret = -EBADF;
- int fput_needed;
/*
利用描述符fd,从进程task的文件表得到kernel维护的file结构。
参数fput_needed表示在得到file后,是否需要进行fput。
*/
- file = fget_light(fd, &fput_needed);
- if (file) {
- //得到文件当前的偏移。对于socket来说,没有偏移的概念
- loff_t pos = file_pos_read(file);
- //进行写操作,并得到新的偏移量
- ret = vfs_write(file, buf, count, &pos);
- //更新file的偏移量
- file_pos_write(file, pos);
- //与fget_light对称的put操作,其中只有在fput_needed为true的情况下,才会真正执行put操作
- fput_light(file, fput_needed);
- }
- return ret;
- }
- struct file *fget_light(unsigned int fd, int *fput_needed)
- {
- struct file *file;
- struct files_struct *files = current->files;
- *fput_needed = 0;
- /*
- 当文件表的引用为1时,表明只有该task使用该文件表,那么可以直接从文件表中获得文件描述符对应的file结构 而不为1的情况下,说明为多个task共用了该文件表,那么在访问该文件表时需要使用RCU保护
- */
- if (atomic_read(&files->count) == 1) {
- /* 从文件描述符fd得到file结构 */
- file = fcheck_files(files, fd);
- /* 如果file为目录,那么肯定不可以write,重置file */
- if (file && (file->f_mode & FMODE_PATH))
- file = NULL;
- } else {
- rcu_read_lock();
- file = fcheck_files(files, fd);
- if (file) {
- /*
- 同样要检测file是否为目录,并且要在file->count不为0的情况下增加file的引用计数。
- 如果file->count为0,那么说明file结构已经被别人释放了。那么也要重置file.
- */
- if (!(file->f_mode & FMODE_PATH) &&
- atomic_long_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;
- }
- ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
- {
- ssize_t ret;
/* 做各种权限检查 */
- if (!(file->f_mode & FMODE_WRITE))
- return -EBADF;
- if (!file->f_op || (!file->f_op->write && !file->f_op->aio_write))
- return -EINVAL;
- if (unlikely(!access_ok(VERIFY_READ, buf, count)))
- return -EFAULT;
- ret = rw_verify_area(WRITE, file, pos, count);
- if (ret >= 0) {
- count = ret;
- /*
- 在前面的文件《socket与VFS的关联1》中的socket_alloc_file函数中,已经将socket的文件操作集赋
- 给了socket对应的file结构。因此这里的file->f_op即为socket_file_ops。
- 而socket_file_ops->write为空,则下面会进入do_sync_write,即同步IO操作
- */
- if (file->f_op->write)
- ret = file->f_op->write(file, buf, count, pos);
- else
- ret = do_sync_write(file, buf, count, pos);
- if (ret > 0) {
- fsnotify_modify(file);
- add_wchar(current, ret);
- }
- inc_syscw(current);
- }
- return ret;
- }
- ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
- {
- struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len };
- struct kiocb kiocb;
- ssize_t ret;
- init_sync_kiocb(&kiocb, filp);
- kiocb.ki_pos = *ppos;
- kiocb.ki_left = len;
- kiocb.ki_nbytes = len;
- for (;;) {
- /*
- 这里即调用socket_file_ops->aio_write,即sock_aio_write。
- 也就进入socket层的操作。到此完成了应用层-》内核-》VFS-》socket的过程。
- */
- ret = filp->f_op->aio_write(&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如何将应用层的写操作,通过VFS传递到socket层。