[英]Check if a `read` would return EOF without consuming any data
我有一個 C 程序,它通過 (Linux) pipe 從另一個程序接收數據。如果 pipe 在寫入任何數據之前關閉,我希望程序表現不同。
這樣做的自然方法是嘗試從 pipe 讀取並檢查我是否得到EOF
,但是如果有任何可用數據,它會消耗 pipe 中的一些數據,並且(據我所知)沒有辦法放置數據“返回”在 pipe 中。
我要檢查 pipe 是否為空的程序部分與我處理數據的位置相去甚遠,因此在此之前我寧願不必處理保存第一次讀取的數據。
有沒有辦法檢查 pipe 是否為空( read
將返回EOF
)而不消耗任何數據(如果它不為空)?
注意:如果 pipe 尚未寫入或關閉,我確實希望阻止它。
If you used Unix domain stream sockets instead of pipes – meaning you replace your pipe(fds)
calls with socketpair(AF_UNIX, SOCK_STREAM, 0, fds)
–, you could use recv(fd, dummybuffer, 1, MSG_PEEK)
to read/receive一個字節的數據,而不將其從接收緩沖區中刪除。
如果您不想阻塞,您可以將MSG_PEEK
與MSG_DONTWAIT
結合使用,如果您想阻塞直到可以填充整個緩沖區,則可以與MSG_WAITALL
結合使用。
Unix 域 stream 套接字和 pipe 之間的差異很小。 stream 套接字是雙向的,但您可以使用shutdown(fd, SHUT_WR)
(或SHUT_RD
)關閉“寫端”(分別為“讀端”),這意味着如果另一端試圖從套接字讀取,他們'將立即獲得流結束( read()
, recv()
等返回 0)。 (關閉“讀取端”意味着當另一端嘗試寫入套接字時,他們會得到EPIPE
。)
現在,我什至想不出為什么與 pipe 一起工作的程序不能與 Unix 域 stream 套接字對一起工作的原因。
如果您使用命名管道,您確實需要將mkfifo()
和open()
更改為socket(AF_UNIX, SOCK_STREAM, 0)
然后將bind()
更改為套接字地址。 read()
、 write()
,甚至更高級別的標准 I/O 工具都可以在 Unix 域 stream 套接字之上正常工作(使用fdopen()
將套接字描述符轉換為FILE
句柄)。
如果您無法修改閱讀器,您可以創建一個插入openat()
的最小動態庫(這是當前 C 庫在fopen()
下使用的),為除套接字路徑之外的所有內容調用原始openat()
,例如在環境變量中命名, 而是創建一個套接字並綁定到該套接字路徑。 執行閱讀器二進制文件時,您只需將LD_PRELOAD
設置為指向此插入庫。
換句話說,我相信從管道切換到 Unix 域 stream sockets 沒有真正的障礙。
您不能將recv()
與管道一起使用,因為管道是在 Linux 中使用特殊文件系統而不是 sockets 實現的。
不,沒有辦法做你描述的事情。 確定您是否已到達不可查找文件(例如 pipe)末尾的方法是嘗試從中讀取。 這不僅是自然的方式,也是方式。
但這會消耗 pipe 中的一些數據(如果有的話),
是的。
並且(據我所知)沒有辦法將數據“放回”pipe 中。
那要看。 如果您正在使用 POSIX read()
,那么不會。 如果您將 pipe 結尾包裝在FILE
中並使用 stdio 函數讀取它,那么就有ungetc()
。
盡管如此,這:
我想檢查 pipe 是否為空的程序部分離我處理數據的地方很遠
似乎是一個設計問題。 在您真正獲得數據或看到 EOF 之前,您無法知道是否會獲得數據。 pipe 寫入端的進程可以在對 pipe 執行任何操作之前延遲任意時間,即使該進程由您提供,您也無法完全控制其行為的這方面。 因此,從某種意義上說,在您准備好使用數據之前嘗試檢查 EOF 沒有多大意義,因為您不能在沒有阻塞的情況下獲得答案。
,所以我寧願不必處理保存我第一次讀取的數據直到那時。
我想您必須避免在沒有數據要處理的情況下執行某種重量級初始化。 好的,但我看不出有什么大不了的。 無論如何,您都需要提供用於讀取數據的存儲空間。 這樣的事情有什么問題:
void consume_pipe_data(int fd) {
char buffer[BUFFER_SIZE];
ssize_t count;
count = read(fd, buffer, BUFFER_SIZE);
if (count == 0) {
handle_no_data();
return;
} else if (count > 0) {
perform_expensive_initialization();
}
do {
if (count == -1) {
handle_error();
return;
}
consume_data(buffer);
count = read(fd, buffer, BUFFER_SIZE);
} while (count);
}
關鍵不是這一定是您的程序的適當結構,而是可以構建程序,以便從初始讀取中存儲數據(如果有的話)非常干凈和自然。
你可以做一個虛擬寫。 如果您的 stdin 已達到 eof,它是一種非阻塞機制來確定您是否已達到 EOF,而無需任何更復雜的工具。
if( write( fileno(stdin), 0, 0 ) != 0 )
return 1; // Is end-of-file.
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.