在考虑如何在自己的程序中实现某个功能时,我一直想知道bash如何在内部处理以下性质的管道:
FXMLLoader
这显然什么也不做,但是我不明白这怎么不会导致错误。我会以为:
因为yes | sleep 10
没有从stdin读取,连接两个进程的管道将填满并导致sleep
在尝试写入现在已满的管道时无限期阻塞
如果使用非阻塞IO,则如果首先执行yes
并在yes
进程运行之前将其写入管道,则不会发生任何错误,因此会发生错误。读取管道末端
我想这是我的主要误解。我已经尝试过查看bash源代码,但这已经超出了我的脑袋。
答案 0 :(得分:10)
这是运行shell命令yes | sleep 10
时实际发生的情况。
首先,shell使用anonymous pipe创建一个pipe
system call。 pipe
系统调用将打开两个文件描述符,它们是管道的读取端和写入端。写入到写入端的所有内容都可以从读取端读取。
此后,shell使用fork
system call创建两个子进程。两个孩子并行运行。
execve
system call,用yes
的代码映像替换此过程中的代码映像。yes
会尽可能长时间地写入管道。如果在管道的读取端没有有效的read
调用,则write
调用只会阻塞。 (实际上有一个write
缓冲区会被阻塞之前填满,但这并不重要。)execve
系统调用,以用sleep
的代码映像替换此过程中的代码映像。
程序sleep
在10秒钟内什么也不做。wait
system call)。 10秒一结束,运行sleep
的进程就会退出。此时,管道的读取端在任何过程中都不再打开。当某个进程试图写入在任何进程中未打开读取端的管道时,内核会向写入进程发送SIGPIPE
信号。因此,运行yes
的进程被SIGPIPE信号杀死。
这时,shell检测到它在管道两侧的子进程已经退出。 pipeline命令返回右侧的状态,该状态为0(sleep
成功退出)。
由于无法从stdin中读取睡眠,因此连接两个进程的管道将填满,并在尝试写入当前已满的管道时导致yes无限期阻塞
这是正确的。
如果使用非阻塞IO,则首先执行yes并在睡眠进程甚至运行之前就将其写入管道,因此没有进程连接到管道的读取端,则应该发生错误
在某些地方这是不正确的。 yes
不使用非阻塞IO。它与sleep
并行执行,而不是首先执行。从来没有任何时间点,没有进程连接到管道的读取端,直到sleep
退出。根据时间的不同,yes
可能会在sleep
开始执行之前开始写操作,甚至可能在sleep
程序的子进程被派生之前开始,但是当在写端打开的同时,pipe
调用返回了。