在学习文件描述符之前,我们先来看一点其它相关的知识点。
我们都知道,对一个文件我们可以进行打开、读、写、关闭操作,在我们没有接触Linux之前,将信息输出到显示器上,我们通常能想到的方法有以下几种:
#include<stdio.h> #include<string.h> int main() { char *msg = "hello world!\n"; fwrite(msg, strlen(msg), 1, stdout); printf("hello world!\n"); fprintf(stdout, "hello world!\n"); return 0; }
C语言会默认帮我们打开stdin、stdout、stderr这三个流,并且,这三个流的类型都是FILE*型的。
其实,上面三种方式,我们都是调用的C标准库函数,但在Linux中也有这些操作,而它们都没有带“f”(例如open、read、write、close),这些实质上是系统调用接口,相当于被库函数进行了一次封装。
接下来,我们来总结一下“文件描述符”的相关知识点!
一、文件描述符
(1)概念
1.所谓文件描述符,其实就是一个小整数;
2.Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0,标准输入1,准本错误2;
3.0,1,2,对应的物理设备一般是:键盘、显示器,输入、输出还可以采用如下方式;
1 #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 { 9 char buf[1024]; 10 ssize_t s=read(0,buf,sizeof(buf)); 11 if(s>0){ 12 buf[s]=0; 13 write(1,buf,strlen(buf)); 14 write(2,buf,strlen(buf)); 15 } 16 return 0; 17 }
(2)相关图
从这张图,我们不难看出,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述文件,于是就有了file的结构体,此时已经打开相应文件对象。
而且从上图不难看出,文件描述符本质上就是数组的下标,因此,只要拿着文件描述符,就可以找到对应的文件
(3)文件描述符的分配规则
下面我们来看一段代码
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<fcntl.h> 5 6 int main() 7 { 8 int fd=open("myfile",O_RDONLY); 9 if(fd<0){ 10 perror("open"); 11 return 1; 12 } 13 printf("fd:%d\n",fd); 14 return 0; 15 }
输出结果:fd:3。这也就验证了上面所说的一句话,操作系统会默认打开0,1,2,三个文件,存放stdin、stdout、stderror、当我们再打开一个文件时,它的文件描述符会从3开始。
其实,除了这种情况以外,还有一种情况,当关闭了0、1、2三个文件时,下一次当操作系统打开一个新的文件时,会默认从文件描述符没有使用的最小下标开始。(例如关闭了0,下一次打开一个新的文件时,该新文件描述符为0),说到这里,我们很容易产生一些疑问,这是怎么回事,下面,我们再来看一个概念来解决我们的疑惑吧!
二、重定向
1.常见的重定向有:>(输出重定向)、>>(追加重定向)、<(输入重定向)
2.以输出重定向为例,我们来看一段代码:
1 #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 { 9 close(1); 10 int fd=open("myfile",O_WRONLY|O_CREAT,0644); 11 if(fd<0){ 12 preeor("open"); 13 return 1; 14 } 15 printf("fd:%d\n",fd); 16 fflush(stdout); 17 close(fd); 18 exit(0); 19 20 }
我们会发现,本应该输出到显示器上面的内容,却输出到了文件myfile当中,其中文件myfile的fd为1,这就是所谓的输出重定向。
3.重定向的本质
一般printf往stdout中输出,但是stdout在底层访问文件的时候,找的还是fd=1,但此时fd:1下标所表示的内容,已经变成了myfile的地址,不再是显示器文件的地址,所以,输出的任何消息都会往文件中写入,进而完成输出重定向。
通过上面的学习,我们都知道访问文件都是通过fd访问的,因此C标准库中的FILE结构体内部,必定封装了fd。