重新啟動系統呼叫是否安全?
我正在閱讀一本教科書,該教科書描述瞭如何在中斷時處理系統呼叫:
系統呼叫可以被中斷。可能會長時間阻塞程序的系統呼叫(例如讀取、等待和接受)稱為慢速系統呼叫。在某些舊版本的 Unix 上,當處理程序擷取信號時被中斷的慢速系統呼叫不會在信號處理程序返回時恢復,而是立即返回給使用者,並帶有錯誤條件並將 errno 設置為 EINTR。在這些系統上,程序員必須包含手動重新啟動中斷系統呼叫的程式碼。
但是重啟系統呼叫總是安全的嗎?假設系統呼叫維護一個內部資料結構,需要在系統呼叫完成之前重置該結構。所以我們啟動系統呼叫,它是長時間執行和阻塞的,當一個信號中斷它時,系統呼叫只是重新啟動,所以第一個系統呼叫沒有機會重置資料結構。
由於上一次呼叫的資料結構沒有被重置,所以在第二次系統呼叫發生後,資料結構不一致,可能會污染操作。
那麼重啟系統呼叫安全嗎?
在 Linux 上,是的,重新啟動系統呼叫總是安全的,它返回
EINTR
: 該返回值意味著系統呼叫在取得任何有用的進展之前被中斷,應該重新啟動。系統呼叫的實現考慮到了這一點。由於系統呼叫中斷而導致系統狀態發生變化的情況,處理方式不同;例如,
read
呼叫在中斷之前檢索了一些數據,將返回該數據,表示成功,而write
在中斷之前傳輸了一些數據的呼叫將返回它寫入的數據量,也表示成功。(順便說一句,這是檢查這些函式的返回值而不是假設成功呼叫完成了所有請求的工作的原因之一。)
SA_RESTART
通過為適當的信號設置標誌,許多系統呼叫可以自動重新啟動。GNU C 庫提供了一個宏,它可以幫助編寫重新啟動的程式碼,TEMP_FAILURE_RETRY
inunistd.h
(defined if_GNU_SOURCE
is defined)。請注意,在 Linux 上,
EINTR
即使沒有信號處理程序,系統呼叫也可以返回。“信號處理程序中斷系統呼叫和庫函式”部分
man 7 signal
包含所有詳細資訊,包括各種場景中受影響的系統呼叫的列表。