【文件管理】文件的打开与关闭

用户进程在能读写一个文件时之前必须要先打开这个文件;对文件的读写从概念上来说是一种进程与文件系统之间的一种连接通信;所谓文件打开实质上就是进程与文件之间建立连接,而文件打开号,就是唯一地标识着这样一个连接,这样的一个连接是一个独立的上下文;如果一个进程与某个目标之间重复建立起多个连接,则每一个连接都应该是独立的;但是通过dup()可以使用同一个file结构对应多个打开文件号;但是通常一个file结构(上下文)都有一个file结构对应;


(1)在sys_open()中,filename是代表文件的路径名(绝对路径名,或相对路径名),mode为打开的模式,flag表示打开模式以外的一些属性和要求;通过getname()从用户空间把文件的路径名拷贝到系统空间;并使用get_unused_fd()从当前进程的打开文件表找到一个空闲表项,该表项的下标即为打开文件号;然后根据文件名通过file_open()找到或创建一个连接,或者说是一个读写文件的上下文;

(2)在file结构中有指向文件的dentry结构的指针,还有指向将文件所在设备安装在文件系统的vfsmnt结构的指针,有共享计数f_count,还有一个在文件中当前位置f_ops,这就是上下文;找到或创建了代表目标文件的file结构以后,就通过fd_install()将指向这个结构的指针填入当前进程的打开文件表,即由其task_struct结构的指针指向files_struct数组中,并返回数组中下标;

(3)在get_unused_fd()中,在task_struct中,有个指针files,指向本进程的files_struct数据结构中;与打开的文件有关的信息都保存在这个数据结构中;在files_struct中,file结构指针数组fd_array[],其下标为打开的文件号,另外还有指针fd,最初也指向fd_array;结构中还有两个位图close_on_exec_init和open_fds_init,这些位图大致对应着file结构数组中内容,但是比fd_array要大的多;同时又有两个指针close_on_exec和open_fds,最初也指向上述的两个位图;每次打开文件分配一个打开文件号时就将由open_fds所指向的位图中设成1;此外,max_fds和max_fdset,分别反映着当前file结构指针数组与位图的容量;超出了其file结构指针数组的容量需要通过expand_fd_array()扩充该数组的容量(意义重大,因为现在的环境下常常需要要求同时打开数量众多的文件),并让指针fd指向新的位图;打开文件时,通过宏FD_SET将open_fds所指向的位图中的相应位设成1,还可通过FD_CLR将由指针close_on_exec所指向的位图的相应位清0;如果当前进程通过exec()执行一个可执行程序的话,就无需将这个文件关闭了;

(4)在file_open()中,在调用open_namei()之前,先要对flags中的4个标志进行相应的变换,使得统一;在open_namei(),O_CREAT表示如果要打开文件的不存在,就创建这个文件,也就是通过path_init和path_walk()根据目标节点的路径名找到该节点的dentry和inode结构,在传入的flags还是用了lookup_flags()转换成搜索的标志位,最多是LOOKUP_FOLLOW以及LOOKUP_DERECTORY;如果找不到目标节点就失败返回了;如果O_CREAT标志位为1,那么也要通过path_init和path_walk()根据目标节点的路径名找到该节点的dentry和inode结构,不过这一次找到的不是目标节点本身,而是其父节点,也就是目标文件所在的目录,素以在调用时,设寻找标志位LOOKUP_PARENT,如果在搜索的过程中,出错,那就出错返回了;否则也就是找到了这个父节点,且终点是路径名,会设置last_type为LAST_NORM的,如果不是这个标志,程序出错返回;记住,这个一个打开文件的系统调用,而不是打开一个目录的系统调用;

