在linux底层环境下理解重定向的本质和缓冲区是什么

目录

重定向的本质

1.重定向的现象

2.dup2接口

3.追加重定向

4.输出重定向  

缓冲区

1.缓冲区是什么


重定向的本质

1.重定向的现象

        首先写一段代码展现出重定向的现象。

  1 #include <stdio.h> 
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <unistd.h>
  5 #include <fcntl.h>
  6 
  7 int main()
  8 {
  9   int fd = open("test.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
 10   
 11   if(fd < 0)                                                                                           
 12   {                
 13     perror("open");
 14     return 1;
 15   }
 16                                                       
 17   fprintf(stdout,"打开成功,我的文件描述符是%d\n",fd);
 18                                                       
 19   close(fd);
 20             
 21   return 0;
 22 }

        我们都知道文件描述符的规则,不晓得的可以去看一眼我上一篇博客。将最小的没使用的数组下标,分配给文件,所以我们这时的fd(文件描述符)是3,因为我目前只打开了一个文件。

        我们这时可以关闭stdin(fd=0)这个文件,则给我们的test.txt分配的fd则是0,这毋庸置疑。 

         那我们这时关闭stdout(fd=1)这个文件,那么显示器上输出什么呢?还会不会是“打开成功,我的文件描述符是1”。 

结果:

        为什么什么都没打印?原先我的文件stdout作为标准输出fd是指向显示器的,我把fd这时关了,将它分给了新的文件,那么这时的fprintf会将输出到显示器的内容输出到了文件内,看看我们的分析对不对。

        为什么文件里还没有?完蛋讲错了?

         我们可以刷新一下缓冲区,神奇的结果出来咯。至于为什么呢,下面讲到缓冲区时会讲到。

        这种往一个地方打印输出,最终变成朝指定文件打印的现象,不就是重定向嘛。

        我们都知道stdout是个FILE*的指针指向一个struct FILE,这个结构体其中封装了fd(1),我们这时只改变了fd(1)的所属,它变成了另外一个文件的文件描述符。也就是上层只关注文件描述符,只通过文件描述符(0,1,2,3,4,5...)进行文件操作,不管下层指向的文件是什么。所以,我们如果要进行重定向,我们可以通过os中某种接口调整fd指向的文件,就可以完成重定向。

2.dup2接口

        我们来见识一下这个接口。

 作用:

        让新的fd成为旧的fd的copy,如果必要的话可以先把新的fd关了,但是注意以下情况:如果旧的fd不是有效的文件描述符,这个调用将会失效,新的fd将会被关闭。如果旧的fd是有效的文件描述符,新的fd和旧的fd如果值相同,这个函数将什么也不做,返回新的fd。

         我们想让输出到显示器的内容输出到test.txt中,怎么去使用dup2这个函数,究竟谁是newfd谁是oldfd呢?

        可以这样理解,既然是拷贝(注意这里的拷贝不单单是两个数来拷贝而是fd对应文件关系的拷贝),那么我想让这俩fd都指向文件,也就是1作为newfd来拷贝oldfd,fd就是oldfd,所以就要这样传参:

dup2(fd,1);

代码如下:

结果:而且我们的文件描述符也为3了,注意我们现在没有close(1)。

        这样也能重定向,总之不要管什么newfd和oldfd,重定向的去向放第一个参数,被改变的放第二个参数。

3.追加重定向

        至于追加重定向更简单,在open时,传标记位时传

int fd = open("test.txt",O_WRONLY | O_CREAT | O_APPEND,0666);

        再运行时就是追加重定向了。 

4.输出重定向  

       至于输出重定向呢,我们要从test.txt中读数据,所以dup2()中应该传fd,0

  9 int main()
 10 {
 11   int fd = open("test.txt",O_RDWR);
 13   if(fd < 0)
 14   {
 15     perror("open");
 16     return 1;
 17   }
 18   dup2(fd,0);
 19
 23   char a[128];
 24   while(fgets(a,sizeof(a),stdin) != NULL)
 25   {
 26     printf("%s",a);
 27   }
 28 
 29   fflush(stdout);                                                                                                                        
 31   close(fd);
 33   return 0;
 34 }

 结果如此:

缓冲区

1.缓冲区是什么

        缓冲区的本质就是一段内存,用来存储将要输出的内容。

2.缓冲区出现的原因

①解放使用了缓冲区的进程的时间。这个进程在cpu中进行读写操作时,要输出到对应硬件,需要去等待硬件,这个时间取决于这个硬件是否已经完成上一个任务,这个时间损耗是不可估量的。现在有了缓冲区,可以先将数据存到缓冲器中,进程可以继续运行下去,不用等待,等待缓冲区满了或者刷新了缓冲区就能输入到对应的硬件。

②减少对硬件的操作次数

3.缓冲区在哪儿

        先写段代码验证一下缓冲区在哪里

 

  1 #include <stdio.h>  
  2 #include <unistd.h>  
  3 #include <sys/types.h>  
  4 #include <sys/stat.h>  
  5 #include <fcntl.h>  
  6 #include <string.h>  
  7   
  8   
  9 int main()  
 10 {  
 11   printf("我是printf\n");//默认输出到显示器 std->1  
 12                                                                                                                                          
 13   const char * inform = "我是write\n";
 14   write(1,inform,strlen(inform));
 15   sleep(5);
 16 } 

        那我们把printf中的\n去掉会如何呢?

printf("我是printf");

        为什么呢?为什么\n一没有,write先打印呢?printf在其后打印。 

        难道是write带了\n的缘故? 我们去掉试一下。注意“我是write”打印完之后,等待了5秒之后,“我是printf”才打印出来。说明进程结束了,“我是printf”才打印出来。

        我都知道printf是封装了write的c语言函数,write立马打印,而封装了printf的函数没有立马打印,说明什么?这个缓冲区肯定不在wirte中,因为write直接打印无论带没带\n,而只有printf带了\n才能立马打印出来,不带\n刷新不了缓冲区才在程序结束后缓冲区刷新随后打印出“我是printf”。

        证明了什么?缓冲区不在内核(系统)层面(write是系统提供的接口),而在printf所在的语言层面。缓冲区只是C语言提供的。

        printf  fprintf  fputs 都是输出到stdout中,而stdout是个FILE*指针指向FILE结构体,这个结构体里就要我们要的缓冲区。

        既然缓冲区在struct里是否跟fd没关系,我们在刷新之前将fd关闭,会出现什么情况呢?

结果: 

 

        证明同一个结构体中的fd和缓冲区有关联 ,证明了我们上文的一个问题。我们上文在close(fd)之前就要刷新缓冲区,是因为缓冲区与fd有关,关闭了fd系统访问不到对应的缓冲区。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值