《Linux文件系统之文件的读写.doc》由会员分享,可在线阅读,更多相关《Linux文件系统之文件的读写.doc(35页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、Linux文件系统之文件的读写(续二) - 本文系本站原创,欢迎转载!转载请注明出处:-八:VFS层的I/O操作VFS层是与用户界面直接交互的接口,在这一节里,我们将分为读写两部份来介绍VFS层的操作以及跟上层用用的交互.8.1:文件的读操作在用户空间,读文件操作的常用函数为read()。对应在系统空间的调用入口是sys_read().它的代码如下:asmlinkage ssize_t sys_read(unsigned int fd, char _user * buf, size_t count) struct file *file; ssize_t ret = -EBADF; int fp
2、ut_needed; /根据fd从进程中取出相应的file对象 file = fget_light(fd, &fput_needed); if (file) loff_t pos = file_pos_read(file); /文件的当前位置 ret = vfs_read(file, buf, count, &pos); /更新当前的文件位置 file_pos_write(file, pos); fput_light(file, fput_needed); return ret;从进程中取得文件描述符后和文件当前的操作位置后会调用vfs_read()执行具体的操作过程.它的代码如下:ssize
3、_t vfs_read(struct file *file, char _user *buf, size_t count, loff_t *pos) struct inode *inode = file-f_dentry-d_inode; 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;/检查当前区段是否允许读操作 ret = locks_verify_area(FLOCK_VER
4、IFY_READ, inode, file, *pos, count); if (!ret) /是否有权限 ret = security_file_permission (file, MAY_READ); if (!ret) /如果有read 操作,调用之 if (file-f_op-read) ret = file-f_op-read(file, buf, count, pos); else /否则调用aio_read ret = do_sync_read(file, buf, count, pos); /ret: 写入的字节数 if (ret 0) /产生通告 dnotify_parent
5、(file-f_dentry, DN_ACCESS); return ret;从上面看到,会最终调用file的相关操作完成文件的读操作.曾记得我们在文件的打开一节中分析了文件的打开过程。在打开文件过程中,文件描述符的相关操作会被赋值为inode-f_op.对于ext2文件系统,inode的相关信息如下: inode-i_fop = &ext2_file_operations;struct file_operations ext2_file_operations = .llseek = generic_file_llseek, .read = generic_file_read, .write
6、= generic_file_write, .aio_read = generic_file_aio_read, .aio_write = generic_file_aio_write, .ioctl = ext2_ioctl, .mmap = generic_file_mmap, .open = generic_file_open, .release = ext2_release_file, .fsync = ext2_sync_file, .readv = generic_file_readv, .writev = generic_file_writev, .sendfile = gene
7、ric_file_sendfile,相应文件读操作入口为generic_file_read():ssize_tgeneric_file_read(struct file *filp, char _user *buf, size_t count, loff_t *ppos) /用户空间的地址和长度 struct iovec local_iov = .iov_base = buf, .iov_len = count ; /记录完成状态 struct kiocb kiocb; ssize_t ret; /kiocb.ki_key=KIOCB_SYNC_KEY; kiocb.ki_filp=filp;
8、kiocb.ki_obj=current; init_sync_kiocb(&kiocb, filp); /返回读写完成的字节数 ret = _generic_file_aio_read(&kiocb, &local_iov, 1, ppos); /异步操作,需用等待 if (-EIOCBQUEUED = ret) ret = wait_on_sync_kiocb(&kiocb); /返回完成的字节数 return ret;_generic_file_aio_read()是一个很重要的函数,它是读操作的入口。代码如下:ssize_t_generic_file_aio_read(struct k
9、iocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t *ppos) struct file *filp = iocb-ki_filp; ssize_t retval; unsigned long seg; size_t count; count = 0; for (seg = 0; seg iov_len; if (unlikely(ssize_t)(count|iv-iov_len) iov_base 开始的iov_len区间的合法性 if (access_ok(VERIFY_WRITE, iv-iov_base
10、, iv-iov_len) continue; if (seg = 0) return -EFAULT; /nr_seg: 有效的数据段数目 nr_segs = seg; /上一个数据段无效,将其长度减下来 count -= iv-iov_len; /* This segment is no good */ break; /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ /如果定义了O_DIRECT:直接传送数据绕过了页高速缓存 if (filp-f_flags & O_DIRECT) loff_t pos = *ppos,
11、 size; struct address_space *mapping; struct inode *inode; mapping = filp-f_mapping; inode = mapping-host; retval = 0; if (!count) goto out; /* skip atime */ size = i_size_read(inode); if (pos = 0 & !is_sync_kiocb(iocb) retval = -EIOCBQUEUED; if (retval 0) *ppos = pos + retval; file_accessed(filp);
12、goto out; /count:读取文件的长度 retval = 0; if (count) for (seg = 0; seg host; unsigned long index, end_index, offset; loff_t isize; struct page *cached_page; int error; struct file_ra_state ra = *_ra; cached_page = NULL; /找到页面的偏移量。即确定是存储在那个存面中 index = *ppos PAGE_CACHE_SHIFT; /第一个请求字节在页面的偏移量 /亦即请求的字节在页面中的偏
13、移 offset = *ppos & PAGE_CACHE_MASK; /inode对应的文件大小 isize = i_size_read(inode); if (!isize) goto out; /最后的缓存页序号 end_index = (isize - 1) PAGE_CACHE_SHIFT; for (;) struct page *page; unsigned long nr, ret; /* nr is the maximum number of bytes to copy from this page */ /nr: 缓存页空间大小 nr = PAGE_CACHE_SIZE;
14、if (index = end_index) /index end_indx: 肯定是非法的页面缓存器大小 if (index end_index) goto out; /执行到这里,肯定有index = end_index /nr转化成了文件在最后一个缓存page中的位置 nr = (isize - 1) & PAGE_CACHE_MASK) + 1; /offset是当前位置在页中的偏移,nr: 是最后一个块在磁盘中的偏移 /如果nr=offset说明文件已经操作完了 if (nr error = -EWOULDBLOCKIO; break; handle_ra_miss(mapping,
15、 &ra, index); goto no_cached_page; /在页缓存区中找到了相关的页面 /检查PG_uptodata标志是否被设置如果这个标志被设置的话,就不需要从设备 /上去读取了 if (!PageUptodate(page) /页面没有设置PG_uptodata页面中的内容无效,所以要从文件系统中把数据读取出来 if (nonblock) page_cache_release(page); desc-error = -EWOULDBLOCKIO; break; goto page_not_up_to_date; page_ok: /* If users can be wri
16、ting to this page using arbitrary * virtual addresses, take care about potential aliasing * before reading the page on the kernel side. */ if (mapping_writably_mapped(mapping) flush_dcache_page(page); /* * Mark the page accessed if we read the beginning. */ if (!offset) mark_page_accessed(page); /*
17、* Ok, we have the page, and its up-to-date, so * now we can copy it to user space. * * The actor routine returns how many bytes were actually used. * NOTE! This may not be the same as how much of a user buffer * we filled up (we may be padding etc), so we can only update * pos here (the actor routin
18、e has to update the user buffer * pointers and the remaining count). */ /页面与用户空间的值拷贝.返回拷贝的数据数 ret = actor(desc, page, offset, nr); offset += ret; index += offset PAGE_CACHE_SHIFT; offset &= PAGE_CACHE_MASK; page_cache_release(page); /如果ret = nr: 拷贝的长度等于在页面中的剩余长度,说明拷贝没有发生错误 if (ret = nr & desc-count)
19、 continue; /否则,可以退出了 goto out;page_not_up_to_date: /* Get exclusive access to the page . */ /要从文件系统中传数据到此页面上。将此页面锁定 lock_page(page); /* Did it get unhashed before we got the lock? */ /有可能在锁页面的时候有其它的进程将页面移除了页缓存区 /在这种情况下:将page解锁并减少它的使用计数,重新循环 /重新进入循环后,在页缓存区找不到对应的page.就会重新分配一个新的page if (!page-mapping)
20、unlock_page(page); page_cache_release(page); continue; /* Did somebody else fill it already? */ /在加锁的时候,有其它的进程完成了从文件系统到具体页面的映射? /在这种情况下,返回到page_ok.直接将页面上的内容copy到用户空间即可 if (PageUptodate(page) unlock_page(page); goto page_ok; /读取页面readpage: /* Start the actual read. The read will unlock the page. */ /
21、到这里的话,实际的读取过程开始了 _ error = mapping-a_ops-readpage(filp, page); /读取错误,退出 if (unlikely(error) goto readpage_error; /如果PG_uptodata标志仍然末设置.就一直等待,一直到page不处于锁定状态 / TODO: 在将文件系统的内容读入page之前,page一直是处理Lock状态的。一直到 /读取完成后,才会将页面解锁. 然后将进程唤醒 if (!PageUptodate(page) wait_on_page_locked(page); /如果页面仍然没有PG_uptodata标志
22、.只可能是发生了错误.出错返回 if (!PageUptodate(page) error = -EIO; goto readpage_error; /* * i_size must be checked after we have done -readpage. * * Checking i_size after the readpage allows us to calculate * the correct value for nr, which means the zero-filled * part of the page is not copied back to userspac
23、e (unless * another truncate extends the file - this is desired though). */ isize = i_size_read(inode); end_index = (isize - 1) PAGE_CACHE_SHIFT; /如果文件大小无效或者当前位置超过了文件大小 if (unlikely(!isize | index end_index) page_cache_release(page); goto out; /* nr is the maximum number of bytes to copy from this p
24、age */ /重新计算nr 即在页面中剩余的要copy的字节数 nr = PAGE_CACHE_SIZE; if (index = end_index) nr = (isize - 1) & PAGE_CACHE_MASK) + 1; if (nr error = error; page_cache_release(page); goto out;no_cached_page: /* * Ok, it wasnt cached, so we need to create a new * page. */ /在页缓区中没有相关的缓存页 /新分匹一个页面 if (!cached_page) ca
25、ched_page = page_cache_alloc_cold(mapping); if (!cached_page) desc-error = -ENOMEM; goto out; /将分得的页加到页缓存区和LRU / TODO:在将新页面插入页缓存区域中,会将页面标志设置为PG_locked error = add_to_page_cache_lru(cached_page, mapping, index, GFP_KERNEL); if (error) if (error = -EEXIST) goto find_page; desc-error = error; goto out; page = cached_page; cached_page = NULL; goto readpage; out: *_ra = ra; /ppos: 最后的读取位置 *ppos = (loff_t) index error = -EWOULDBLOCKIO;