fork() 和 scanf() 如何协同工作?
How fork() and scanf() work together?
我试图看看如果我在使用 fork() 的多个进程中从键盘读取内容会发生什么(在我的例子中有两个 children 和一个 parent),我发现了以下问题:我需要告诉 parent 等待 children 的进程,否则程序的行为会很糟糕。运行gely.
我做了一项研究,发现问题出在 parent,他需要等待 child 的进程结束,因为如果 parent 的进程以某种方式先结束,他关闭 STDIN,对吗?但我也发现每个进程都有一份 STDIN 所以我的问题是:
为什么它以这种方式工作以及为什么只有 parent 有 STDIN 的问题而 children 没有,我的意思是为什么如果 child 的进程先结束不影响 STDIN 但是如果 parent 的进程先结束,它会影响 STDIN 吗?
这是我的测试:
我 运行 没有 wait() 的程序,在我输入一个数字后程序停止了,但随后我又按了两次 enter并且出现了来自 printf() 的另外两条消息。
当我 运行 带有 wait() 的程序一切正常时,每个进程分别调用 scanf() 并读取不同的数字。
当多个进程尝试对同一个 TTY 执行 I/O 时,可能会发生几个问题。没有代码,我们无法判断可能发生的情况。
尝试从后台进程组执行 I/O 可能会发出信号:SIGTTIN
输入(通常启用),或 SIGTTOU
输出(通常禁用)
缓冲:如果您在分叉之前执行任何 I/O,任何已缓冲的数据都将用于 both 进程。在某些情况下,使用 fflush
可能会有所帮助,但最好避免完全缓冲。请记住,与输出缓冲不同,不可能逐行缓冲输入(尽管您只能缓冲可用的内容,因此它可能 看起来 是行缓冲的一开始)。
竞争条件:如果多个进程试图读取同一个类似管道的文件,则不确定哪个进程会 "win" 并在每次可用时实际获取输入.
好吧,这里发生了很多事情。我会尝试一步一步解释它。
当您启动终端时,终端会创建一个具有路径 /dev/pts/<some number>
的特殊文件。然后它启动您的 shell(在本例中为 bash
)并将 bash 进程的 STDIN
、STDOUT
和 STDERR
链接到这个特殊的文件。此文件称为特殊文件,因为它实际上并不存在于您的硬盘上。相反,无论您 write
这个文件是什么,它都会直接进入终端,然后终端将它呈现在屏幕上。 (类似地,每当您尝试从此文件 read
时,read
会阻塞,直到有人在终端输入内容。
现在,当您通过键入 ./main
启动程序时,bash 会调用 fork
函数以创建一个新进程。 child 进程 exec
s 你的可执行文件,而 parent 进程 wait
s 让 child 终止。然后您的程序调用 fork
两次,我们有三个进程试图读取它们的 STDIN
s,即同一个文件 /dev/pts/something
。 (请记住,调用 fork
和 exec
会分别复制和保留文件描述符)。
三个进程处于竞争状态。当您在终端输入内容时,三个进程之一将收到它(100 次中有 99 次是 parent 进程,因为 children 在到达 [=27 之前必须做更多的工作=] 声明).
因此,parent 进程先打印数字并退出。等待 parent 完成的 bash 进程恢复并 puts the STDIN into a so called "non-canonical" mode
,并调用 read
以读取下一个命令。现在,三个进程(Child1、Child2 和 bash)再次尝试读取 STDIN。
由于 children 尝试读取 STDIN 的时间更长,下次您输入内容时,children 中的一个将收到它,而不是 bash.所以你想到打字,比方说,23
。但是哎呀!在您按下 2
键之后,您会得到 Your number is: 2
。你甚至没有按下 Enter 键!这是因为所谓的“non-canonical”模式。我不会深入探讨那是什么以及为什么会这样。但是现在,为了让事情更简单,可以在 sh
上使用 运行 你的程序而不是 bash
,因为 sh
不会将 STDIN 置于 non-canonical 模式.这将使图片清晰。
TL;DR
不,parent 进程关闭其 STDIN 并不意味着其 children 或其他进程将无法使用它。
您看到的奇怪行为是因为当 parent 退出时,bash
将 pty(伪终端)置于 non-canonical 模式。如果您改用 sh
,您将看不到该行为。如果您想清楚地理解,请阅读伪终端和行规程。
shell 进程将在 parent 退出后立即恢复。
如果你使用 wait
来确保 parents 最后退出,你不会有任何问题,因为 shell 将无法运行 连同您的程序。
通常,bash 确保没有两个前台进程同时从 STDIN 读取,因此您不会看到这种奇怪的行为。它通过将一个程序的 STDOUT 管道传输到另一个程序,或使一个进程成为后台进程来实现这一点。
琐事:当后台进程试图从其 STDIN
中读取时,它会收到一个信号 SIGTTIN
,该信号会停止该进程。不过,这与这种情况并不相关。
我试图看看如果我在使用 fork() 的多个进程中从键盘读取内容会发生什么(在我的例子中有两个 children 和一个 parent),我发现了以下问题:我需要告诉 parent 等待 children 的进程,否则程序的行为会很糟糕。运行gely.
我做了一项研究,发现问题出在 parent,他需要等待 child 的进程结束,因为如果 parent 的进程以某种方式先结束,他关闭 STDIN,对吗?但我也发现每个进程都有一份 STDIN 所以我的问题是:
为什么它以这种方式工作以及为什么只有 parent 有 STDIN 的问题而 children 没有,我的意思是为什么如果 child 的进程先结束不影响 STDIN 但是如果 parent 的进程先结束,它会影响 STDIN 吗?
这是我的测试:
我 运行 没有 wait() 的程序,在我输入一个数字后程序停止了,但随后我又按了两次 enter并且出现了来自 printf() 的另外两条消息。
当我 运行 带有 wait() 的程序一切正常时,每个进程分别调用 scanf() 并读取不同的数字。
当多个进程尝试对同一个 TTY 执行 I/O 时,可能会发生几个问题。没有代码,我们无法判断可能发生的情况。
尝试从后台进程组执行 I/O 可能会发出信号:
SIGTTIN
输入(通常启用),或SIGTTOU
输出(通常禁用)缓冲:如果您在分叉之前执行任何 I/O,任何已缓冲的数据都将用于 both 进程。在某些情况下,使用
fflush
可能会有所帮助,但最好避免完全缓冲。请记住,与输出缓冲不同,不可能逐行缓冲输入(尽管您只能缓冲可用的内容,因此它可能 看起来 是行缓冲的一开始)。竞争条件:如果多个进程试图读取同一个类似管道的文件,则不确定哪个进程会 "win" 并在每次可用时实际获取输入.
好吧,这里发生了很多事情。我会尝试一步一步解释它。
当您启动终端时,终端会创建一个具有路径 /dev/pts/<some number>
的特殊文件。然后它启动您的 shell(在本例中为 bash
)并将 bash 进程的 STDIN
、STDOUT
和 STDERR
链接到这个特殊的文件。此文件称为特殊文件,因为它实际上并不存在于您的硬盘上。相反,无论您 write
这个文件是什么,它都会直接进入终端,然后终端将它呈现在屏幕上。 (类似地,每当您尝试从此文件 read
时,read
会阻塞,直到有人在终端输入内容。
现在,当您通过键入 ./main
启动程序时,bash 会调用 fork
函数以创建一个新进程。 child 进程 exec
s 你的可执行文件,而 parent 进程 wait
s 让 child 终止。然后您的程序调用 fork
两次,我们有三个进程试图读取它们的 STDIN
s,即同一个文件 /dev/pts/something
。 (请记住,调用 fork
和 exec
会分别复制和保留文件描述符)。
三个进程处于竞争状态。当您在终端输入内容时,三个进程之一将收到它(100 次中有 99 次是 parent 进程,因为 children 在到达 [=27 之前必须做更多的工作=] 声明).
因此,parent 进程先打印数字并退出。等待 parent 完成的 bash 进程恢复并 puts the STDIN into a so called "non-canonical" mode
,并调用 read
以读取下一个命令。现在,三个进程(Child1、Child2 和 bash)再次尝试读取 STDIN。
由于 children 尝试读取 STDIN 的时间更长,下次您输入内容时,children 中的一个将收到它,而不是 bash.所以你想到打字,比方说,23
。但是哎呀!在您按下 2
键之后,您会得到 Your number is: 2
。你甚至没有按下 Enter 键!这是因为所谓的“non-canonical”模式。我不会深入探讨那是什么以及为什么会这样。但是现在,为了让事情更简单,可以在 sh
上使用 运行 你的程序而不是 bash
,因为 sh
不会将 STDIN 置于 non-canonical 模式.这将使图片清晰。
TL;DR
不,parent 进程关闭其 STDIN 并不意味着其 children 或其他进程将无法使用它。
您看到的奇怪行为是因为当 parent 退出时,
bash
将 pty(伪终端)置于 non-canonical 模式。如果您改用sh
,您将看不到该行为。如果您想清楚地理解,请阅读伪终端和行规程。shell 进程将在 parent 退出后立即恢复。
如果你使用
wait
来确保 parents 最后退出,你不会有任何问题,因为 shell 将无法运行 连同您的程序。通常,bash 确保没有两个前台进程同时从 STDIN 读取,因此您不会看到这种奇怪的行为。它通过将一个程序的 STDOUT 管道传输到另一个程序,或使一个进程成为后台进程来实现这一点。
琐事:当后台进程试图从其
STDIN
中读取时,它会收到一个信号SIGTTIN
,该信号会停止该进程。不过,这与这种情况并不相关。