fork()和scanf()如何一起工作?

时间:2016-12-27 17:29:41

标签: c linux fork scanf stdin

我试着看看如果我用键盘读取东西会发生什么,而我有多个进程使用fork()(在我的情况下有两个孩子和一个父亲)我发现了以下问题:我需要告诉父母等待孩子的过程,否则程序表现得很奇怪 我做了一个研究,我发现问题在于父母,他需要等待孩子的过程结束,因为如果父母的过程首先以某种方式结束他关闭了STDIN,我是对的吗?但我也发现每个过程都有一份STDIN,所以我的问题是:

为什么它以这种方式工作以及为什么只有父级问题与STDIN和子级没有问题,我的意思是为什么如果孩子的进程首先结束不会影响STDIN,但如果父进程首先结束它会影响STDIN?

  • 以下是我的测试:

    1. 我在没有wait()的情况下运行程序,在输入数字后程序停止了,但是我再次按输入两次,并且出现了另外两条来自printf()的消息。 Here is the picture.

    2. 当我用wait()运行程序时,一切正常,每个进程分别调用scanf()并读取不同的数字。 Here is the picture.

2 个答案:

答案 0 :(得分:6)

嗯,这里有很多东西。我将尝试逐步解释它。

启动终端时,终端会创建一个路径为/dev/pts/<some number>的特殊文件。然后它启动你的shell(在这种情况下是bash)并将bash进程的STDINSTDOUTSTDERR链接到这个特殊文件。此文件称为特殊文件,因为它实际上并不存在于您的硬盘上。相反,无论你write到这个文件,它都直接进入终端,终端在屏幕上呈现它。 (同样,每当您尝试从此文件read时,read会阻止,直到有人在终端输入内容。

现在,当您通过键入./main启动程序时,bash会调用fork函数以创建新进程。子进程exec是您的可执行文件,而父进程wait则是子进程终止。然后,您的程序会调用fork两次,我们有三个进程尝试读取STDIN s,即同一个文件/dev/pts/something。 (请记住,调用forkexec分别重复并保留文件描述符。)

这三个过程处于竞争状态。当您在终端输入内容时,三个进程中的一个将接收它(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置于非规范模式。这将使图片清晰。

TL; DR

  • 不,父进程关闭其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>