(5)在open_namei()中,就是通过lookup_hash()寻找目标文件的dentry结构,它首先在目标文件的所在的目录中寻找,如果找到了就把已经创建的dentry结构归还,找不到时才采用它;如果是新创建的dentry是新的,那么其inode是空的,就用vfs_create()来创建;如果已存在,当有O_CREAT和O_EXECL同时为1,表示目标文件在此前就不存在,所以已存在就返回出错了;对于安装点以及连接文件还要有相应的控制;在vfs_creat()中,当O_CREAT标志位为1,而目标文件又不存在时,就要通过这个函数来创建;此时的dir是所在目录的inode结构,dentry指向待创建文件的dentry结构,但是此时创建的文件还没有inode结构,所以dentry->d_inode还是为0的;对于unmask,称之为文件访问权限屏蔽,记录在fs_struct中,即某一位1,就把相应的权限给屏蔽掉,如077就把同组和其他组的所有权限都给屏蔽掉了;创建文件时是要改变所在目录的内容的,因此是需要在临界区的,通过inode中i_zombie来完成的,在may_create()中,首先使用IS_DEADDIR检查目标文件所在的目录是否实际上已被删除了(删除目录时,会将所在目录的inode的i_flags置成S_DEAD);通过访问权限以后,每种文件的操作因文件系统而异,每种文件提供通过其inode_operations结构提供创建文件的函数,对于EXT2,就是ext2_create();

(5.1)在ext2_create()中,简言之 ,就是通过ext2_new_inode()创建目标文件在存储设备上的索引节点和在内存中inode结构,日拿货通过ext2_add_entry()把目标文件的文件名与索引节点号写入其所在的目录,最后由 d_instantiate()将目标文件的dentry结构和inode结构联系在一起;其中inode的i_op和i_fop都是在这里设置的;

(5.1.1)在ext2_new_inode()(可以创建目录也可以是文件)中,参数dir指向所在目录的inode结构(i_nlink表示有几个目录项与这个inode相联系),先用new_inode()在内存分配一个inode,并将它挂入到内核中inode_in_use队列中,为了找到合适所在块组的位置,有可能会离开其父节点所在的块组的;再确定了将索引节点分配在哪一个块组以后,就要从该块组的索引节点位图中分配一个节点了,super_block的ext2_sb_info结构中有一个索引节点位图的缓冲区的指针数组,用来缓冲存储若干个块组的位图;当需要使用某个块组的索引节点位图时,就先在这个数组中找,若找不到再从设备上把这个块组的位图读入缓冲区中,并让该数组中的某个指针指向这个缓冲区,这是由load_inode_bitmap()完成的;取得了目标块组的索引节点位图以后,就通过ext2_find_first_zero_bit()从位图中找到一位仍然为0的位,也就是找到一个空闲的索引节点;所谓从位图中分配一个索引节点,就是通过ext2_set_bit()将其对应的位置设置成1,但是另一方面还要检查这一位是否为1,如果有冲突,因而要goto转回到repeat处另行寻找;尽管块组的描述结构告诉我们有空闲节点,ext2_find_first_zero_bit()还是有可能失败,因为块组的描述符结构有可能破换了,如断电等;紧接着就对新建立的inode进行初始化了;首先是uid,和gid的设置;新创建的文件的uid是当前进程的fsuid,也就是说因为执行一个suid可执行程序而成为超级用户的,那么所创建的文件是属于超级用户uid的,以及gid在SGRPID为0,S_ISGID为1的情况下,也就是继承所在的目录gid;然后使用insert_inode_hash()将新的inode结构链入到inode_hashtable中;

(5.2)ext2_create()中,然后就是设置新创的inode结构中的inode结构中的inode_operations以及inode_operations,还有用于文件映射的address_space_operations结构指针,使它们一一指向由EXT2文件系统提供的相应数据结构,这样VFS层与EXT2层的连接也就提供好了;然后还要使用ext2_add_entry()在该文件所在的目录中增加一个目录项与这个inode连接起来;最后还要将新建的dentry与inode挂上勾,使用的是d_instantiate();

