收藏 分享(赏)

linux-vfs(虚拟文件系统).ppt

上传人:无敌 文档编号:1110948 上传时间:2018-06-12 格式:PPT 页数:44 大小:557.52KB
下载 相关 举报
linux-vfs(虚拟文件系统).ppt_第1页
第1页 / 共44页
linux-vfs(虚拟文件系统).ppt_第2页
第2页 / 共44页
linux-vfs(虚拟文件系统).ppt_第3页
第3页 / 共44页
linux-vfs(虚拟文件系统).ppt_第4页
第4页 / 共44页
linux-vfs(虚拟文件系统).ppt_第5页
第5页 / 共44页
点击查看更多>>
资源描述

1、Linux VFS,Actions Microelectronics Co., Ltd.,柯锦玲2009-11-19,Agenda,Linux读写文件的5种模式,通用文件模型对象组成,Open一个设备节点文件,VFS open文件的步骤,一些关于vfs的问题,VFS 框图,VFS,类Unix操作系统总共有5种标准文件类型:1).普通文件2).目录文件3).符合链接文件4).设备文件5).管道文件 VFS的其中一个重要作用是将对设备驱动的访问,转换为对文件的访问(设备节点文件),通用文件模型对象-超级块对象,超级块对象(superblock object):存放已安装文件系统的有关信息. 通常对

2、应于放在磁盘上的文件系统控制块(filesystem control block),每挂载一个文件系统 对应一个超级块对象struct super_block unsigned longs_blocksize;/块设备的最写读取单位,如512 字节const struct super_operations*s_op;struct dentry*s_root;struct list_heads_inodes;/* all inodes */struct list_heads_dirty;/* dirty inodes */struct block_device*s_bdev;void *s_fs

3、_info;/* Filesystem private info */;struct super_operations struct inode *(*alloc_inode)(struct super_block *sb);/分配inode 索引节点void (*dirty_inode) (struct inode *);/标志索引节点为脏int (*write_inode) (struct inode *, int);/写索引节点,对于fat 文件系统是写 目录项void (*write_super) (struct super_block *);/写超级块, 对于fat 文件系统是fat

4、的簇表信息,通用文件模型对象-索引节点对象(inode),索引节点对象(inode object):存放关于具体文件的一般信息. 通常对应于存放在磁盘上的文件控制块(file control block).每个索引节点对象都有一个索引节点号,这个节点号唯一地标识文件系统中的文件索引节点对文件(或者目录)是唯一的,并且随文件的存在而存在.struct inode dev_ti_rdev;/若为设备文件的索引节点,记录主次设备号loff_ti_size;/文件大小struct timespeci_atime;/ 文件访问时间struct timespeci_mtime;struct timespe

5、ci_ctime;unsigned inti_blkbits;blkcnt_ti_blocks;umode_ti_mode;/访问模式,如读,写const struct inode_operations*i_op;/索引节点文件操作const struct file_operations*i_fop;/文件对象(file object)操作struct address_space*i_mapping;/高速缓存的核心数据结构体,对块设备的读写操作都放在该结构体里面,索引节点对象(inode),如对于fat 格式, inode节点 会有目录或者文件的首簇号 位置.struct inode_ope

6、rations int (*create) (struct inode *,struct dentry *,int, struct nameidata *);/创建文件int (*mkdir) (struct inode *,struct dentry *,int);/创建目录int (*rmdir) (struct inode *,struct dentry *);/删除目录int (*mknod) (struct inode *,struct dentry *,int,dev_t);/创建节点文件int (*rename) (struct inode *, struct dentry *,

7、 /重命名文件struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);/查找目录/高速缓存的核心数据结构体,对块设备的读写操作都放在该结构体里面struct address_space const struct address_space_operations *a_ops;/* methods */高速缓存的核心操作方法放在索引节点里面struct address_space_operations int (*writepage)(struct page *page, struct writ

8、eback_control *wbc);int (*readpage)(struct file *, struct page *);ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,loff_t offset, unsigned long nr_segs);,通用文件模型对象-文件对象(file object),文件对象(file object)存放打开文件与进程之间进行交互的有关信息.这类信息仅当进程访问文件期间存在于内核内存中. 如文件的读取或者写位置,以及读写同步等操作.struct file const

