• 文件操作系统调用接口、文件描述符的剖析、重定向的原理


    文件系统调用接口

    对文件进行操作的第一步首先就是要将文件打开,文件未打开之前是保存在磁盘中的,而打开后就被加载到内存中了,之后我们就能对文件进行操作。

    打开文件的系统调用接口:open

    #include <sys/stat.h>
    #include <sys/types.h>
    #include <fcntl.h>
    
    int open(const char* pathname, int flags); //函数1
    int open(const char* pathname, int flags, mode_t mode); //函数2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    先分析函数2,
    pathname·:想要打开/创建的文件(包含路径和文件名)
    flags:标志位。表示想要它是如何打开的以及一些属性。对于这个参数系统定义了几个宏。

    • O_WRONLY:只写打开
    • O_RDONLY:只读打开
    • O_RDWR:读写打开

    上面的这三个宏,再使用open时必须要指定一个,并且也只能指定一个。

    • O_CREAT:如果指定文件不存在,使用这个宏,可以自动创建它。创建的话,需要使用mode参数指明创建出的文件的访问权限。
    • O_APPEND:追加写

    返回值
    成功:返回一个新打开的文件描述符(在后面介绍)
    失败:返回-1

    关闭文件

    #include <unistd.h>
    
    int close(int fd); //传入文件描述符
    
    • 1
    • 2
    • 3

    文件描述符

    一个进程通常会打开多个文件,那么操作系统就需要对这些文件进行管理。

    Linux操作系统将打开的文件相关属性信息放在了结构体struct file内。而Linux的进程PCB---->task_struct中有一个结构体指针struct files_struct *fs,fs指向的表中又存在着一个数组,这个数组里面的内容正是存放着文件相关属性信息的结构体地址。
    在这里插入图片描述
    而文件描述符就是文件打开后存放其相关属性信息的结构体,对应的fd_array数组下标。只要有描述它相关属性信息的结构体信息,那么就能对这个文件进行操作。

    //打开文件,查看文件描述符
      1 #include <stdio.h>
      2 #include <sys/types.h>
      3 #include <sys/stat.h>
      4 #include <fcntl.h>
      5 #include <unistd.h>
      6 
      7 int main(void)
      8 {
      9    int fd = open("./log.txt", O_WRONLY |O_CREAT, 0644);
     10    if(fd < 0)
     11    {
     12      //open fail
     13      perror("open");
     14      return 1;
     15    }
     16 
     17    printf("%d\n", fd);                                                                                                                                          
     18 
     19   //close file
     20    close(fd);
     21   return 0;
     22 }
    ~
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    运行结果
    在这里插入图片描述
    为什么打开文件log.txt,open返回的文件描述符是3?
    打开多个文件,它们的文件描述符又会怎样?

      1 #include <stdio.h>
      2 #include <sys/types.h>
      3 #include <sys/stat.h>
      4 #include <fcntl.h>
      5 #include <unistd.h>
      6 
      7 int main(void)
      8 {
      9    int fd1 = open("./log1.txt", O_WRONLY |O_CREAT, 0644);
     10    int fd2 = open("./log2.txt", O_WRONLY |O_CREAT, 0644);
     11    int fd3 = open("./log3.txt", O_WRONLY |O_CREAT, 0644);
     12    int fd4 = open("./log4.txt", O_WRONLY |O_CREAT, 0644);
     13    int fd5 = open("./log5.txt", O_WRONLY |O_CREAT, 0644);
     14 
     15    printf("%d\n", fd1);
     16    printf("%d\n", fd2);
     17    printf("%d\n", fd3);
     18    printf("%d\n", fd4);
     19    printf("%d\n", fd5);
     20 
     21   //close file
     22    close(fd1);
     23    close(fd2);
     24    close(fd3);
     25    close(fd4);
     26    close(fd5);                                                                                                                                                  
     27   return 0;
     28 }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在这里插入图片描述
    显然,包含它们相关属性信息的结构体,对应的fd_array[]数组下标是3开头的。

    那么fd_array[]数组下标0、1、2里面放了哪些struct file的地址?
    事实上,当一个进程被创建的时候,操作系统会默认打开标准输入、标准输出、标准错误。而这些都是文件,标准输入、标准输出、标准错误对应的文件描述符正是0、1、2

    文件描述符的分配规则
    文件描述符的分配规则:给一个新文件分配文件描述符,从数组头部开始线性遍历,直到第一次找到一个没有被占用的空间,用存放这个新文件的struct file,其下标就是对应的文件描述符。(即下标最小的,没有被占用的作为文件描述符)

    正如前面所举例,我们新打开一个文件,0、1、2被标准输入、标准输出和标准错误占用,那么此时下标最小的、没有被占用的就是数组下标为3的空间。所以这个文件的文件描述符为3。

    //当我们尝试将标准输出关闭
      1 #include <stdio.h>
      2 #include <sys/types.h>
      3 #include <sys/stat.h>
      4 #include <fcntl.h>
      5 #include <unistd.h>
      6 
      7 int main(void)
      8 {
      9   close(1); //关闭标准输出
     10   int fd = open("./log.txt", O_CREAT | O_WRONLY, 0644);
     11   printf("%d\n", fd);
     12   printf("hello world\n");
     13   printf("hello world\n");
     14   printf("hello world\n");
     15   printf("hello world\n");
     16   printf("hello world\n");
     17   printf("hello world\n");
     18   //close(fd);   
     19   return 0;
     20}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    运行结果:本来应该直接显示到屏幕,但是并没有,发现跑到了文件log.txt中
    在这里插入图片描述
    因为我们把标准输入关闭了,那么文件描述符1就分配给了log.txt,而操作系统一直认为,打印应该是打印到文件描述符为1的位置,所以才会发生这种情况,这也是输出重定向的原理

    这个例子要注意,不要close(fd),如果最后关闭了文件描述符1,那么这个位置就是空的,无法打印。

  • 相关阅读:
    Python使用pymysql和xlrd2将Excel数据导入MySQL数据库
    如何在填报场景中使用数据绑定获取数据源
    IDEA git操作技巧大全,持续更新中
    Java 中的 String Pool 简介
    Academic Inquiry|创新与城市间知识溢出:社会、地理和技术的连通性和心理开放性
    利用pytorch可以实现手写字体的识别吗 基于深度学习算法
    数据链路层
    Tengine 边缘AI计算框架移植RV1126(包括opencv的交叉编译)
    vue的指令和插值总结
    PG-PFC-17E、PG-PEV-16A、PG-PEV-18A插装式比例流量阀控制器
  • 原文地址:https://blog.csdn.net/qq_56870066/article/details/125557541