目录
前言:
首先来回顾一下open函数,即在进程中同时打开多个文件:
Linux底层进程与文件的关系 :
2.1关闭stdin:
运行结果:
编辑由结果知:fd1指向文本文件cyq.txt,原本fd1是默认被fd_array[2]指向的,现在fd1却被fd_array[0]指向了。
2.2关闭stderr
运行结果:
2.3关闭stdout :
查看该文件cyq.txt:
运行结果:编辑
总结:
2.4重定向函数dup2()的学习
2.4.1函数作用:
2.4.2.参数
2.4.3返回值问题:
2.4.4案例——输出重定向:一般用于写内容到指定的地方
运行结果:
运行结果:
一次运行:
二次运行:
2.4.6输入重定向案例:一般用于从指定的地方读取内容。
运行结果:编辑
前言:
之前,我讲解了Linux下文件的特性、讲解了关于Linux关于文件的系统调用函数open、write、read、close函数,讲解了它们与C语言库中fopen、fclose、fgets、fputs等函数的区别,其实fopen、fclose、fgets、fputs这些函数是在Linux系统调用函数的底层原理下封装形成的优化C库函数。
Linux文件基础IO的理解1
接下来,我将继续深入讲解Linux中文件的一些底层知识。
首先来回顾一下open函数,即在进程中同时打开多个文件:
#include<errno.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>
#define Filename1 "big1.txt"
#define Filename2 "big2.txt"
#define Filename3 "big3.txt"
#define Filename4 "big4.txt"
int main(){
//同时打开多个文件
int f1=open(Filename1,O_CREAT| O_WRONLY |O_TRUNC,0664);
int f2=open(Filename2,O_CREAT| O_WRONLY |O_TRUNC,0664);
int f3=open(Filename3,O_CREAT| O_WRONLY |O_TRUNC,0664);
int f4=open(Filename4,O_CREAT| O_WRONLY |O_TRUNC,0664);
//打印这些文件描述符的值
printf("f1: %d\n",f1);
printf("f2: %d\n",f2);
printf("f3: %d\n",f3);
printf("f4: %d\n",f4);
close(f1);
close(f2);
close(f3);
close(f4);
return 0;
}
运行结果:
从结果中,我们看到这4个文件描述符的值分别是3-6,那么有人会想为什么是从3开始往后呢?为什么不是从1开始,或者从0开始呢?
其实这就要说到标准流了! 我们在学C语言的过程中,相必从书中就已经了解到了多种流,它们分别是标准输入流stdin、标准输出流stdout、标准错误流stderr,这三种流分别对应键盘、显示器、显示器。stdin标准输入流就是通过键盘向计算机中写入数据的;stdout标准输出流是通过读取计算机中的正常运行得出的数据到显示器中,方便查看的;stderr标准错误流是读取计算机在运行过程中产生的错误信息到显示器中。
Linux下一切皆文件,那么这三个流也不例外,它们也是文件,而且它们三个是处于一直被打开的状态,因为每个进程的运行都离不开它们几个,于是stdin、stdout、stderr所代表的也是文件描述符,每个进程都有一个文件描述符表,它相当于一个数组,专门用于存放该进程打开的各个文件。这三个文件描述符就占据了数组的榜一榜二榜三位置,导致后面被进程打开的文件只能从数组索引位置3开始!
求证一下:
结果运行:
Linux底层进程与文件的关系 :
每个进程都有一个task_struct的结构体,该结构体中存放着该进程的各个属性,既然一个进程打开了多个文件,那么这几个被该进程打开的文件就必须要被管理和维护,所以task_struct的进程属性中就又多了一个属性:file_struct* files指针,该指针指向记录着该进程所打开的所有文件的结构体:files_struct。
Linux下一切皆文件的本质和 “先描述,再组织 ” 的核心管理理念,使得我们能够清楚的了解到这个结构体中包含着每个被打开文件的各个属性,其中的成员file* fd_array[ ]指针数组便是存放着文件的各个文件描述符。所以本质上,文件描述符就是指针数组的下标。所以进程和被打开文件的关系就是通过文件描述符表files_struct映射的。
二.
既然stdin,stdout,stderr这三个流的类型也是文件,那么意味着它们可以被close,假如我们关闭了file* fd_array[ ]数组中的前3个标准流,会发生什么?
2.1关闭stdin:
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(){
close(0); //关闭stdin流
int fd1=open("cyq.txt",O CREAT O WRONLY O TRUNC,0666);
if(fd1<0){
perror("open");
exit(-1);
}
printf("open fd1: %d\n", fd1);
close(fd1);
return 0;
}
由上面代码可知:关闭了文件描述符表中数组的首元素,意味着fd_array[0]不再指向stdin——键盘,那么该数组的首元素为空。
运行结果:
由结果知:fd1指向文本文件cyq.txt,原本fd1是默认被fd_array[2]指向的,现在fd1却被fd_array[0]指向了。
2.2关闭stderr
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(){
close(2); //关闭stderr流
int fd1=open("cyq.txt",O CREAT O WRONLY O TRUNC,0666);
if(fd1<0){
perror("open");
exit(-1);
}
printf("open fd1: %d\n", fd1);
close(fd1);
return 0;
}
运行结果:
根据这个案例的结果,我们可以证出了一些东西:假如当fd_array[ ]数组中有某个指向文件的描述符下标2被关闭了,变成空闲的了,那么后面的文件描述符指向新的文件时,就会由描述符下标2去指向新文件。
一般来说,不关闭这3个默认的流时,fd默认是在指针数组fd_array[ ]的下标3处开始分配,一旦关闭了这3个的某一个,那么fd就会被分配到数组中空闲的那一个下标位置处。
2.3关闭stdout :
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(){
close(1); //关闭stdout流
int fd1=open("cyq.txt",O CREAT O WRONLY O TRUNC,0666);
if(fd1<0){
perror("open");
exit(-1);
}
printf("open fd1: %d\n", fd1);
fprintf(stdout, "open fd:%d\n",fd1);
close(fd1);
return 0;
}
运行结果:
从结果可知:我们发现执行该程序时,没有给出任何结果,什么都没有显示!这是为什么?其实很好解释。因为我们关闭了stdout输出流,那么printf函数的内容也就不再从显示屏中输出了,且czq.txt的文件描述符放在了fd_array[1]中,所以printf的输出内容都打印到了cyq.txt文件中去,所以当我们执行该程序时才什么都不会显示。
查看该文件cyq.txt:
使用cat指令查看该文件的内容时,也是什么都没有显示出来,这又是为什么?
其实,printf的内容先是传输到了系统的缓冲区中,需要使用fflush(stdout);才会强制刷新缓冲区,将内容传到文件中!
运行结果:
总结:
fd的分配规则:
从小到大,自顶向下的,且该处数组下标处没有被使用的位置为其分配fd地址。
这就是所谓的“重定向”!改变数据的流向,让原本写入A文件的数据不再写入A中,而是写入指定的B文件中。而上面的几个案例是:让原本写入流中的数据,写入指定的文本文件中。
但是这几个案例的方式有些低效率!是需要先关闭数组的某个下标处的元素,然后重定向新的文件描述符到该下标处。
2.4重定向函数dup2()的学习
dup2();
2.4.1函数作用:
该函数的作用就是直接改变指针数组中下标处元素的数据流向。
2.4.2.参数
最常用的是dup2函数接口,参数oldfd是当前新建文件的文件描述符下标处(源目的地),newfd是要拷贝到的指定的文件数组下标处(最终目的地)。
2.4.3返回值问题:
若调用成功,则成功改变数组的下标处数据指向;若失败,则返回-1
2.4.4案例——输出重定向:一般用于写内容到指定的地方
#include<std1o.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<fcntl.h>
#include<errno.h>
#include<unistd.h>
#include<stdlib.h>
int main(){
int fd2=open("cyq.txt",0_CREAT O WRONLY O TRUNC ,0666);
if(fd2<0){
perror("open file");
exit(-1);
}
//方法2:
dup2(fd2,1); //将old fd,拷贝到new fd中
printf("open fd2:%d\n",fd2); //打印到文件中了
fprintf(stdout,"open fd:%d\n",fd2);
int cnt=3;
while(cnt){
printf("hello my girl,%d\n",cnt--);
}
//因为内容在缓冲区中,必须强制刷新出来
fflush(stdout);
close(fd2);
return 0;
}
运行结果:
结果解析:虽然dup2函数改变了fd_array[1]的数据指向到了文本文件中,但fd_array[3]的数组元素也是指向该文本文件的,所以fd2的值为3是正确的。
使用dup2(fd,1); 函数造成的结果和使用close(1); 造成的结果是一样的,都是切断了从显示屏中输出的内容转而到了指定文件中。
2.4.5案例——追加重定向(是输出重定向的一种):
该案例在输出重定向的基础下只改变了open函数的O_APPEND属性。其他不变。
运行结果:
一次运行:
二次运行:
2.4.6输入重定向案例:一般用于从指定的地方读取内容。
int fd2=open("cyq.txt",O_RDONLY);
if(fd2<0){
perror("open file");
exit(-1);
}
dup2(fd2,0); //输入重定向
char str[64];
while(1){
printf(">: ");
if(fgets(str,sizeof(str),stdin)==NULL){
break;
}
printf("%s",str);
}
解析:输入重定向是从改变数组元素指向的stdin流,转而指向文本文件,之后我们便可以读取该文件的内容。
运行结果:
以上就是Linux系统中重定向的含义和两种做法。