我用管道和叉子编写了一个小代码。子进程调用写入管道的子函数。父进程调用从管道读取的父函数。
当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 pipe\n");
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] = '\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 conversation\n");
exit(0);
}
else
printf("MSG=%s\n" , 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);
}
答案 0 :(得分:4)
一个问题是:
char buf[MSGSIZE];
buf[0] = '\0';
这只将buf
中的第一个字符设置为空终止符: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: %s\n", strerror(errno));
}
else
{
...
}
编辑:
请参阅nos关于阻止/非阻止配置问题的回答。
答案 1 :(得分:3)
你真正的问题是这一行:
fcntl(pfd[0],F_SETFL,O_NDELAY);
这会将读取结束管道设置为非阻塞。因此,每次read()调用都将返回与缓冲区中一样多的数据,如果在此特定时间没有要读取的数据,则返回-1并将errno设置为EWOULDBLOCK。
但是,您的代码不处理这种情况,它只检查if(nread == 0)
并打印出缓冲区,即使您没有读取任何内容。所以删除该行。
如果您不想发送固定大小的消息,或者希望保持读取结束不阻塞,则事情变得更加棘手,因为您必须考虑至少这些情况:
即。除非你只需要进一步传输管道的内容,否则你需要在你需要处理的消息上使用某种形式的框架/分隔符。
答案 2 :(得分:0)
Read
不会终止输入。
要打印非空终止的字符缓冲区,请执行以下操作:
printf("MSQ=%.*s\n", nread, buf);
如果要终止读缓冲区,则需要进行2次更改。
1。将缓冲区的大小增加到MSGSIZE + 1:
char buf[MSGSIZE + 1];
2。每次阅读后都会终止buf。
buf[nread > 0 ? nread : 0] = 0; // read returns -1 on error
答案 3 :(得分:0)
msg1
和msg2
是字符串文字,小于MSGSIZE
。
关于垃圾的事情就是这个叫GIGO的原则:垃圾输入,垃圾输出。
不要管道里有垃圾吗?在厨房水槽上使用排水管。