9、struct file_operations*f_op;mode_tf_mode;/打开文件的模式loff_tf_pos;/文件的读写位置struct address_space*f_mapping;/指向inode节点的页高速缓存操作struct file_operations loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char _user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char _user

10、 *, size_t, loff_t *);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*readdir) (struct file *, void *, filldir_t); /readdir 扫描目录中的文件或者子目录;,通用文件模型对象-目录项对象(dentry),目录项对象(dentry object)存放目录项(也就是文件的名称)与对应文件进行链接的有关信息.每个磁盘文件系统都以自己特有的方式将该类信息存在磁盘上.struc

11、t dentry struct inode *d_inode;struct qstr d_name;/文件名比较长时候,需要动态分配空间,放在这里unsigned char d_inameDNAME_INLINE_LEN_MIN;/文件名长度小于36 放在这里;,VFS Open函数流程图,VFS Open文件的步骤,1)fd = open(“/home/fatfile.txt”,O_RDWR|O_CREAT,0660); c库函数open会执行系统调用 2)进入内核空间sys_open 得到空的 fd 号(sys_open会返回fd号,当返回值小于0时候出错, 等于0 是一个合法的fd号.

12、) 得到一个空的struct file结构体 struct file *filp = get_empty_filp(); 通过当前任务的task_struct(current)获取根目录(/的struct dentry结构体 通过dentry 获取 索引节点d_inode, d_inode -i_op-lookup操作,3)查找dentry object首先使用 hash查找方法搜索要查找的目录项对象是否在内存中_d_lookup(nd-path.dentry, name);若存在则返回.不用查找调用具体的文件系统(读取磁盘扇区)去查找若不存在分配新的目录项对象(dentry object)

13、将文件名保存在目录项对象(dentry object) d_iname 成员中(如保存目录名home)调用d_inode -i_op-lookup 进行具体文件系统的操作,4)具体文件系统的搜索目录static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)搜索具体的文件系统,当搜索到具体的文件或者目录(需要通过超级块对象找到目录等在磁盘的具体位置)时候, 分配新的索引节点结构体(inode)inode = new_inode(sb);填充inode结构体操作

14、if (S_ISREG(inode-i_mode) /是文件inode-i_op = ,else if (S_ISDIR(inode-i_mode) /是目录inode-i_op = Lookup完毕,回到VFS,VFS 分离路径取得文件名 fatfile.txt继续搜索文件名4)最后,填充 struct file*f 结构体f-f_mapping = inode-i_mapping;f-f_path.dentry = dentry;f-f_path.mnt = mnt;f-f_pos = 0;f-f_op = inode-i_fop;调用f- f_pos-open 函数5)返回的fd号 与

15、struct file*f建立关系.current 为当前任务的 struct task_struct 结构体(每一个任务都有一个)struct files_struct *files = current-files;struct files_struct struct file * fd_arrayNR_OPEN_DEFAULT;填充 前任务的 struct task_struct 结构体 成员struct files_struct *files中的指针数组struct file * fd_arrayNR_OPEN_DEFAULT;最后 sys_open返回 fd号,Open一个设备节点文件

