部分讀取的 unix 流輔助數據會發生什麼情況?
所以我已經閱讀了很多關於 unix-stream 輔助數據的資訊,但是所有文件中缺少的一件事是當有部分讀取時應該發生什麼?
假設我將以下消息接收到 24 字節緩衝區中
msg1 [20 byes] (no ancillary data) msg2 [7 bytes] (2 file descriptors) msg3 [7 bytes] (1 file descriptor) msg4 [10 bytes] (no ancillary data) msg5 [7 bytes] (5 file descriptors)
第一次呼叫 recvmsg,我得到了所有的 msg1(和 msg2 的一部分?作業系統會這樣做嗎?)當我知道消息實際上告訴我如何處理數據時?如果我從 msg1 中釋放 20 個字節,然後再次呼叫 recvmsg,它會同時傳遞 msg3 和 msg4 嗎?來自 msg3 和 msg4 的輔助數據是否在控制消息結構中連接起來?
雖然我可以編寫測試程序以實驗性地找出這一點,但我正在尋找有關輔助數據在流上下文中如何表現的**文件。**我在上面找不到任何官方資訊,這似乎很奇怪。
我將在這裡添加我的實驗結果,這是我從這個測試程序中得到的:
https://github.com/nrdvana/daemonproxy/blob/master/src/ancillary_test.c
Linux 3.2.59、3.17.6
只要在呼叫 recvmsg 期間不需要傳遞先前的輔助有效負載,Linux 似乎會將部分輔助承載消息附加到其他消息的末尾。一旦傳遞了一條消息的輔助數據,它將返回一個簡短的讀取,而不是開始下一條輔助數據消息。所以,在上面的例子中,我得到的讀數是:
recv1: [24 bytes] (msg1 + partial msg2 with msg2's 2 file descriptors) recv2: [10 bytes] (remainder of msg2 + msg3 with msg3's 1 file descriptor) recv3: [17 bytes] (msg4 + msg5 with msg5's 5 file descriptors) recv4: [0 bytes]
BSD 4.4、10.0
BSD 提供了比 Linux 更多的對齊方式,並在帶有輔助數據的消息開始**之前立即進行簡短讀取。**但是,它會很樂意將非輔助承載消息附加到輔助承載消息的末尾。因此,對於 BSD,看起來如果您的緩衝區大於輔助承載消息,您將獲得幾乎類似於數據包的行為。我得到的讀數是:
recv1: [20 bytes] (msg1) recv2: [7 bytes] (msg2, with msg2's 2 file descriptors) recv3: [17 bytes] (msg3, and msg4, with msg3's 1 file descriptor) recv4: [7 bytes] (msg5 with 5 file descriptors) recv5: [0 bytes]
全部:
仍然想知道它在舊版 Linux、iOS、Solaris 等上是如何發生的,以及它在未來會如何發生**。**
輔助數據被接收,就好像它與段中的第一個正常數據八位字節(如果有)一起排隊。
對於你剩下的問題,事情變得有點棘手。
…就本節而言,數據報被認為是終止記錄的數據段,並且包括作為特殊類型的輔助數據的源地址。
當數據通過協議傳遞到套接字時,數據段被放入隊列中。正常數據段在傳遞時被放置在隊列的末尾。如果新段包含與前段相同類型的數據並且不包含輔助數據,並且如果前段沒有終止記錄,則這些段在邏輯上合併為單個段……
接收操作不得從多個段返回數據或輔助數據。
因此,現代 BSD 套接字與此提取完全匹配。這並不奇怪:-)。
請記住,POSIX 標準是在 UNIX 之後編寫的,並且是在 BSD 與 System V 等拆分之後編寫的。主要目標之一是幫助理解現有的行為範圍,並防止現有功能出現更多拆分。
Linux 是在沒有參考 BSD 程式碼的情況下實現的。它似乎在這裡表現不同。
- 如果我沒看錯的話,當一個新段確實包含輔助數據但前一個段不包含時,聽起來 Linux 會另外合併“段” 。
- 您的觀點是“只要在呼叫 recvmsg 期間不需要傳遞先前的輔助有效負載,Linux 就會將部分輔助承載消息附加到其他消息的末尾”,該標準似乎並未完全解釋。一種可能的解釋將涉及競爭條件。如果您閱讀“段”的一部分,您將收到輔助數據。也許 Linux 將此解釋為意味著該段的其餘部分不再算作包括輔助數據!因此,當收到一個新段時,它會被合併 - 根據標准或上述差異 1。
如果你想編寫一個最大程度的可移植程序,你應該完全避開這個領域。在使用輔助數據時,使用數據報套接字更為常見。如果您想在技術上渴望提供類似於 POSIX 的所有奇怪平台上工作,那麼您的問題似乎是冒險進入一個黑暗且未經測試的角落。
你可以說 Linux 仍然遵循幾個重要的原則:
- “輔助數據被接收,就好像它與段中的第一個正常數據八位字節一起排隊一樣”。
- 正如您所說,輔助數據永遠不會“連接”。
但是,當您將它與 BSD 行為進行比較時,我不相信 Linux 行為特別*有用。*您描述的程序似乎需要添加特定於 Linux 的解決方法。而且我不知道為什麼 Linux 會期望你這樣做的理由。
在編寫 Linux 核心程式碼時,它可能看起來很明智,但從未被任何程序測試或執行過。
或者它可能由一些主要在這個子集下工作的程式碼執行,但原則上可能有邊緣情況“錯誤”或競爭條件。
如果您無法理解 Linux 的行為及其預期用途,我認為這可以將其視為 Linux 上的“黑暗、未經測試的角落”。