父级和子级都写入标准输出时,输出无交错

时间:2018-11-01 02:02:24

标签: c multiprocessing fork race-condition

我正在尝试编写代码,以查看如何使用fork创建子进程。由于子级从父级继承文件对象和描述符,因此如果子级和父级都向标准输出写入,那么如果我理解正确的话,输出中应该会有交错。在下面的代码中,我在父级和子级中都声明了两个字符串,并将它们写入stdout。我观察到的是输出中没有交错。我想念什么吗?

代码:

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

int main() {
  pid_t return_value;
  pid_t wait_p;
  if ((return_value = fork()) != 0) {
     char pa[15]= "Hi from parent\n";
     char pa_2[18]= "Hello from pariii\n";
     write(1, pa, 15);
     write(1, pa_2, 18);
  } else {
     char c[17] = "Hello from child\n";
     char c_2[14] = "Hi from chiii\n";
     write(1, c , 17);
     write(1, c_2, 14);
  }
  exit(0);
}

我机器上的输出(Ubutun 18.04,符合gcc):

Hi from parent
Hello from pariii
Hello from child
Hi from chiii

我了解到写入的原子性如何导致过程不交错。但是,为什么输出看起来像父级先执行所有写操作,然后子级执行其写操作呢?另外,无论我尝试了多少次,父母总是会先于孩子写。

2 个答案:

答案 0 :(得分:2)

  

输出中应该有交错

否,输出 可能是隔行扫描的。否则可能不会。这取决于调度程序或月相。

答案 1 :(得分:2)

import static java.lang.System.out; import java.util.Scanner; public class JavaApplication { public static void main(String[] args) { Scanner kb = new Scanner(System.in); //initialize String array x20 String[] regName = new String[20]; int regCount = 0; int func = 0; while (func == 0) //Main Menu Looper { out.println("Select function by entering its number."); out.println("[1] Insert"); out.println("[2] Search"); out.println("[3] Delete"); out.println("[4] Exit"); out.print("Choose Operation: "); func = kb.nextInt(); //Choose Option out.print("======================================"); out.print("\n"); switch (func) { case 1: //Insertion //set Array index start char yesNo; do { //Inserting into arrays loop out.print("Insert student last name: "); regName[regCount] = kb.next(); regCount++; out.print("\n"); //Viewing loop out.println("Student List: "); for (int ctrl = 0; ctrl < regCount; ctrl++) { out.println(regName[ctrl]); } out.print("\n"); //Question loop out.print("You want to insert again(Y/N):"); yesNo = kb.findWithinHorizon(".", 0).charAt(0); if (yesNo == 'y' || yesNo == 'Y') { yesNo = 'y'; } } while (yesNo == 'y'); func = 0; break; case 2: //Searching out.print("Enter keyword: "); String search = kb.next(); boolean found = false; int searchCount = 0; for (int ctrl = 0; ctrl < regCount; ctrl++) { if (regName[ctrl].equalsIgnoreCase(search)) { found = true; out.println(search + " has " + " a match."); } else { out.println(search + " has " + " not found."); } } out.print("\n"); func = 0; break; case 3: //Deleting out.print("type surname you want to delete: "); String toDelete = kb.next(); for (int ctrl = 0; ctrl < regCount; ctrl++) { if (regName[ctrl].equalsIgnoreCase(toDelete)) { regName[ctrl] = null; out.println("Record deleted."); } } out.print("\n"); func = 0; break; } //switch } //while } //main } //class 系统调用是 atomic ;也就是说,这一切都立即发生。字符串不可能交错。如果要分多个进行write调用(例如,编写字符串write,然后编写字符串"Hi from ""parent",然后编写换行符),那么您可能会看到交错。当您将整个消息作为单个字符串"child"时,将永远不会发生。

请注意,像write之类的更高级的调用具有更复杂的缓冲规则,因此可能具有不同的规则。


为回答有关为什么两个父行始终总是在两个子行之前的问题,这并不是很幸运,但不能保证。在具有相同设置的同一系统上,我希望无论是先安排父项还是先安排子项,在大多数情况下都是一致的,但是其他平台可能会做出相反的决定。

至于为什么它从不打印父级的一行,然后从子级的一行,然后从父级的第二行,我猜想它与调度程序的细节以及写入虚拟终端有关。从一个进程切换到另一个进程的上下文很昂贵;如果printf足够快,则调度程序可能会告诉它不应该这样做。也许如果您改为写光盘,并在write之间调用sync以确保确实(相对较慢的)光盘被卷入,则插入的可能性更大。也许不是;真的很难预测。如果您真的想看到交错,那么我会至少从父母和孩子那里写几千字节,一次写几千字节。几乎可以交错。