fd(文件描述符)与FILE结构体

文件描述符fd
通过对open函数的学习我们现在知道了文件描述符就是一个小整数,从0开始。是文件指针数组的下标。
0&1&2

    #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <fcntl.h>
  5 #include <string.h>
  6 
  7 int main(){
  8     char buf[1024];
  9     printf("fd\n");
 10     ssize_t s =read(0,buf,sizeof(buf));
 11     if(s>0){
 12         buf[s]=0;
 13         printf("fd1\n");
 14         write(1,buf,strlen(buf));
 15         printf("fd2\n");
 16         write(2,buf,strlen(buf));
 17         printf("fd3\n");
 18         write(3,buf,strlen(buf));
 19     }
 20     return 0;
 21 }
 22 

执行的结果如下:
这里写图片描述
linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0,标准输出1,标准错误2。然而3不是缺省打开的文件描述符。
0、1、2对应的物理设备一般是:键盘,显示器,显示器
文件描述符是如何找到对应的文件的
这里写图片描述
Linux下大尅文件就会获得文件描述符,它是一个很小的整数,每个进程在PCB中保存着一份文件描述符表,文件描述符就是这个表的索引,,每个表项都有一个指向已打开文件的指针,文件指针,C语言中使用文件指针作为IO的句柄。文件指针指向进程用户区中一个被称为FILE结构的数据结构。FILE结构包括一个缓冲区和一个文件描述符,而文件描述符就是文件描述符表的一个索引,因此从某种意义上来说,文件指针就是句柄的句柄(在Windows系统上文件描述符被称为文件句柄)。
每个进程都有一个PCB,每一个PCB中都包含一个file指针,它指向一个文件结构体,结构体中包含了一个文件指针数组,每个元素都包含了一个指向打开文件的指针!,所以文件描述符就是该文件指针数组的下标,因此,我们只需要知道文件描述符,就可以通过文件描述符去找到指针指向的文件。
文件描述符的分配规则

在file_struct结构体中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
文件描述符也是一种资源,这种分配规则可以更好的利用资源。

文件描述符的有效范围是 0 到 OPEN_MAX。一般来说,每个进程最多可以打开 64 个文件(0 — 63)。对于 FreeBSD 5.2.1、Mac OS X 10.3 和 Solaris 9 来说,每个进程最多可以打开文件的多少取决于系统内存的大小,int 的大小,以及系统管理员设定的限制。Linux 2.4.22 强制规定最多不能超过 1,048,576 。

1)当没有关闭默认文件描述符时,新文件的文件描述符等于3。
2)当关闭了0时,新文件的文件描述符时0;
3)关闭使用close(0)
重定向
概念:就是各种请求重新定个方向转到其他位置。
分类:输出重定向(>)、输入重定向(<)、追加重定向(>>)

 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <fcntl.h>
  5 #include <stdlib.h>
  6 
  7 int main(){
  8     close(1);
  9     int fd=open("myfile",O_WRONLY|O_CREAT,0644);                                                     
 10     if(fd<0){
 11         perror("open");
 12         return 1;
 13     }
 14     printf("fd:%d\n",fd);
 15     fflush(stdout);
 16 
 17     close(fd);
 18     exit(0);
 19 }
~                                                                                                                    

根据分配原则,新打开的文件接收到的文件描述符是1,但是结果是咋样呢?
这里写图片描述
我们发现本来输出到显示器上的内容,输出到了文件myfile中,其中fd=1,这种现象叫做输出重定向。
那输出重定向的本质是什么?
这里写图片描述
printf是c库当中的IO函数,一般往stdout中输出,但是stdout底层访问文件的时候,找的换是fd:1,但是此时,fd:1下标所表示内容,已经变成了myfile的地址,不再是显示器的文件的地址,所以,输出任何消息都会往文件中写入,进而完成输出重定向。
FILE结构体
1、FILE是在stdio.h定义的保存文件流信息的一个结构体类型。
2、本质上访问文件都是通过fd访问的,库函数封装了fd,所以,FILE结构体内部肯定封装了fd。
3.fork缓冲问题。

//行缓冲:比如写入显示器中,输入的数先进入行缓冲区,当满足条件时在进一步输出到屏幕。
//全缓冲;比如写入文件中。
//无缓冲
①使用三种操作 方式(库函数、系统调用)往显示器上打印数据。

 1 #include <stdio.h>
  2 #include <string.h>
  3 
  4 int main(){
  5     const char *msg0="hello printf\n";
  6     const char *msg1="hello fwrite\n";
  7     const char *msg2="hello write\n";
  8 
  9     printf("%s",msg0);
 10     fwrite(msg1,strlen(msg0),1,stdout);
 11     write(1,msg2,strlen(msg2));
 12 
 13     fork();
 14     return 0;
 15 }
 16                                                                                       
~                      

运行结果如下所示:
这里写图片描述

②但是如果对进程实现输出重定向呢?我们发现结果变成了:
这里写图片描述
我们发现printf和fwrite(库函数)都输出了2次,而write只输出了一次(系统调用)为什么呢?这与fork有关。
为什么会出现这种情况呢?不应该都是出现两次吗?
答:当进程输出重定向到新文件时,数据的缓冲方式由行缓冲变为了全缓冲,printf与fwrite(库函数自带缓冲区)先进入了全缓冲区,不会立即被刷新;write是系统调用,没有缓冲区,直接输出到了屏幕,当下一步执行fork()函数时,子进程的执行起点为fork之后,但此时父子数据会发生写时拷贝,所以当父进程全缓冲区准备刷新的时候,子进程也有了同样的一份数据,随机产生两份数据。那为什么没有write数据呢?因为写时拷贝,只有在写入时,数据才被复制。
所以当父进程全缓冲区准备刷新的时候,子进程也有了同样的一份数据,随机产生两份数据。那为什么子没有write数据呢?因为写时拷贝,只有在写入时,数据才被复制。

一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲的。

printf、fwrite库函数会自带缓冲区(进度条例子就可以说明)当发生重定向到普通文件时,数据的缓冲方式由行缓冲变为了全缓冲。
而我们放在缓冲区的数据,就不会立即被刷新,甚至fork之后。
但是进程退出后,会统一刷新,写入文件当中。
但是fork的时候,父子进程数据会写时拷贝,所以当你父进程准备刷新的时候,子进程也有了同样的一份数据,随即产生两份数据。
write没有变化,说明没有所谓的缓冲。

FILE结构体

#ifndef _FILE_DEFINED
struct _iobuf
{
       char *_ptr; //文件输入的下一个位置 
    int _cnt; //当前缓冲区的相对位置 
    char *_base; //指基础位置(即是文件的其始位置) 
    int _flag; //文件标志 
    int _file; //文件的有效性验证 
    int _charbuf; //检查缓冲区状况,如果无缓冲区则不读取 
    int _bufsiz; //缓冲区大小 
    char *_tmpfname; //临时文件名
};
typedef struct _iobuf FILE;

上面结构体中的 _file 实际上是一个描述符,作为进入打开文件表索引的整数。
就是我们说的文件描述符。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值