(6)找到和创建了目标文件以后,还要对代表着目标文件的数据结构进行一系列的校验,包括访问权限以及一些处理;是通过accmode来变的;在文件的inode数据结构中有个计数器i_writecount,用来对正在写访问该文件的进程计数;另一方面,有些文件可能已经通过mmap()系统调用映射到某个进程的虚存空间,这个计数器就通过正常的文件操作和通过内存映射这两种写访问之间的沪指,但这个数为负值时就表示有进程可以通过续存管理对文件进行写操作,为正值时,表示某个或某些进程正在对文件进行写访问;

(7)在通过get_write_access()得到了写操作以后,还要考虑目标文件是否已经被其他进程锁住了;加锁分为两种,一个进程之间协调的,称为协调所,内核只提供加锁和检测文件是否已经加锁的手段,并不参与锁的实施;另一种是由内核强制实施的,称为强制锁,不管进程不按照锁的协议,内核会给予阻拦;在inode中有个i_flag字段中定义标志位MS_MANDLOCK,就是起着开关的作用;这个标志不仅用于inode,也适用于super_block。对整个文件子系统可以在安装时将参数中的这个标志设成1或0,是整个设备的文件全部允许或不允许使用强制锁;在locks_verify_locked()中,先是在使用了MANDATORY_LOCK()检查了是允许使用强制锁,那就要进一步检查是否已经加了锁;每一个文件的inode都有一个file_lock(fl_type表示锁的性质)数据结构队列i_flock,每当一个进程对一个文件中的一个区间加锁时,就创建一个区间锁file_lock并将它挂入到该队列中;如果标志位FL_FLOCK为1,说明是一个协调锁,是通过系统调用flock()系统调用加上的;这种锁一定是协调锁;如果目标文件并未加上强制锁,就可以通过do_truncate()执行结尾了;

(8)在do_truncate()中,现在代码中准备一个iattr结构(长度为0,表示全部截除),然后通过notify_change()来完成操作;在notify_change()中,先是改变inode结构的一些数据,以及伴随着这些改变的操作,由于EXT2并未通过这样的函数,默认采用了inode_change_ok()和inode_setattr();在inode_change_ok()中,是对权限的检验;最后由于inode数据变化,将它挂载到所属的super_block结构的s_dirty;

(9)在file_open()中,调用dentry_open(),建立起目标文件的一个上下文,即file结构,并让他与当前进程挂上勾;首先是get_empty_filp()分配一个空闲的file数据结构,内核中有一个空闲的file结构队列file_list,需要file结构时,就从该队列里摘一个,并将其暂时挂入一个中间队列anon_list,再确认了对该文件可以进行写操作以后,就对这个空闲file结构初始化,然后通过file_move()将其从中间队列移除到该文件所在设备的super_block结构的s_files队列中;还要把之前已验证通过的O_CREAT,O_EXCL,O_NOCTTY,O_TRUNC给去除掉,最后返回一个file结构指针;

(10)在sys_open()中,用fd_install()将新建的file结构安装到当前进程的file_struct中,确切的说是里面已打开文件指针数组中;

(11)在sys_close()中,刚开始就是要测试这个fd,此时能否为空;有些文件系统在关闭文件时会冲刷文件的内容,把文件中已改变的内容回写到设备中,并因而在其file_operations中有个flush;但是EXT2没有这样的安排,最后还要使用locks_remove_posix来释放这个posix锁;最后使用fput()递减file结构的共享计数,如果递减到0后,就可以释放这个file结构了;其他的相关活动,如clone()是有可能会递增该计数的;还要使用locks_remove_flock来释放这个FL_FLOCK协调锁;最后还要使用有的文件系统还提供了release,在ext2,就是把分配的数据块给释放掉了;最后目标文件的dentry和vfsmount也要使用dput()和mntput()递减他们的共享计数;如果最初的打开方式是以FMODE_WRITE写访问的,则还要通过put_write_access()递减其inode结构中i_writecount计数;释放file结构,就是把它从inode_hashtable中的杂凑队列移除,退还到free_list中;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值