c - fork() 和 scanf() 如何协同工作?

标签 c linux fork scanf stdin

我试图看看如果我在使用 fork() 进行多个进程时从键盘读取内容会发生什么(在我的例子中有两个 child 和一个 parent ),我发现了以下问题:我需要告诉 parent 等待 child 的进程,否则程序行为异常。
我做了一项研究,发现问题出在 parent 身上,他需要等待 child 的进程结束,因为如果 parent 的进程以某种方式先结束,他会关闭 STDIN,对吗?但我还发现每个进程都有一份 STDIN,所以我的问题是:

为什么它以这种方式工作,为什么只有 parent 有 STDIN 的问题而 child 没有,我的意思是为什么如果 child 的进程先结束不影响 STDIN 但如果 parent 的进程先结束它会影响 STDIN?

  • 这是我的测试:

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

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

最佳答案

好吧,这里发生了很多事情。我将尝试逐步解释它。


当您启动终端时,终端会创建一个路径为 /dev/pts/<some number> 的特殊文件.然后它启动您的 shell(在本例中为 bash)并链接 STDIN , STDOUTSTDERR bash 过程到这个特殊文件。此文件称为特殊文件,因为它实际上并不存在于您的硬盘上。相反,无论你write对于这个文件,它直接进入终端,终端将它呈现在屏幕上。 (类似地,无论何时您尝试从此文件中输入 readread 都会阻塞,直到有人在终端输入内容)。

现在,当您通过键入 ./main 启动您的程序时, bash 调用 fork函数以创建一个新进程。子进程exec是你的可执行文件,而父进程 wait s 让 child 终止。然后你的程序调用 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。

由于 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/

相关文章:

android - 在 Linux 和 Windows 之间共享 Eclipse 工作区

linux - 用于查找大文件中字符串序列位置的快速 shell 命令

我可以使用 1G 的 super 页面来支持共享的 mmaps 吗?

c - 使用 execv 启动程序并传递参数而不引发 argc

perl - 为什么我不从我的子进程中获取退出状态?

c - 使用 fork 和 kill 信号

c - OpenSSL 更新到更新版本 : Alternative for the deprecated `OPENSSL_config`

c++ - std::memcpy与std::copy_n用于旧版C结构

c - 用索引替换

c - ftruncate() 对大页面失败