我试图看看如果我在使用 fork() 进行多个进程时从键盘读取内容会发生什么(在我的例子中有两个 child 和一个 parent ),我发现了以下问题:我需要告诉 parent 等待 child 的进程,否则程序行为异常。
我做了一项研究,发现问题出在 parent 身上,他需要等待 child 的进程结束,因为如果 parent 的进程以某种方式先结束,他会关闭 STDIN,对吗?但我还发现每个进程都有一份 STDIN,所以我的问题是:
为什么它以这种方式工作,为什么只有 parent 有 STDIN 的问题而 child 没有,我的意思是为什么如果 child 的进程先结束不影响 STDIN 但如果 parent 的进程先结束它会影响 STDIN?
这是我的测试:
我在没有 wait() 的情况下运行程序,在我输入一个数字后程序停止了,但随后我又按了两次 enter 并且出现了来自 printf() 的另外两条消息。
当我使用 wait() 运行程序时,一切正常,每个进程分别调用 scanf() 并读取不同的数字。
最佳答案
好吧,这里发生了很多事情。我将尝试逐步解释它。
当您启动终端时,终端会创建一个路径为 /dev/pts/<some number>
的特殊文件.然后它启动您的 shell(在本例中为 bash
)并链接 STDIN
, STDOUT
和 STDERR
bash 过程到这个特殊文件。此文件称为特殊文件,因为它实际上并不存在于您的硬盘上。相反,无论你write
对于这个文件,它直接进入终端,终端将它呈现在屏幕上。 (类似地,无论何时您尝试从此文件中输入 read
,read
都会阻塞,直到有人在终端输入内容)。
现在,当您通过键入 ./main
启动您的程序时, bash 调用 fork
函数以创建一个新进程。子进程exec
是你的可执行文件,而父进程 wait
s 让 child 终止。然后你的程序调用 fork
两次,我们有三个进程试图读取它们的 STDIN
s,即同一个文件/dev/pts/something
. (请记住,调用 fork
和 exec
会分别复制和保留文件描述符)。
三个进程处于竞争状态。当您在终端输入内容时,三个进程之一将收到它(100 次中有 99 次是父进程,因为子进程在到达 scanf
语句之前必须做更多的工作)。
因此,父进程先打印数字并退出。等待父进程完成的 bash 进程恢复并 puts the STDIN into a so called "non-canonical" mode
, 并调用 read
为了读取下一个命令。现在,三个进程(Child1、Child2 和 bash)再次尝试读取 STDIN。
由于 children 尝试阅读 STDIN 的时间更长,因此下次您输入内容时,其中一个 child 将接收到该内容,而不是 bash。所以你想到打字,比方说,23
.但是哎呀!在您按下 2
之后 key ,你得到Your number is: 2
.你甚至没有按下 Enter 键!发生这种情况是因为这种所谓的“非规范”模式。我不会深入探讨那是什么以及为什么会这样。但是现在,为了让事情更简单,可以在 sh
上运行您的程序。而不是 bash
, 自 sh
不会将 STDIN 置于非规范模式。这将使图片清晰。
长话短说
不,父进程关闭其 STDIN 并不意味着其子进程或其他进程将无法使用它。
您看到的奇怪行为是因为当父级退出时,
bash
将 pty(伪终端)置于非规范模式。如果你使用sh
相反,您不会看到该行为。如果你想有一个清晰的理解,请阅读伪终端和线路纪律。一旦父进程退出,shell 进程就会恢复。
如果您使用
wait
为了确保父级最后退出,您不会有任何问题,因为 shell 将无法与您的程序一起运行。通常,bash 会确保没有两个前台进程同时从 STDIN 读取数据,因此您不会看到这种奇怪的行为。它通过将一个程序的 STDOUT 管道传输到另一个程序,或使一个进程成为后台进程来实现这一点。
琐事:当后台进程尝试读取其
STDIN
时, 发送一个信号SIGTTIN
,这会停止该过程。不过,这与这种情况并不相关。
关于c - fork() 和 scanf() 如何协同工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41349537/