Bash while 循环并从管道读取

Bash while 循环并从管道读取

我有一个 Windows 命令行程序,正在 Ubuntu 中通过 wine 在 Bash 脚本中运行。 Bash 脚本基本上如下所示:

wine myprogram.exe | while read line
do
   # Process line
done

现在,自从我编写了 myprogram.exe 以来,我知道它只是尽可能快地吐出数据。谁能向我解释一下 Bash while 循环如何能够处理数据,以防我的程序吐出数据的速度比 while 循环处理的速度快?幕后是否存在某种魔法,如果内核调度程序产生太多数据,就会让 myprogram.exe 进入睡眠状态?任何人?目前我倾向于它是黑魔法。

答案1

首先,程序可以进行自己的输出缓冲。这有时被称为“标准输入输出在 C 中执行此任务的库组件名称后添加“buffering”:在 中声明的函数,如putcfputsfprintfstdio.h。如果是这样,它将产生突发输出,通常是几千字节(当输出是终端时,默认是在每个换行符处刷新缓冲区)。

在某些时候,程序员或底层库函数调用write明确地。这请求内核将指定的数据写入管道。内核可能决定写入全部或部分数据。由于文件是一个管道,内核会将数据复制到管道的缓冲区中。如果管道缓冲区已满,则write系统调用将阻塞,直到有空间为止:您的程序(或更准确地说,调用 的线程write,如果有多个内核级线程)将不会恢复执行,直到调用write返回。

(有可能,但在这种情况下不太可能,程序已将管道的文件描述符设置为非阻塞。如果是这种情况,如果内核确定它无法复制任何数据,它将把控制权返回给程序;系统write调用返回 0。进行此类非阻塞系统调用的程序通常会调用selectpollepoll在一个循环中阻塞,直到它所通信的文件描述符之一准备好输入或输出。)

程序在系统调用期间被阻塞的事实与调度算法的选择无关。其核心是任何调度程序区分准备好线程,可以给予 CPU 时间,以及等待线程,但不能。调度程序的要点是选择一个就绪线程,并让它运行,直到该线程进行系统调用(将线程置于等待状态)或发生某些异步事件(实际上,处理器打断)。在处理系统调用期间,可能之前被阻止的线程已准备好,例如因为该线程正在调用write并且内核现在已经能够从该调用传递数据。有一些事情可以使就绪线程从外部阻塞,例如暂停信号 ( SIGSTOP)。调度程序维护某种准备清单决定接下来要调度哪个线程:准备好的线程列表(它通常比现实调度程序中的简单列表复杂得多)。

答案2

它被称为管道。运行一下man 7 pipe,您将了解管道的工作原理。

编辑添加: 简而言之,wine 命令的输出被收集在缓冲区中。当缓冲区已满并且 wine 尝试写入更多内容时,wine 进程将被内核停止。当缓冲区中的空间变得可用时(即,当 while 循环从中读取时),停止的进程将被重新唤醒并允许其继续。这是经典的有界缓冲区算法,但在 Unix/Linux 世界中它通常称为管道。它非常流行且功能强大,我建议您全面了解它。

答案3

不,没有黑魔法(至少这一次没有)。它只是使用输出流中的缓存和缓冲区。

相关内容