我正在尝试编写代码,以查看如何使用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
我了解到写入的原子性如何导致过程不交错。但是,为什么输出看起来像父级先执行所有写操作,然后子级执行其写操作呢?另外,无论我尝试了多少次,父母总是会先于孩子写。
答案 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
以确保确实(相对较慢的)光盘被卷入,则插入的可能性更大。也许不是;真的很难预测。如果您真的想看到交错,那么我会至少从父母和孩子那里写几千字节,一次写几千字节。几乎可以交错。