我编写了一个简短的C程序,以便学习IPC基础。该程序包含一个子进程,它试图向管道和父进程写入两次,该进程尝试读取这两个消息。邮件位于text1
和text2
。执行后,父进程只读取第一条消息。
子进程的输出如下:
I write text1 in pipe
I write text2 in pipe
通过管道从子级接收的父级文本为:Hi Dady, I'm your child and text1!
。
以下是代码:
main(void){
int fd0[2], nbytes;
pid_t childpid;
ssize_t errorfi;
char text1[] = "Hi Dady, I'm your child and text1!\n";
char text2[] = "Hi Dady, I'm your child and text2!\n";
char readbuffer[80],msg[80];
pipe(fd0);
if((childpid = fork()) == -1)
{
perror("fork");
_exit(1);
}
if(childpid == 0)
{ //child process
close(fd0[0]);
fprintf (stderr, "I write text1 in pipe\n");
errorfi = (write(fd0[1], text1, (strlen(text1)+1)));
if (errorfi <0 )
{
fprintf (stderr, "errorfi = %d\n", errorfi);
fprintf (stderr, "error writting texto1 in fd0 pipe %s\n",strerror(errorfi));
_exit (EXIT_FAILURE);
}
fprintf (stderr, "I write text2 in pipe\n");
errorfi = (write(fd0[1], text2, (strlen(text2)+1)));
if (errorfi <0 )
{
fprintf (stderr, "errorfi = %d\n", errorfi);
fprintf (stderr, "error writting texto2 in fd0 pipe %s\n",strerror (errorfi));
_exit (EXIT_FAILURE);
}
_exit(0);
else
{ //parent process
close(fd0[1]);
nbytes = read(fd0[0], readbuffer, sizeof(readbuffer));
printf("text received in parent from child trough pipe:%s\n", readbuffer);
}
return(0);}
为什么我只阅读text1
?
答案 0 :(得分:1)
这个问题比AB_的答案更糟糕。 (现已删除:此答案建议再做一次阅读)
父母和孩子之间可能存在竞争条件。如果孩子只能在父母阅读第一条信息之前写一次,那么一切都会好的(如果你读了两次)。但如果孩子在父母阅读之前写了两次,那么第一次(或唯一)阅读会得到72个字符......但是你只会打印第一条消息!
为了证明这一点,我略微修改了父部分,等待0.5秒并显示nbytes:
{ //parent process
close(fd0[1]);
usleep(500);
nbytes = read(fd0[0], readbuffer, sizeof(readbuffer));
printf("text received in parent from child trough pipe (%d) :%s\n",
nbytes,readbuffer); }
我得到了:
I write text1 in pipe
I write text2 in pipe
text received in parent from child trough pipe (72) :Hi Dady, I'm your child and text1!
为什么它会获得72个字符并仅在上半部分打印?因为readbuffer[36] == '\0'
!并且所有C字符串函数都使用NULL作为字符串终止符。
因此当你在管道或套接字中传递文本时,规则是,除非你对它们进行特殊处理,否则永远不要发送终止空值(*)
你应该在孩子身上使用:
errorfi = write(fd0[1], text2, strlen(text1));
仅发送非空字符并避免竞争条件的风险。
(*)根据Mat的评论编辑:在串行通道(管道,插座或...串行线)中,您必须使用大小+数据或分隔符来清楚地标识数据块。我通常避免使用NULL作为分隔符(在C或C ++程序中),因为它阻止了所有字符串函数的使用。但是你要处理它并继续处理超过第一个null,NULL是一个很好的分隔符,因为它没有在文本中使用(这就是为什么它是C分隔符)。
并且不要忘记:单个读取的大小(至少在串行线和管道上)可以大于或小于单个写入的大小:这里,例如,睡眠连接两个写入。