Linux-fd与FILE结构体

本文介绍了系统I/O的基本概念,包括使用系统调用进行文件读写的示例代码,并探讨了文件描述符fd和FILE结构体的工作原理,以及它们在文件操作中的作用。

学习文件描述符fd和FILE结构体前,我们先了解一下系统I/O
(1)系统文件I/O
我们学习C语言的时候,通过fopen(),fclose(),fread(),fwrite()等 I/O函数来操作文件,同样的,我们也可以采用系统接口open、close、write、read等来进行文件访问。

往文件里写
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
int main()
{
    umask(0);
    int fd = open("myfile",O_WRONLY|O_CREAT,0644);
    if(fd < 0)
    {
        perror("open");
        return 1;
    }

    int count = 5;
    const char* msg = "hello world\n";
    int len = strlen(msg);

    while(count--)
    {
        write(fd,msg,len);
    }

    close(fd);
    return 0;
}
读文件
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>

int main()
{
    int fd = open("myfile",O_RDONLY);
    if(fd < 0)
    {
        perror("open");
        return 1;
    }

    char buf[1024];
    char *msg = "hello world\n";
    int len = strlen(msg);
    while(1)
    {
        ssize_t s = read(fd,buf,len);
        if(s > 0)
        {
            printf("%s",buf);
        }
        else
            break;
    }
    close(fd);
    return 0;
}

这里写图片描述
通过上图我们可以很明显的看出系统调用接口和库函数的关系,可以认为,f#系列的函数,都是对系统调用的封装,方便二次开发。
(2)文件描述符fd
当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件,即file结构体,来表示已经打开的文件。进程执行open系统调用时必须让进程和文件关联起来。每个进程都有一个*file指针,指向一张表files_struct,这张表包含一个文件指针数组,每个元素都是一个指向打开文件的指针。所以,文件描述符本质上是文件指针数组的下标。只要拿着fd,就可以找到对应的文件。
这里写图片描述
从上面这张图可以看出文件描述符fd是一个从0开始的小整数。
那当我们关闭标准输入0时,重新打开一个文件,它的fd是多少呢?

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

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

    close(fd);
    return 0;
}

结果:
这里写图片描述
我们可以得出文件描述符的分配规则:在files_struct数组当中,找到没有被使用的最小一个下标,作为新的文件描述符。而当关闭标准输出1时,会发生重定向,他将本来应该显示在屏幕上的内容显示到了一个打开的文件中。因为底层访问文件时,找的还是fd:1,而此时fd:1下标所表示的内容已经变成了一个新打开文件的地址,而不再是显示器的地址,所以会发生重定向。
(3)FILE结构体
从上面介绍我们可以得出访问文件都是通过fd访问的,所以file结构体里面一定封装了fd。那file里面还封装了什么?
先来看段代码:

#include<stdio.h>
#include<string.h>

int main()
{
    const char* msg1 = "hello fprintf\n";
    const char* msg2 = "hello fwrite\n";
    const char* msg3 = "hello writre\n";

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

    fork();
    return 0;
}

结果:
这里写图片描述
但是对进程实现输出重定向后,发现结果变成:
这里写图片描述
除了write其他都输出了两次,这是为什么?
其实,这就牵扯到了缓冲区的概念。缓冲数据有三种方式:无缓冲、行缓冲和全缓冲。当数据写入显示器时是行缓冲,遇到 “\n”就输出,所以打印了三条语句。而往文件里面写入时是全缓冲,fork后,父子数据写时拷贝,当父进程准备刷新的时候,子进程有了相同的一份数据,随机产生两份数据,等进程退出后统一刷新,这两份数据都写入了文件中。
从上述例子可以看出,fwrite,printf库函数自带缓冲区,而write系统调用没有,足以说明,该缓冲区是二次加上的,又因为是C,所以由C库提供。
所以,C库当中的FILE结构体内部一定也封装了缓冲区。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值