我试着看看如果我用键盘读取东西会发生什么,而我有多个进程使用fork()(在我的情况下有两个孩子和一个父亲)我发现了以下问题:我需要告诉父母等待孩子的过程,否则程序表现得很奇怪
我做了一个研究,我发现问题在于父母,他需要等待孩子的过程结束,因为如果父母的过程首先以某种方式结束他关闭了STDIN,我是对的吗?但我也发现每个过程都有一份STDIN,所以我的问题是:
为什么它以这种方式工作以及为什么只有父级问题与STDIN和子级没有问题,我的意思是为什么如果孩子的进程首先结束不会影响STDIN,但如果父进程首先结束它会影响STDIN?
以下是我的测试:
我在没有wait()的情况下运行程序,在输入数字后程序停止了,但是我再次按输入两次,并且出现了另外两条来自printf()的消息。
当我用wait()运行程序时,一切正常,每个进程分别调用scanf()并读取不同的数字。
答案 0 :(得分:6)
嗯,这里有很多东西。我将尝试逐步解释它。
启动终端时,终端会创建一个路径为/dev/pts/<some number>
的特殊文件。然后它启动你的shell(在这种情况下是bash
)并将bash进程的STDIN
,STDOUT
和STDERR
链接到这个特殊文件。此文件称为特殊文件,因为它实际上并不存在于您的硬盘上。相反,无论你write
到这个文件,它都直接进入终端,终端在屏幕上呈现它。 (同样,每当您尝试从此文件read
时,read
会阻止,直到有人在终端输入内容。
现在,当您通过键入./main
启动程序时,bash会调用fork
函数以创建新进程。子进程exec
是您的可执行文件,而父进程wait
则是子进程终止。然后,您的程序会调用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。
由于孩子们试图长时间阅读STDIN,下次你输入的东西会被其中一个孩子收到,而不是bash。所以你想要打字,比如23
。但是哎呀!在您按下2
键后,即可获得Your number is: 2
。你甚至没有按下回车键!之所以发生这种情况,是因为这种所谓的“非规范性”#34;模式。我不会进入什么原因,为什么会这样。但就目前而言,为了方便起见,使用可以在sh
而不是bash
上运行您的程序,因为sh
并未将STDIN置于非规范模式。这将使图片清晰。
不,父进程关闭其STDIN并不意味着其子级或其他进程无法使用它。
您看到的奇怪行为是因为当父出口时,bash
将pty(伪终端)置于非规范模式。如果您改用sh
,则无法看到该行为。如果你想要明确的修改,请阅读伪终端和线路规则。
父进程退出后,shell进程将立即恢复。
如果你使用wait
来确保父母退出最后一次,那么你就不会有任何问题,因为shell无法与你的程序一起运行。
通常情况下,bash会确保没有两个前台进程同时从STDIN读取,因此您不会看到这种奇怪的行为。它通过将一个程序的STDOUT传递给另一个程序,或者通过使一个进程成为后台进程来实现这一点。
琐事:当后台进程尝试从其STDIN
读取时,会向其发送信号SIGTTIN
,从而停止进程。但是,这与这种情况并不相关。
答案 1 :(得分:1)
当多个进程尝试对同一TTY执行I / O时,可能会出现几个问题。没有代码,我们无法分辨可能发生的事情。
尝试从后台进程组执行I / O可能会发出信号:SIGTTIN
用于输入(通常已启用),或SIGTTOU
用于输出(通常已禁用)
缓冲:如果您在fork之前执行任何I / O,那么任何已缓冲的数据都将用于两个进程。在某些情况下,使用fflush
可能有所帮助,但最好完全避免缓冲。请记住,与输出缓冲不同,不可能逐行缓冲输入(尽管您只能缓冲可用的内容,因此它可能出现首先进行行缓冲)
竞争条件:如果多个进程尝试读取相同的类似管道的文件,则未定义哪一个将“赢”并且每次可用时实际获取输入。
< / LI>