我用pipe和fork写了一个小代码。子进程调用写入管道的子函数。父进程调用从管道读取的父函数。
当 fork(( 之后程序的第一次调用转到父函数时,问题就来了。此处的写入结束已关闭。现在的问题是读取调用正在将一些垃圾读入 buf,而 nread 给出的值> 0 。如何防止这种情况发生。
使用 Linux 2.6.32-30-generic 和 gcc 4.4.3。下面是代码::
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#define MSGSIZE 16
void parent(int* p);
void child(int* p);
char* msg1 = "hello";
char* msg2 = "bye";
int main()
{
int pfd[2];
if(pipe(pfd) == -1)
{
printf("Unable to create pipen");
exit(1);
}
fcntl(pfd[0],F_SETFL,O_NDELAY);
if(fork() == 0)
child(pfd);
else
parent(pfd);
return 0;
}
void parent(int p[2])
{
int nread;
char buf[MSGSIZE];
buf[0] = ' ';
close(p[1]);
for(;;)
{
nread = read(p[0] , buf , MSGSIZE);
if(nread == 0)
{
printf("pipe Empty/n");
sleep(1);
}
else
{
if(strcmp(buf,msg2) == 0)
{
printf("End of conversationn");
exit(0);
}
else
printf("MSG=%sn" , buf);
}
}
}
void child(int p[2])
{
int count;
close(p[0]);
for(count = 0 ; count < 3 ; count++)
{
write(p[1],msg1 , MSGSIZE);
sleep(3);
}
write(p[1],msg2,MSGSIZE);
exit(0);
}
一个问题是:
char buf[MSGSIZE];
buf[0] = ' ';
这仅将 buf
中的第一个字符设置为 NULL 终止符:buf
中的其余字符是单元化的。read()
正在尝试读取16
字节,这意味着buf
中的字符不会以空结尾,printf("%s", buf)
要求buf
以空结尾。即使buf
已正确初始化,由于它的大小16
,它仍然不够,但read()
读数也16
,没有为空终止符留下空间。
可能的解决方法是:
char buf[MSGSIZE + 1] = ""; /* +1 added to store the null terminator and
all characters set to 0 (null terminator). */
另一个问题是write()
s(正如Joachim Pileborg所评论的那样(:
write(p[1],msg1 , MSGSIZE);
write(p[1],msg2 , MSGSIZE);
msg1
和msg2
的长度不是 16
字节。更改为:
write(p[1],msg1 , strlen(msg1));
write(p[1],msg2 , strlen(msg2));
此外,read()
失败时返回-1
,因此满足以下条件是不够的:
nread = read(p[0] , buf , MSGSIZE);
if(nread == 0)
{
...
}
还要检查-1
:
else if(nread == -1)
{
fprintf(stderr, "read() failed: %sn", strerror(errno));
}
else
{
...
}
编辑:
请参阅有关阻塞/非阻塞配置问题的回答。
你真正的问题是这一行:
fcntl(pfd[0],F_SETFL,O_NDELAY);
这会将读取尾管设置为非阻塞。因此,每个 read(( 调用将返回与缓冲区中一样多的数据,或者返回 -1 并将 errno 设置为 EWILLBLOCK,如果在此特定时间没有要读取的数据。
但是,您的代码不会处理这种情况,即使您没有读取任何内容,它也只会检查if(nread == 0)
并打印出缓冲区。因此,请删除该行。
如果您不想发送固定大小的消息,或者希望保持读取端非阻塞,事情会变得更加棘手,因为您必须至少考虑以下情况:
- read(( 返回 -1 并将 errno 设置为 EWILLBLOCK(只需再次尝试 read(( (。
- read(( 读取"消息"的前 4 个字节,下一个 read 返回消息的其余部分。
- read(( 读取第一条消息,以及后续消息的一半。
即,您需要在需要处理的消息上使用某种形式的框架/分隔符,除非您只需要进一步流式传输管道的内容。
Read
不会终止输入。
要打印未以 nul 结尾的字符缓冲区,请执行以下操作:
printf("MSQ=%.*sn", nread, buf);
如果要对读取缓冲区进行 nul-终止,则需要进行 2 次更改。
1 .将缓冲区的大小增加到 MSGSIZE+1:
char buf[MSGSIZE + 1];
2 . 每次读取后终止 BUF。
buf[nread > 0 ? nread : 0] = 0; // read returns -1 on error
msg1
和 msg2
是小于 MSGSIZE
的字符串文字。
关于垃圾的事情是这个叫做GIGO的原理:垃圾进,垃圾出。
不想在管道里放垃圾?在厨房水槽上使用排水陷阱。