zoukankan      html  css  js  c++  java
  • 文件描述符的本质

    1. 文件描述符的本质是数组元素的下标

    Alt text

    右侧的表称为i节点表,在整个系统中只有1张。该表可以视为结构体数组,该数组的一个元素对应于一个物理文件。

    中间的表称为文件表,在整个系统中只有1张。该表可以视为结构体数组,一个结构体中有很多字段,其中有3个字段比较重要:

    1. file status flags:用于记录文件被打开来读的,还是写的。其实记录的就是open调用中用户指定的第2个参数
    2. current file offset:用于记录文件的当前读写位置(指针)。正是由于此字段的存在,使得一个文件被打开并读取后,下一次读取将从上一次读取的字符后开始读取
    3. v-node ptr:该字段是指针,指向右侧表的一个元素,从而关联了物理文件。

    左侧的表称为文件描述符表,每个进程有且仅有1张。该表可以视为指针数组,数组的元素指向文件表的一个元素。最重要的是:数组元素的下标就是大名鼎鼎的文件描述符。

    open系统调用执行的操作:新建一个i节点表元素,让其对应打开的物理文件(如果对应于该物理文件的i节点元素已经建立,就不做任何操作);新建一个文件表的元素,根据open的第2个参数设置file status flags字段,将current file offset字段置0,将v-node ptr指向刚建立的i节点表元素;在文件描述符表中,寻找1个尚未使用的元素,在该元素中填入一个指针值,让其指向刚建立的文件表元素。最重要的是:将该元素的下标作为open的返回值返回。

    这样一来,当调用read(write)时,根据传入的文件描述符,OS就可以找到对应的文件描述符表元素,进而找到文件表的元素,进而找到i节点表元素,从而完成对物理文件的读写。

    2. fork 对文件描述符的影响

    fork会导致子进程继承父进程打开的文件描述符,其本质是将父进程的整个文件描述符表复制一份,放到子进程的PCB中。因此父、子进程中相同文件描述符(文件描述符为整数)指向的是同一个文件表元素,这将导致父(子)进程读取文件后,子(父)进程将读取同一文件的后续内容。

    案例分析(forkfd.c):

    #include <stdio.h>  
    #include <stdlib.h>  
    #include <fcntl.h>  
    #include <sys/types.h>  
    #include <unistd.h>  
    #include <sys/types.h>  
      
    int main(void)  
    {  
           int fd, pid, status;  
           char buf[10];  
             if ((fd = open("./test.txt", O_RDONLY)) < 0) {  
                     perror("open");  exit(-1);  
            }  
             if ((pid = fork()) < 0) {  
                   perror("fork");  exit(-1);  
            } else if (pid == 0) {  //child  
                    read(fd, buf, 2);  
                    write(STDOUT_FILENO, buf, 2);  
            } else {  //parent  
                     sleep(2);  
                    lseek(fd, SEEK_CUR, 1);  
                    read(fd, buf, 3);  
                     write(STDOUT_FILENO, buf, 3);  
                     write(STDOUT_FILENO, "
    ", 1);  
            }  
             return 0;  
     }  

    假设,./test.txt的内容是abcdefg。那么子进程的18行将读到字符ab;由于,父、子进程的文件描述符fd都指向同一个文件表元素,因此当父进程执行23行时,fd对应的文件的读写指针将移动到字符d,而不是字符b,从而24行读到的是字符def,而不是字符bcd。程序运行的最终结果是打印abdef,而不是abbcd。

    相对应的,如果是两个进程独立调用open去打开同一个物理文件,就会有2个文件表元素被创建,并且他们都指向同一个i节点表元素。两个文件表元素都有自己独立的current file offset字段,这将导致2个进程独立的对同一个物理文件进行读写,因此第1个进程读取到文件的第1个字符后,第2个进程再去读取该文件时,仍然是读到的是文件的第1个字符,而不是第1个字符的后续字符。

    对应用程序员而言,最重要结论是: 如果子进程不打算使用父进程打开的文件,那么应该在fork返回后立即调用close关闭该文件。

  • 相关阅读:
    ActiveReports 报表应用教程 (9)---交互式报表之动态排序
    struts2-结果处理方式
    struts2-action的创建方式
    struts2-动态方调用
    struts2-常量配置
    struts2架构图
    maven依赖范围-Scope
    Maven在eclipse的使用入门
    Maven简介
    数组的学习与使用
  • 原文地址:https://www.cnblogs.com/dingxiaoqiang/p/7783391.html
Copyright © 2011-2022 走看看