欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux:文件描述符

程序员文章站 2024-01-31 12:15:34
...

我们之前就知道在Linux操作系统下 “一切皆文件” ,所以在这个条件下,所有研究的对象都变相的相当于研究文件,那么对文件系统的研究也应该是必须的。

文件描述符fd

我们的内核利用文件描述符来访问文件,每个文件描述符都是非负整数,打开现存的文件或者是新建文件时,内核会返回一个文件描述符,读写文件也需要使用文件描述符来指定待读写的文件。

这样说来,其实每个打开的文件,都对应属于其本身的一个文件描述符。

  • Linux进程默认的情况下会有三个默认打开的文件描述符。即标准输入0,标准输出1,标准错误2
  • 0 ,1,2对应的物理设备一般是键盘、显示器、显示器

    Linux:文件描述符

由图可以理解到,在我们进程的进程控制块即PCB中有一个结构体指针file,它指向了一个files_struct结构体,这个结构体内保存着一个指针数组,其每个元素都是一个指针,这个指针就是打开文件的指针,而我们所谓的文件描述符就是这些指针的下标,所以只要拿着文件描述符就可以找到对应的文件。

#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    int fd = open("myfile",O_RDONLY);
    if(fd < 0)
    {
        perror("open");
        return 1;
    }
    printf("fd: %d\n",fd);

    close(fd);

    return 0;
}

Linux:文件描述符

这个也验证了在Linux下,系统默认打开三个文件,即标准输入、标准输出、标准错误。而我们打开一个新的文件,文件描述符是从3开始的。

重定向

先看一个代码

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    close(1);
    int fd = open("myfile",O_WRONLY|O_CREAT, 00644);
    if(fd < 0)
    {
        perror("open");
        return 1;
    }
    printf("fd: %d\n",fd);
    fflush(stdout);

    close(fd);

    return 0;
}

Linux:文件描述符

我们发现,当我们利用close关闭掉文件描述符1所对应的文件时,然后进行printf,竟然在运行test的时候屏幕没有输出。这是为什么呢?

我们之前说过,系统默认打开三个文件,即标准输入、标准输出、标准错误,也就是在我们关闭1之后,标准输出被关闭了,标准输出一般对应的是显示器,所以在我们printf的时候,在file_struct结构体中,file*回去寻找此时文件描述符为1的内容进行打印,它并不知道此时的1已经不是对应的显示器了,而这个时候的1则对应我们打开的新文件,即myfile,所以他将内容写入了这个文件。

Linux:文件描述符

常见的重定向有:> < >>其中>>为追加重定向。

FILE

  • 因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以在本质上,访问文件都是通过fd来进行访问的
  • 所以在C库当中的FILE结构体内部,必定封装了fd
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    const char* msg0 = "hello prntf\n";
    const char* msg1 = "hello fwrite\n";
    const char* msg2 = "hello write\n";

    printf("%s",msg0);
    fwrite(msg1,strlen(msg1),1,stdout);
    write(1,msg2,strlen(msg2));


    fork();

    return 0;
}

Linux:文件描述符

我们发现在直接运行./test的时候,显示器打印三行hello,而我们队进程实现重定向的时候,发现printf与fwrite打印了两次,而write只有一次。这是为什么?

C库函数写入文件的时候是进行的全缓冲,而写在显示器上是行缓冲,所以我们在定义时定义成全部都是\n结尾,也就是说在屏幕上输出的时候,直接输出整句话,不再等待。所以显示了三句话。而当我们由屏幕改为文件后,原来的行缓冲输出被变成了全缓冲,此时\n只是单纯的换行,并不会直接输出,那么这个时候由于fork()函数的存在子进程也拥有一份父进程相同的数据,那么最终重定向到文件的时候,则将缓冲区内的所有内容全部定向到新的文件当中,所以也就出现了五句话,那么为什么write这句话并没有在文件当中呢?原因是因为printf,fwrite都为库函数,而库函数自带缓冲区,而write为系统调用,它没有缓冲区,也就是说他直接输出。

库函数其实是在系统调用的基础上对系统调用进行封装,但是write并未有缓冲区,而fwrite有缓冲区是为什么呢?这其实是因为这个缓冲区是二次加上的,有C标准库提供。


欢迎大家共同讨论,如有错误及时联系作者指出,并改正。谢谢大家!