我有一个小程序,它与服务器建立SSL连接,然后将数据从STDIN复制到服务器,将数据从服务器复制到STDOUT(很像openssl s_client
)。我使用boost :: asio来读写STDIN,STDOUT和SSL套接字。问题是我不能从我的程序中管道来自另一个程序的数据,例如
cat | myprog
我键入一行并按Enter键,它全部按原样运行:文本行通过我的程序,响应的服务器,响应打印到我的控制台。下次发送命令时,cat
会发送命令,但在下一次read()
呼叫时失败(我输入以&#34开头的行; echo"):
echo foo
foo
echo bar
cat: -bar
: Resource temporarily unavailable
为什么会这样?
strace
从cat
read(0, "echo foo\n", 32768) = 9
write(1, "echo foo\n", 9) = 9
read(0, "echo bar\n", 32768) = 9
write(1, "echo bar\n", 9) = 9
read(0, 0xa02c000, 32768) = -1 EAGAIN (Resource temporarily unavailable)
理论#1:boost :: asio为了它的目的将STDIN设置为非阻塞,但它也影响cat
的STDIN。如果我将代码从预处理器更改为fork()
,允许它继承STDIN和STDERR,并捕获asio可以直接读取的STDOUT,这不应该成为问题。这样asio就不必触摸STDIN了。这已经完成,strace
确认文件描述符0已被保留。
理论#2:当我的程序写入STDOUT时,它会将cat
的STDIN从阻塞变为非阻塞。我不认为是这种情况:
14211 read(0, <unfinished ...>
//myprog (pid 14209) does epoll stuff here
//cat (pid 14211) receives my command
14211 <... read resumed> "echo foo\n", 32768) = 9
//more epoll
//cat writes
14211 write(1, "echo foo\n", 9) = 9
14209 <... epoll_wait resumed> {{EPOLLIN, {u32=136519504, u64=136519504}}}, 128, -1) = 1
//cat starts reading again
14211 read(0, <unfinished ...>
//my prog receives command from cat
14209 readv(3, [{"echo foo\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 512}], 1) = 9
//sends it to the server (encrypted)
14209 sendmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"\27\3\3\0'T\252\251\317w\255\310}h\322\222%\204\326FA\271\302\241\376\237\7\377\275\250o\262"..., 44}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 44
14209 epoll_wait(5, {{EPOLLIN|EPOLLOUT, {u32=136514856, u64=136514856}}}, 128, 0) = 1
14209 readv(3, 0xbfd8be04, 1) = -1 EAGAIN (Resource temporarily unavailable)
//receives response
14209 recvmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"\27\3\3\0\"\357\351\3276a\233\356C\326z\317\252\344\27A\f\t|\f\307\275u\344\\\351 \320"..., 17408}], msg_controllen=0, msg_flags=0}, 0) = 39
//myprog sets non-blocking IO on STDOUT
14209 ioctl(1, FIONBIO, [1]) = 0
//writes out response
14209 writev(1, [{"foo\n", 4}], 1) = 4
//myprog does more epoll stuff again
//cat receives seccond command, not that that this call started before myprog wrote anything or called ioctl()
14211 <... read resumed> "echo bar\n", 32768) = 9
14209 <... epoll_wait resumed> {{EPOLLOUT, {u32=136514720, u64=136514720}}}, 128, -1) = 1
14211 write(1, "echo bar\n", 9 <unfinished ...>
14209 epoll_wait(5, <unfinished ...>
14211 <... write resumed> ) = 9
14209 <... epoll_wait resumed> {{EPOLLIN, {u32=136519504, u64=136519504}}}, 128, -1) = 1
14211 read(0, <unfinished ...>
14209 readv(3, <unfinished ...>
//cat's next read fails
14211 <... read resumed> 0x8d6f000, 32768) = -1 EAGAIN (Resource temporarily unavailable)
我的程序确实将自己的STDOUT更改为非阻塞,但我可以看到它单独留下fd 0。 full trace可用。