第三步:建立file descriptor
file descriptor就是进程控制块task_struct中files中维护的fd_array。因为是数组,所以file descriptor实际上已经预先分配好空间了,这里这是需要把某个空闲的file descriptor与file object关联起来。这个file descriptor在数组中的索引号就是open文件时得到的文件fd。
12 open与dup 同一个文件是可以open多次的,结构如下图所示。每次open都会建立一个新的file descriptor与file object。然后指向同一个文件的inode节点。下图中,假设open的文件与fd1指向的是同一个文件,则新创建的file object 2与fd1的file object 2会指向同一个inode2节点。
Linux还提供了dup功能,用于复制file descriptor。使用dup不会建立新的非file object,所以新建立的file descriptor会与原filedescriptor同时指向同一个file object。下图中,我们通过dup(fd1)得到了fd2,则fd2与fd1指向了同一个file object2。
两次open后由于会生成新的object,所以文件读写属性、文件读写位置(f_pos)等信息都是独立的。使用dup复制file descriptor后,由于没有独立的object,所以修改某个fd的属性或文件读写位置后,另一个fd也会随之变化。
13 Fork对打开文件的影响 Dup的操作与fork一个子进程时的操作类似。
下图是已有父进程的文件结构:
使用fork后的结构如下。同样是没有创建新的file object,因此当对parent process中的fd1进行文件指针的移动时(如读写),child process中的fd1也会受影响。也即是说opened files list不是进程的一部分,因此不会被复制。Opened files list应该是一个全局性的资源链表,进程维护的是一个指针列表fd table,所以被复制的只是指针列表fd table,而不是opened files list。
14 文件操作函数解析 通过上面的分析,可以对各个函数的作用域与使用方式有更清晰的了解。下面列出了常用的文件操作:
函数名 | 作用对象 | 说明 | creat | dentry, inode | 创建文件时会创建新的dentry与inode | open | file object | 如果文件不存在,且有O_CREAT参数,则会先调用creat | close | file object | 删除file object,但不会删除文件。 | state/lstate | inode | 读取inode的内容。如果目标是symbolic link,stat会读取symbolic link指向的内容;lstat则会读取symbolic link文件本身。 | chmod | file object | 改变file object中的f_mode | chown/lchown | file object | 改变file object中的f_uid与f_gid | truncate | inode | 改变文件长度。 | read | file object | 读文件会改变file object中的f_pos | write | file object,inode | 写文件改变file object中的f_pos的同时也会改变文件内容与更新修改时间。 | dup | file object | 建立一个新的file descriptor,指向同一个file object项 | seek/lseek | file object | 改变file object中的f_pos | link | dentry | 创建新的dentry项,指向同一个inode节点。 | unlink | dentry | 删除一个dentry项。如果该dentry指向的inode节点没有被其他dentry项使用,则删除inode节点与磁盘文件。 | rename | dentry | 修改dentry相中的d_name | readlink | ———– | read无法读取symbolic link 文件的内容,需要使用readlink读取 | symlink | dentry, inode | 作用与creat类似,但创建的文件属性为symbolic link。 | 注:磁盘文件与inode节点一一对应,所以在表中不再单独列出磁盘文件。 |