基础IO 六

98 阅读2分钟

“我正在参加「掘金·启航计划」”

✔ 测试用例一:

#include<stdio.h>  
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>

int main()
{
    //c call
    printf("hello printf\n");
    fprintf(stdout, "hello fprintf\n");
    fputs("hello fputs\n", stdout);
    //system call
    const char* msg = "hello write\n";
    write(1, msg, strlen(msg));
                                       
    fork();

    return 0;                          
}                                      
  • 这里的 fork 看起来没有什么价值,也不会影响到什么,fork 之后,父子进程马上就退出了。

    在这里插入图片描述

    但是当我们重定向后,就会出现很诡异的现象。

    在这里插入图片描述

    我们发现往显示器上输出的结果是合理的,但是往普通文件上输出的结果却很诡异,它输出了 7 条信息,且使用 C 语言接口的都输出了两次,使用系统调用接口的输出了一次。毫无疑问这种现象是和随手写的 fork 是相关联的,因为去掉 fork 再重定向是合理的。

    这里先考虑 C 语言接口,我们都知道,这里输出的三条数据并不会直接写到操作系统里,而是先写到 C 语言的缓冲区中,fork 之后,程序就分流了,但不幸的是,程序马上要退出了,而 C 语言缓冲区中的数据也要被刷新,此时父子进程谁先运行,谁就先刷新,而缓冲区中的数据也是数据,即使是 C 语言的数据,也不能凌驾于进程之上,当父或子想刷新时,那么立马要发生写时拷贝。至此,我们就能理解重定向后,刷新策略由行刷新变为全缓冲,也就是说 fork 时,数据还在 C 缓冲区中,所以重定向后,C 接口的数据输出了两份;而向显示器输出时,因为显示器的刷新策略是行刷新,且这里的每条数据都有 \n,所以每执行完 printf,数据就立马刷新出来,最后 fork 时便无意义了。

    而重定向后,系统接口没有受影响的原因是 write 会绕过语言层缓冲区,写到内核层缓冲区,而其实只要是数据都要写时拷贝,但大部分情况只针对用户数据,对于内核数据,数据属于操作系统不会写时拷贝,属于进程会写时拷贝,但这种情况很少考虑,现在我们就认为写时拷贝主要拷贝的是用户数据。

  • 通常我们不建议所语言接口和系统接口混合使用,因为可能会出现一些难以理解的现象。