16、,初始化设备节点文件的索引引节点void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)inode-i_mode = mode;if (S_ISCHR(mode) /字符设备节点文件inode-i_fop = ,字符设备处理,字符设备 处理得比较简单,只有一个open成员const struct file_operations def_chr_fops = .open = chrdev_open,;static int chrdev_open(struct inode *inode, struct file *

17、filp)filp-f_op = 注册字符设备驱动的字符设备操作表 因此,通过 sys_open返回的fd, 就可以找到注册字符设备驱动的字符设备操作表在用户态里面调用read 最终就是调用到filp-f_op-read函数(编写字符设备驱动时候,所写的 read函数),块设备处理,块设备操作const struct file_operations def_blk_fops = .open= blkdev_open,.release= blkdev_close,.llseek= block_llseek,/这就说明了 为什么块设备驱动没有read ,write函数了.read= do_sync

18、_read,.write= do_sync_write,.mmap= generic_file_mmap,;,块设备文件的open方法static int blkdev_open(struct inode * inode, struct file * filp)struct block_device *bdev;/通过设备节点的主 设次备号 ,获取块设备文件结构体bdev = bd_acquire(inode);res = do_open(bdev, filp, 0);在do_open里面设置,file-f_mapping = bdev-bd_inode-i_mapping;而且 若设备文件不

19、是一个分区是整个磁盘, 则重新扫描分区表,建立分区表信息. 其实扫描分区表的操作在 注册块设备驱动时候 ,已经做了一次,文件的读写,文件的读写,Linux下访问文件的5种模式:1).规范模式. Open文件时候没有设置O_SYNC与O_DIRECT 模式.系统调用read()将阻塞调用进程,直到数据被拷贝进用户地址空间.但系统调用write()不同,它在数据被拷贝到页高速缓存后(延时写),就马上返回结束.fd = open(“/home/fatfile.txt”,O_RDWR);,2).同步模式 文件以O_SYNC模式打开,或者稍后由系统调用fcntl()对其置1.则写操作(读操作总是阻塞),

20、它将阻塞调用进程,直到数据被有效写入磁盘.fd = open(“/home/fatfile.txt”,O_RDWR|O_SYNC);,3).内存映射模式(耗费内存?若读取大量数据,不建议使用)文件被打开后,应用程序发出系统调用mmap()将文件映射到内存中.因此文件就成为RAM中的一个字节数组,应用程序直接访问数据元素.,而不需要系统调用read(),write()或lseek().,内存映射:一个线性区可以和磁盘文件系统的普通文件的某部分或者块设备文件相关联. 这就意味这内核把对区线性中页内某个字节的访问 转换成 对文件中相应字节的 操作.两种类型的内存映射:共享型:在线性区的任何写操作都会

21、修改磁盘上的文件;而且,如果进程对共享映射中的一个页进行写,这种修改对于其他映射了这同一文件的所有经常来说是可见的.进程发出一个mmap()系统调用,并且指定一个MAP_SHARED标志私有型:进程创建映射只是为了读取文件,而不是写文件.出于此目的,私有映射比共享映射效率高更高. 但是对于私有映射页的任何写操作都会使内核停止映射该文件中的页. 私有映射中还没有被进程改变的页会因为其他进程进行文件的更新而更新.进程发出一个mmap()系统调用,并且指定一个MAP_PRIVATE标志,内存映射举例:fd = open(depfile, O_RDONLY);map = mmap(NULL, st.s

22、t_size, PROT_READ, MAP_PRIVATE, fd, 0);/.do somethingchar sPATH_MAX;memcpy(s, map, PATH_MAX);/拷贝.munmap(map, st.st_size);Mmap 其实是分配了没有物理页面对应的线性区空间,Mmap操作流程,memcpy(s, map, PATH_MAX);操作做的事情,4).直接I/O模式文件以O_DIRECT模式打开.任何读写操作都将数据在用户态空间与磁盘间直接传送而不通过页高速缓存.fd = open(“/home/fatfile.txt”,O_RDWR|O_DIRECT);直接I/O

23、传送(linux提供的绕过页高速缓存的办法)对于嵌入式系统来说,内存是很紧张的,而且同一块数据很少会多次使用内核页高速缓存个人觉得反而会影响到读写的效率.,1).很多页框浪费在复制已在RAM中的磁盘数据(用户级磁盘高速缓存). 2).处理页高速缓存和预读的多余指令降低了read()和write()系统调用的执行效率. 3).read,write系统调用不是在磁盘和用户存储器之间直接传送数据,而是分两次传送:在磁盘和内核缓冲区之间 和 在内核缓冲区与用户存储区直接I/O传送使得应用程序的用户态地址空间中的页与磁盘之间直接传送数据.(内核ntfs 并不支持该模式 ,vfat 支持)个人觉得 对于嵌

