cat 如何在读取端口时暂停?

cat 如何在读取端口时暂停?

当我们cat调整文本文件的内容时,所有内容都会打印出来并且命令终止。但是,当我们 时cat /dev/ttyS0,命令会挂起并等待新数据的到来。

这次暂停的原因是什么?

答案1

cat在终端中进行试验

cat正在等待更多数据,因为库恩格姆 说。当您输入cat并按下Enter以便其输入是您的终端时,也会发生同样的事情。在终端中,您可以轻松地试验 的行为cat。首先,这里描述了如何做到这一点。

当您在终端中键入Ctrl+时D,终端不会将文字Ctrl+D字符发送到程序。为此,您需要输入Ctrl+V来告诉终端下一个控制字符应该按字面意思处理,然后输入Ctrl+ D。反而:

  • 如果输入缓冲区中有文本——也就是说,您输入了尚未发送到程序的文本——那么缓冲区将被刷新,也就是说它已被发送到程序。该程序将在下次读取时接收它。
  • 如果输入缓冲区中没有文本,则终端向程序指示输入结束,这将导致程序在下一次读取操作时遇到文件结束条件,这(见下文)实际上是当前的读操作,在这种情况下,因为cat已经在读。

按下Enter,除了指示换行符,Ctrl即使您没有按+ D,也会导致缓冲区被刷新。因此你会观察到:

  • 输入文本并按EnterCtrl+D会将cat相同的文本写入您的终端。使用Enter, cat也会写入换行符。

  • Enter在行首按,或按Ctrl+后执行此操作D,会发送一个换行符并cat 写入一个。

  • 在行首按Ctrl+或按+之后(即连续两次)将结束输入。DCtrlD

    cat看到从最后一个输入源中没有任何内容可读取时(请记住,“cat”代表“catenate”或“concatenate”,这是它接收多个文件名参数时的行为方式),它会退出。

就像cat从控制终端读取数据时等待输入直到输入完成一样,它也会等待来自任何其他文件或设备(包括串行终端)的更多输入(如果您告诉它从中读取)。

Ctrl请注意,与+D和相关的特殊行为Enter会导致您的输入被发送到cat,这是你的终端,而不是cat。特别是,cat它本身并不特殊对待换行符。

等待进程的状态

cat等待您的输入时,尝试从另一个终端检查其状态。

在大多数类 Unix 操作系统上,您可以通过运行pidof cat或 来查找所有正在运行的 cat 进程的进程 ID pgrep -x cat。至少其中通常受到支持。或者在正在运行的同一终端中按Ctrl+来暂停它 - 这Zcat实际上挂起进程——然后运行ps以查找其 PID,然后通过运行 来恢复它fg。或者tty在第一个终端中运行以显示终端设备的路径名 - 在 GNU/Linux 中,这可能看起来像/dev/tty2/dev/pts/4--then run cat,然后在第二个终端中运行,替换ps -t ttyttytty第一个终端的输出。

在第二个终端中,运行您替换的位置ps pidpid与 cat 进程的实际进程 ID。这将列出进程,包括显示其状态的列。 (即使您已经运行了tty -t上面的代码,这在某些系统上也是必要的,包括 GNU/Linux,它们之前不会包含状态列。)

例如,我在 GNU/Linux 系统上这样做:

ek@Io:~$ ps 30196
  PID TTY      STAT   TIME COMMAND
30196 pts/4    S+     0:00 cat

你的可能看起来不同。我建议运行man ps并查找出现在 STAT 或 S 列下的进程状态代码,这些代码在不同系统中略有不同。在那个 GNU/Linux 系统上,附注(1)显示进程的状态已报告为:

               S    interruptible sleep (waiting for an event to complete)

               +    is in the foreground process group

在那个 GNU/Linux 系统上,如果我运行cat然后用Ctrl+暂停它Z,如上所述,S+更改为T

               T    stopped by job control signal

睡眠可中断,所以有不间断的睡觉?是的:

               D    uninterruptible sleep (usually IO)

当进程正在进行内核不允许取消的 IO 操作时,通常会发生这种情况。处于不间断睡眠状态的进程无法被终止。如果您使用或,一旦操作完成,它就会(分别)收到 TERM 或 KILL 信号。kill pidkill -9 pid

追踪过程

通常要调试程序,您可以在调试器中打开它,例如gdblldb。然而,包括 GNU/Linux 在内的一些系统有一个名为strace,您可以使用它来查看什么系统调用一个过程正在发生。当您用来strace运行等待来自终端的输入的进程时,您可以看到阻止其执行的每个read调用,直到更多输入发送到终端上的进程。如果您有 GNU/Linux 系统,例如 Fedora、CentOS、Debian 或 Ubuntu(这些只是示例,还有许多其他系统),请尝试运行以下命令:

strace cat

您将看到一大堆系统调用:首先是那些执行进程的系统调用(如果程序使用共享库,则后面execve是涉及文件名的各种调用,这几乎肯定是这样),然后是为大多数程序运行的系统调用ld.so使 C 库函数可以工作,包括mmapand brk...然后最终它将读取标准输入

read(0,

这就是 的语法strace。过程cat不是从字面上看,是在传递参数的过程中read(因为该语法对于以前没有使用过的 C 程序员来说可能看起来很像strace)。相反,它进行了read系统调用,尚未返回。当读取数据或到达输入结束时它将返回。

尝试输入一些内容,例如foo,然后按Enter。然后你会看到类似的内容:

"foo\n", 131072)                = 4
write(1, "foo\n", 4foo
)                    = 4
read(0,

这意味着什么?如果您退出cat(在行首添加Ctrl+ ,或在任意位置添加+ )并重新运行它,同时通过将其重定向到抑制其输出,您将能够更轻松地看到:DCtrlC/dev/null

strace cat >/dev/null

您仍然可以看到输出,strace因为它被写入标准误并且你只重定向了标准输出。但现在cat自己的输出 -foo以及换行符,正如您所给出的那样 - 没有显示。键入foo并按下后您看到的内容Enter如下所示:

"foo\n", 131072)                = 4
write(1, "foo\n", 4)                    = 4
read(0,

这就是说,read调用完成,读取foo后跟一个换行符,显示为\ncat然后调用write将相同的字符串写入其输出。其 C 代码中可能cat没有调用。write相反,它可能使用 C 库函数(如fprintf 或 )fputs来执行写入,这不会显示在 的输出中,strace因为它不是系统调用,并且最终通过使用系统write调用来工作。传递4给它是write为了告诉它——也就是说,告诉内核——应该写入四个字节:f o o \n

请注意,在调用 后write,再次cat调用read以继续读取输入。您可以继续给它输入并看到它调用write,然后read再次调用。例如,我输入abracadabra并按下Enter

"abracadabra\n", 131072)        = 12
write(1, "abracadabra\n", 12)           = 12
read(0,

完成后,在输入最后一个内容后按Enter(或Ctrl+ ),然后按+ 。输入结束,退出,并向您显示如下内容:DCtrlDcatstrace

read(0, "", 131072)                     = 0
munmap(0x7f856a591000, 139264)          = 0
close(0)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

相关内容