【Linux】浅析文件描述符fd与文件指针FILE*

我们先来回顾几个代码。。。。。

在C语言中往一个文件中写入内容我们这样写(C文件接口)

int main()
{
	FILE* fp = fopen("myfile", "r");
        //原型:FILE *fopen(const char *path, const char *mode);
	if (!fp)
	{
		printf("fopen error\n");
	}
	const char* msg = "hello world\n";
	int count = 5;
	while (count--)
	{
		fwrite(msg, strlen(msg), 1, fp);
	}
	fclose(fp);
	return 0;
}
今天我们来看如何让用系统接口来实现王文件中写入内容,代码实现如下:
#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("ly_file",O_WRONLY|O_CREAT,0644);
     // int open(const char *pathname, int flags);
      if(fd<0){
         perror("open");
          return 0;
      }
      int count=3;
      const char* arr="hello luying\n";
      while(count--){
          write(fd,arr,strlen(arr));                                                                                        
     }
      close(fd);
      return 0;
  }

上面是分别用库函数和系统调用接口实现的对文件的操作,你是不是很纳闷,为什么我们会说这么多题外话,别急,我们慢慢来接近今天的主题

上面的代码可不是我瞎敲的,都是有目的的,嘻嘻嘻。。。。

我们先来熟悉两个概念《系统调用》和《库函数libc》

系统调用提供的函数有open、close、read、write、ioctl等,包含的头文件是unistd.h

拿其中一个函数来举例说明吧,比如 write: 原型:size_t write(int fd,const void* buf,size_t nbytes);他的操作对象是文件描述符或者文件句柄(file descriptor)要想写一个文件,必须先以可写权限用open系统调用打开一个文件,获得所打开文件的fd即int fd=open("myfile",O_WRONLY|O_CREAT,0644)(仅仅是一个例子,具体参数根据实际应用并参考man手册参数说明进行选择)

库函数提供的函数有fopen、fclose、fread、fwrite、lseek等,包含的头文件为stdio.h

其实库函数对文件的操作实际上是通过系统调用来实现的,也就是说fwrite()是通过write()实现的;我们也举一个例子,就如fwrite:原型:size_t fwrite(const void* buffer,size_t size,size_t item_num,FILE* pf);其操作对象为文件指针FILE*pf,同样的,要想向一个文件中写入数据,就必须先以可写权限用fopen函数打开一个文件,获得所打开文件的FILE结构指针pf,例如FILE* fp=fopen("ly_text","w")(具体参数参考man手册)      

通过open函数的学习,我们知道了文件描述符实际上是一个小整数,我们还都知道在Linux下一切皆文件。在linux下我们打开一个文件就会获得该文件的文件描述符fd( file discriptor), 每个进程在PCB(Process Control Block)中保存着一份文件描述符表,文件描述符就是这个表的索引,每个表项都有一个指向已打开文件的指针。 下面我们通过图解帮助分析一下两者的关系

【注意】

1.linux进程默认情况下会打开3个缺省打开的文件描述符,分别是标准输入(stdin)、标准输出(stdout)、标准错误(stdout),分别是0/1/2;分别对应的物理设备是键盘、显示器、显示器

2.文件描述符即使从0开始的小整数,当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件,所以就有了file结构体,表示一个已经打开的文件对象

3.进程执行open系统调用,那么怎么让进程和文件关联起来呢?每个进程都有一个指针*files,指向一份文件描述符表,(file_struct),如图中所示,文件描述符的本质就是文件指针数组的下标

文件指针(FILE*)

文件指针指向进程用户区中一个被叫做FILE结构的结构数据。FILE结构包括一个缓冲区(另作分析)一个文件描述符 。 

c程序用不同的FILE结构体管理每个文件。程序员可以使用文件,但是不需要知道FILE结构体的细节。实际上,FILE结构体是间接地操作系统的文件控制块(FCB)来实现对文件的操作的。 
FILE结构体中的_file ,即就是文件描述符,作为进入打开文件表索引的整数。

因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件就是通过fd访问的,也就是说C库当中的FILE结构体内部,必定封装了文件描述符fd

如此,是不是能够清楚一点了呢?

文件描述符的分配原则

我们来实现两个代码,分别观察一下运行结果

  #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("ly_file",O_WRONLY|O_CREAT,0644);
      // int open(const char *pathname, int flags);
      if(fd<0){
          perror("open");
          return 1;
      }
      printf("fd: %d\n",fd);                                                                           
      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()
   {
       close(0); //我们关闭0 号下标                                                                                      
      int fd=open("ly_file",O_WRONLY|O_CREAT,0644);
      // int open(const char *pathname, int flags);
      if(fd<0){
          perror("open");
          return 1;
      }
      printf("fd: %d\n",fd);
      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()
   {
       close(2);  //关闭2号下标                                                                                      
      int fd=open("ly_file",O_WRONLY|O_CREAT,0644);
      // int open(const char *pathname, int flags);
      if(fd<0){
          perror("open");
          return 1;
      }
      printf("fd: %d\n",fd);
      close(fd);
      return 0;
  }

运行结果:


验证可得,文件描述符的分配规则是:在file_struct数组当中,找到当前文件没有被使用的最小的一个下标,作为新文件的文件描述符

至此,我们粗糙的区分了文件描述符fd以及文件指针

【小结】

创建一个进程时,相应的也就创建了文件描述符表, 

进程控制块PCB中的*file指针指向文件描述符表, 

创建文件时,会为指向该文件的指针FILE*关联一个文件描述符并添加在文件描述符表中。 

在文件描述符表中fd相当于数组的下标,FILE*相当于数组的内容,指向一个文件结构体。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值