cat 在讀取埠時如何掛起?
當我們
cat
調整文本文件的內容時,所有內容都被列印出來並且命令終止。但是,當 we 時cat /dev/ttyS0
,命令掛起並等待新數據的到來。這次暫停的原因是什麼?
cat
在終端中進行實驗
cat
正如cuonglm 所說,正在等待更多數據。當您輸入cat
並按下Enter
以使其輸入是您的終端時,也會發生同樣的事情。在終端中,您可以輕鬆地試驗cat
. 所以首先,首先,這裡是如何做到這一點的描述。當您在終端中鍵入
Ctrl
+D
時,終端不會向程序發送文字Ctrl
+D
字元。為此,您需要輸入Ctrl
+V
來告訴終端應該按字面意思處理下一個控製字元,然後是Ctrl
+D
。反而:
- 如果輸入緩衝區中有文本——也就是說,你輸入的文本還沒有被發送到程序——那麼緩衝區被刷新,也就是說它被發送到程序。程序將在下一次讀取時接收它。
- 如果輸入緩衝區中沒有文本,則終端指示程序輸入結束,這將導致它在下一次讀取操作時遇到文件結束條件,這(見下文)實際上是目前讀取操作,在這種情況下,因為
cat
已經在讀取。按
Enter
,除了指示換行符之外,還會導致緩衝區被刷新,即使您沒有按Ctrl
+D
。因此,您將觀察到:
- 輸入文本並按
Enter
或Ctrl
+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 程序的程序 IDpgrep -x cat
。通常至少支持其中之一。或者在正在執行的同一終端中按Ctrl
+以暫停它——這實際上暫停了程序——然後執行以查找其 PID,然後通過執行恢復它。或者在第一個終端中執行以顯示終端設備的路徑名——在 GNU/Linux 中,這可能看起來像或–then run ,然後在第二個終端中執行,替換為第一個終端中的輸出。Z``cat``ps``fg``tty``/dev/tty2``/dev/pts/4``cat``ps -t *tty*
tty
tty
在第二個終端中,執行替換為 cat 程序的實際程序 ID 的位置。這將列出程序,包括顯示其狀態的列。(即使您已經在上面執行,這在某些系統上也是必要的,包括 GNU/Linux,以前不會包含狀態列。)
ps *pid*
pid
tty -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*
追踪過程
通常要調試程序,您可以在調試器中打開它,例如
gdb
或lldb
。但是,包括 GNU/Linux 在內的一些系統有一個名為的實用程序strace
,您可以使用它來查看程序正在執行的*系統呼叫。*當您用於strace
執行等待來自終端的輸入的程序時,您可以看到read
阻止其執行的每個呼叫,直到更多輸入發送到終端上的程序。如果您有 GNU/Linux 系統,例如 Fedora、CentOS、Debian 或 Ubuntu(這些只是範例——還有很多其他的),請嘗試執行以下命令:strace cat
你會看到一大堆系統呼叫:首先是那些執行程序的呼叫(如果程序使用共享庫,然後
execve
是涉及文件名的各種呼叫ld.so
,它幾乎肯定會這樣做),然後是那些為大多數程序執行的呼叫使 C 庫函式可以工作,包括mmap
andbrk
… 然後最終它將從標準輸入中讀取: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 +++