簡體   English   中英

檢查 `read` 是否會在不消耗任何數據的情況下返回 EOF

[英]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_PEEKMSG_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM