Linux
為什麼我似乎使用這種 bash 管道結構失去了數據?
我正在嘗試組合一些這樣的程序(請忽略任何額外的包含,這是正在進行的繁重工作):
pv -q -l -L 1 < input.csv | ./repeat <(nc "host" 1234)
重複程序的來源如下所示:
#include <fcntl.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/epoll.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <iostream> #include <string> inline std::string readline(int fd, const size_t len, const char delim = '\n') { std::string result; char c = 0; for(size_t i=0; i < len; i++) { const int read_result = read(fd, &c, sizeof(c)); if(read_result != sizeof(c)) break; else { result += c; if(c == delim) break; } } return result; } int main(int argc, char ** argv) { constexpr int max_events = 10; const int fd_stdin = fileno(stdin); if (fd_stdin < 0) { std::cerr << "#Failed to setup standard input" << std::endl; return -1; } /* General poll setup */ int epoll_fd = epoll_create1(0); if(epoll_fd == -1) perror("epoll_create1: "); { struct epoll_event event; event.events = EPOLLIN; event.data.fd = fd_stdin; const int result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd_stdin, &event); if(result == -1) std::cerr << "epoll_ctl add for fd " << fd_stdin << " failed: " << strerror(errno) << std::endl; } if (argc > 1) { for (int i = 1; i < argc; i++) { const char * filename = argv[i]; const int fd = open(filename, O_RDONLY); if (fd < 0) std::cerr << "#Error opening file " << filename << ": error #" << errno << ": " << strerror(errno) << std::endl; else { struct epoll_event event; event.events = EPOLLIN; event.data.fd = fd; const int result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event); if(result == -1) std::cerr << "epoll_ctl add for fd " << fd << "(" << filename << ") failed: " << strerror(errno) << std::endl; else std::cerr << "Added fd " << fd << " (" << filename << ") to epoll!" << std::endl; } } } struct epoll_event events[max_events]; while(int event_count = epoll_wait(epoll_fd, events, max_events, -1)) { for (int i = 0; i < event_count; i++) { const std::string line = readline(events[i].data.fd, 512); if(line.length() > 0) std::cout << line << std::endl; } } return 0; }
我注意到了這一點:
- 當我只使用管道時
./repeat
,一切都按預期工作。- 當我只使用流程替換時,一切都按預期工作。
- 當我使用程序替換封裝 pv 時,一切都按預期工作。
- 但是,當我使用特定結構時,我似乎會從標準輸入中失去數據(單個字元)!
我嘗試了以下方法:
- 我試圖禁用所有程序之間
pv
和./repeat
使用的管道上的緩衝stdbuf -i0 -o0 -e0
,但這似乎不起作用。- 我已將 epoll 換成 poll,不起作用。
- 當我查看
pv
和./repeat
with之間的流時tee stream.csv
,這看起來是正確的。- 我曾經
strace
看到發生了什麼,我看到很多單字節讀取(如預期的那樣),它們還表明數據失去了。我想知道發生了什麼事?或者我可以做些什麼來進一步調查?
因為
nc
裡面的命令<(...)
也會從標準輸入讀取。更簡單的例子:
$ nc -l 9999 >/tmp/foo & [1] 5659 $ echo text | cat <(nc -N localhost 9999) - [1]+ Done nc -l 9999 > /tmp/foo
去哪兒了
text
?通過網貓。$ cat /tmp/foo text
你的程序和你的程序
nc
競爭相同的標準輸入,並nc
得到一些。
使用 E/POLLIN 返回的 epoll() 或 poll() 只會告訴您單個read()可能不會阻塞。
並不是說您將能夠像您一樣在換行符之前執行很多一個字節的 read()。
我說可能是因為使用 E/POLLIN 返回的 epoll() 之後的 read() 可能仍會阻塞。
您的程式碼還將嘗試讀取過去的 EOF,並完全忽略任何 read() 錯誤。