fd与FILE结构体

前言:
fopen,fclose,fwrite,fread属于C库当中的函数,为库函数调用。而open,close,write,read这几个函数属于系统提供的借口,称之为系统调用。库函数和系统调用是上下级关系,在库函数中封装了系统调用的函数。(库函数为啥都有的大哥,系统调用就是有一部分的小弟)

一、文件描述符fd

1、简介
文件描述符就是以0开始的一个小整数(文件描述符无负数)
而且Linux进程默认情况下会有三个缺省打开的文件描述符:标准输入(stdin)、标准输出(stdout)、标准错误(stderror)。它们三个所对应的整数为0、1、2,对应的物理设备为键盘、显示器、显示器

当我们打开一个文件的时候,操作系统在内存中会创建相应的数据结构来描述这个文件,所以就有了file结构体。当进程执行open系统调用的时候,就需要将文件和进程关联起来,每个进程都有一个指针files*,指向表files_struct,表中最重要的就是包含指针数组,本质上,文件描述符就是该指针数组的下标。只要能够拿到文件描述符(即下标),就可打开对应的文件。

这里写图片描述

2、文件描述符(fd)的分配规则

  • 在Linux下打开一个文件时,POSIX标准要求每次打开时必须使用当前进程中最小的文件描述符(非负整数)。操作系统会默认将0、1、2分别给标准输入、标准输出和标准错误,此时要打开一个文件时,系统就会将files_struct数组中下标最小的3开始分配。

  • 倘若关闭0,此时打开两个个文件,则第一个被打开文件的文件描述符为0,第二个文件的文件描述符为3。关闭2也是如此,但不要轻易关闭1,这样就无法在屏幕上输出了。(大家可以自行验证一下)

二、FILE 结构体
FILE 结构体里面有 两个非常重要的字段:文件描述符(fd)和缓冲区
1、文件描述符
可见上面具体内容

2、缓冲区

先来看一段代码:

test.c
#include <stdio.h>
#include <string.h>
 int main()
 {
      const char *msg1 = "beautiful gril printf\n";
       const char *msg2 = "beautiful gril fwrite\n";
       const char *msg3 = "beautiful gril write\n";

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

     fork();
     return 0;
 }

这里写图片描述
这里写图片描述
通过上述结果发现file文件里的printf和fwrite函数都输出了两遍,而write函数只输出了一遍。这是为什么嘞?
解释:
因为一般C库函数有三种类型的缓冲区:一是无缓冲;二是写入文件的时候都是全缓冲;三是写入显示器是行缓冲。printf函数和fwrite函数都是C库函数,自带缓冲区,当发生重定向到普通文件的时候,数据的缓冲方式会由行缓冲变为全缓冲,而我们放进文件的数据不会被立即刷新(甚至是在fork之后),在进程退出之后,会统一刷新写入到文件中。但是在fork之后,父子数据会发生写时拷贝,即当父进程刷新时,子进程也就有了同样的数据,所以就会产生两份数据。而write函数为系统调用,无缓冲区。

阅读更多

没有更多推荐了,返回首页