Cat

cat 在讀取埠時如何掛起?

  • November 15, 2017

當我們cat調整文本文件的內容時,所有內容都被列印出來並且命令終止。但是,當 we 時cat /dev/ttyS0,命令掛起並等待新數據的到來。

這次暫停的原因是什麼?

cat在終端中進行實驗

cat正如cuonglm 所說,正在等待更多數據。當您輸入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+或在按+之後(即,連續兩次)結束輸入。D``Ctrl``D

cat看到從它的最後一個輸入源中沒有更多內容可讀取時(請記住,“cat”代表“catenate”或“concatenate”,這是它在接收多個文件名參數時的行為),它退出。

就像cat它在從控制終端讀取直到輸入完成時等待輸入一樣,它也等待來自任何其他文件或設備的更多輸入,包括串列終端,如果這是您告訴它讀取的內容。

請注意,與Ctrl+D和相關的特殊行為會Enter導致您的輸入發送到cat,是終端的行為,而不是終端的行為cat。特別是,cat它本身並不特別對待換行符。

等待程序的狀態

cat等待您的輸入時,請嘗試從另一個終端檢查其狀態。

在大多數類 Unix 作業系統上,您可以通過執行pidof cat或找到所有正在執行的 cat 程序的程序 ID pgrep -x cat。通常至少支持其中之一。或者在正在執行的同一終端中按Ctrl+以暫停它——這實際上暫停了程序——然後執行以查找其 PID,然後通過執行恢復它。或者在第一個終端中執行以顯示終端設備的路徑名——在 GNU/Linux 中,這可能看起來像或–then run ,然後在第二個終端中執行,替換為第一個終端中的輸出。Z``cat``ps``fg``tty``/dev/tty2``/dev/pts/4``cat``ps -t *tty*ttytty

在第二個終端中,執行替換為 cat 程序的實際程序 ID 的位置。這將列出程序,包括顯示其狀態的列。(即使您已經在上面執行,這在某些系統上也是必要的,包括 GNU/Linux,以前不會包含狀態列。)ps *pid*pidtty -t

例如,我在 GNU/Linux 系統上這樣做:

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

你的可能看起來不一樣。我建議執行man ps並查找出現在 STAT 或 S 列下的程序狀態程式碼,它們在不同系統中有所不同。在那個 GNU/Linux 系統上,ps(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 操作的中間時。無法終止處於不間斷睡眠狀態的程序。如果您使用or ,它會在操作完成後(分別)收到 TERM 或 KILL 信號。kill *pid*``kill -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+任何地方)並重新執行它,同時通過將其重定向到來抑制其輸出,您將能夠更容易地看到:D``Ctrl``C``/dev/null

strace cat >/dev/null

您仍然可以看到輸出,strace因為它被寫入標準錯誤並且您只重定向了標準輸出。但是 nowcat自己的輸出——foo和一個換行符,就像你給它的一樣——沒有顯示出來。foo鍵入並按下後您看到的內容Enter如下所示:

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

這就是說read呼叫結束,讀取foo後跟一個換行符,顯示為\n. cat然後呼叫write將相同的字元串寫入其輸出。可能在其 C 程式碼cat中沒有呼叫。write相反,它可能使用類似fprintf 或的 C 庫函式來執行寫入,因為它不是系統呼叫,所以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+ ),按+ 。結束輸入,退出,並顯示如下內容:D``Ctrl``D``cat``strace

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

引用自:https://unix.stackexchange.com/questions/404653