24、入式系统,该读写办法效率比较高而且省内存,5).异步模式 异步模式下,文件的访问可以有两种方法,即通过一组POSIX API或Linux特有的系统调用来实现.所谓异步模式就是数据传输请求并不阻塞调用进程,而是在后台执行,同时应用程序继续它的正常运行.,规范模式读取文件的例子:inode-i_fop = 大部分文件系统的具体读写操作都在这struct address_space_operations结构体中,它负责 激活 从物理磁盘到页高速缓存的 I/O 数据传输const struct address_space_operations ext2_aops = .readpage= ext2_r

25、eadpage,.,读写效率,读写效率思考:假如读取1M 的文件数据,则调用mapping-a_ops-readpage,每次只读一页 4KB的空间若文件系统检查文件的4KB数据 在物理介质上是连续的, 则 只发一次 io调用请求bio-bi_end_io = mpage_end_io_read;/回调函数submit_bio(rw, bio);若不连续 则,可能发 8次io请求(假设块设备,比如nand 是每次最多读取512字节)发io调用请求后,调用_wait_on_bit_lock(),使当前进程出于睡眠状态,当io 调度层 ,成功读取数据后,调用回调唤醒进程mpage_end_io_r

26、ead(struct bio *bio, int err)_wake_up_bit(page_waitqueue(page), 将内核数据拷贝到 用户态 线性地址中.继续读下一页.,思考问题:,思考问题:1)rootfs 文件系统的选择:能使用fat 或者 ntfs 等格式吗?jffs2 呢? 为什么?2) Linux的读取数据与 ucos下 读取数据的效率上的差异.假设用户要一次读取16KB的数据,而且数据在nand介质上是连续存放的,假设每个内存页大小是4KB两者的差异是什么?3) open函数的两种调用,那个对?fd = open(“/dev/ nftla”,O_RDWR);fd = o

27、pen(“/home/fatfile.txt”,O_RDWR|O_CREAT,0660); 4)linux 发送io调用请求读写某一页数据后睡眠当前进程,然后,又是怎样唤醒到对应的进程的呢?5).linux使用block设备流程?,1)Rootfs 选择问题,1)rootfs 文件系统的选择:能使用fat 或者 ntfs 等格式吗?jffs2 呢? 为什么?Fat或者 ntfs 不提供int (*mknod) (struct inode *,struct dentry *,int,dev_t);的实现,所以 不能用作 根文件系统.Ext2 与jffs2 都提供,因此,2)Linux与 ucos

28、读取数据的效率上的差异,Linux与ucos平台读取数据对比:,3)open函数可变长,open函数可变长fd = open(“/dev/ nftla”,O_RDWR);fd = open(“/home/fatfile.txt”,O_RDWR|O_CREAT,0660);int open(const char *file, int flags, .)mode_t mode;if (flags .,4)读写一个page数据后,睡眠当前进程,void wait_on_page_bit(struct page *page, int bit_nr)struct wait_bit_queue wait=

29、 .wait= .private= current,/保存当前进程的struct task_struct/将wait 结构体链接进去wait_table数组中 链表头里面,然后睡眠当前进程_wait_on_bit(通过hash 转换 将某一page与 wait_table数组索引号建立关系/有可能有hash冲突,即几个不同的page都对应wait_table数组相同的索引号,怎么处理这些冲突呢?&zone-wait_tablehash_ptr(page, zone-wait_table_bits),睡眠当前进程,int _sched_wait_on_bit(wait_queue_head_t

30、*wq, struct wait_bit_queue *q,int (*action)(void *), unsigned mode)int ret = 0;do /使睡眠当前进程prepare_to_wait(wq, 处理hash表冲突: 其实是通过do while 循环,查找某一页里面某一位是否被置位. 若置位代表数据读取成功,则退出.否则 继续睡眠,唤醒睡眠的读写进程,static inline void wake_up_page(struct page *page, int bit)_wake_up_bit(,linux使用block设备流程图,Thank You !,Actions Microelectronics Co., Ltd.,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 经营企划

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报