Linux重定向与缓冲区

发布于:2025-05-15 ⋅ 阅读:(12) ⋅ 点赞:(0)

目录

文件描述符的分配规则

重定向

使用 dup2 系统调用

FILE


文件描述符的分配规则

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
int main()
{
 int fd = open("myfile", O_RDONLY);
 if(fd < 0){
 perror("open");
 return 1;
 }
 printf("fd: %d\n", fd);
 
 close(fd);
 return 0;
}

输出发现是 fd: 3

关闭0或者2,再看

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
int main()
{
 close(0);
 //close(2);
 int fd = open("myfile", O_RDONLY);
 if(fd < 0){
 perror("open");
 return 1;
 }
 printf("fd: %d\n", fd);
 
 close(fd);
 return 0;
}

发现是结果是: fd: 0 或者 fd 2 可见,文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

重定向

那如果关闭1呢?看代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
 
int main()
{
 close(1);
 int fd = open("myfile", O_WRONLY|O_CREAT, 00644);
 if(fd < 0){
 perror("open");
 return 1;
 }
 printf("fd: %d\n", fd);
 fflush(stdout);
 
 close(fd);
 exit(0);
}

此时,我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中,其中,fd=1。这种现象叫做输出重定向。常见的重定向有:>, >>,<。

那重定向的本质是什么呢?

实际上我们的上层是不会变的,也就是说操作系统还是只看fd,但是呢fd对应的文件结构体内容变了,指向了其它文件的地址就会发生这种效果。

使用 dup2 系统调用

函数原型如下:

#include <unistd.h>
 
int dup2(int oldfd, int newfd);
//int fd = open(filename,O_CREAT | O_WRONLY | O_TRUNC,0666);
    int fd = open(filename,O_CREAT | O_WRONLY | O_APPEND,0666);

    dup2(fd,1);

    printf("hello world\n");
    fprintf(stdout, "hello world\n");

printf是C库函数当中的IO函数,一般往 stdout 中输出,但是stdout底层访问文件的时候,找的还是fd:1, 但此时,fd:1 下标所表示内容,已经变成了log.txt的地址,不再是显示器文件的地址,所以,输出的任何消息都会往文件中写入,进而完成输出重定向。

FILE

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

来段代码在研究一下:

#include <stdio.h>
#include <string.h>
 
int main()
{
 const char *msg0="hello printf\n";
 const char *msg1="hello fwrite\n";
 const char *msg2="hello write\n";
 
 printf("%s", msg0);
 fwrite(msg1, strlen(msg0), 1, stdout);
 write(1, msg2, strlen(msg2));
 
 fork();
 
 return 0;
}

运行出结果:

hello printf
hello fwrite
hello write

但如果对进程实现输出重定向呢? ./hello > file , 我们发现结果变成了:

hello write
hello printf
hello fwrite
hello printf
hello fwrite

我们发现 printf fwrite (库函数)都输出了2次,而 write 只输出了一次(系统调用)。为什么呢?肯定和 fork有关!

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

综上: printf fwrite 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。另外,我们这里所说的缓冲区, 都是用户级缓冲区。其实为了提升整机性能,OS也会提供相关内核级缓冲区,不过不在我们讨论范围之内。 那这个缓冲区谁提供呢? printf fwrite 是库函数, write 是系统调用,库函数在系统调用的“上层”, 是对系统 调用的“封装”,但是 write 没有缓冲区,而 printf fwrite 有,足以说明,该缓冲区是二次加上的,又因为是 C,所以由C标准库提供。


网站公告

今日签到

点亮在社区的每一天
去签到