父进程和子进程使用的文件

父进程和子进程使用的文件
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>


int main( int argc, char *argv[] ){


    FILE *fptr;
    pid_t pid;

    fptr = fopen("Shared File.txt", "a");
    pid = fork();

    if( pid > 0 ){ // parent process

        int counter = 0;

        while( counter < 10 ){
            fprintf(fptr, "a");
            ++counter;
        }
        wait(NULL);
    }
    else{

        int counter = 0;

        while( counter < 5 ){
            fprintf(fptr, "b");
            ++counter;
        }
    }

    return 0;
}

当我执行此代码时,代码生成的文件包含以下消息:bbbbbaaaaaaaaaa

每当我执行此代码时,我都会收到相同的消息。为什么进程不按打乱顺序写入文件?

为什么操作系统首先尝试结束子进程?

我对消息的期望是这样的: baabbaaabaaabaa 进程之间没有连续的过渡。

答案1

父子之间的调度已经(至少)在子进程执行时fork系统调用到底是如何工作的

但在这种情况下,还存在stdio.您正在使用fprintf()写入常规文件。默认情况下,stdio将输出缓冲到常规文件,直到写入足够的数据,以节省系统调用开销。在 x86 Linux 上,它通常似乎以 4096 字节块写入,但你不能指望这一点,除非你手动设置缓冲(请参阅setbuf()和朋友)。

strace您可以使用类似显示程序进行的系统调用的命令来查看这一点。

因此,虽然您无法预测哪个进程首先运行,但在这种情况下,您可以预测 sa是连续写入的,并且bs 也是如此。你只能得到bbbbbaaaaaaaaaaaaaaaaaaaabbbbb,而得到哪一个几乎取决于运气。

答案2

我基本上同意@ikkachu,除了

你会得到哪一个bbbbbaaaaaaaaaa或者aaaaaaaaaabbbbb是可以预测的。操作系统等待子进程完成,因为wait(NULL),然后父进程退出。当缓冲区在退出时被刷新时,子进程首先开始写入。

但是,不要依赖可预测的缓冲区。需要时使用显式刷新。

答案3

用户 ilkkachu 很好地解释了缓冲如何影响输出。我的答案描述了如果您消除缓冲(例如通过将fprintf调用替换为对write.在这种情况下,您将得到严格交替的as 和bs。这是因为对 的调用write会导致重新调度:在一个进程中写入会阻塞,然后将轮流交给另一个进程,依此类推。

让我们想象一下如果调用 write 会发生什么不是堵塞。然后我们必须考虑时间尺度:您将获得as 和bs 的运行时间,而不仅仅是一次运行一两个,因为现代处理器能够每秒执行数十亿条指令,但调度频率通常在 100 Hz 之间和 1000 赫兹。一个进程在被抢占并且另一个进程被调度运行之前最多能够执行数千万条指令。即使考虑到系统调用开销,这也会给进程时间来打印非常长的连续as 或bs 字符串。

答案4

ikkachu 和 Johan 很好地解释了为什么你会观察到你所做的行为,所以我稍微重写了你的程序,以便每个进程刷新流并休眠一秒钟(这样每个线程每次都有机会以不同的方式交错),这样你就可以更清楚地看到交错效果。

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void) {
  FILE *fptr = stdout;
  pid_t pid;
  int counter = 0;

  pid = fork();

  if(pid > 0) { // parent process
    while(counter++ < 10) {
      fprintf(fptr, "aa");
      fflush(fptr);
      sleep(1);
    }
    wait(NULL);
    puts("");
  } else {
    while(counter++ < 5) {
      fprintf(fptr, "bbbb");
      fflush(fptr);
      sleep(1);
    }
  }
}

如果您运行多次,您偶尔会得到不同的结果:

~ $ ./thread
aabbbbaabbbbaabbbbaabbbbaabbbbaaaaaaaaaa
~ $ ./thread
aabbbbaabbbbaabbbbaabbbbaabbbbaaaaaaaaaa
~ $ ./thread
aabbbbaabbbbaabbbbaabbbbbbbbaaaaaaaaaaaa
~ $ ./thread
aabbbbaabbbbaabbbbaabbbbaabbbbaaaaaaaaaa

相关内容