我编写了一个简短的 C 程序来学习 IPC 基础。该程序由一个尝试向管道写入两次的子进程和尝试读取两条消息的父进程组成。消息位于 text1
和 text2
中。执行后,父进程只读取第一条消息。
子进程的输出如下:
I write text1 in pipe
I write text2 in pipe
父级通过管道从子级收到的文本是:嗨爸爸,我是你的 child 和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
?
最佳答案
想想这个问题比 AB_ 的答案更糟糕。 (现已删除:建议再读一遍此答案)
您的 parent 和 child 之间可能存在竞争状况。如果 child 在 parent 阅读第一条消息之前只能写一次,那么一切都会好起来的(前提是你读了两次)。但是,如果 child 在 parent 读取之前写入两次,则第一次(或唯一一次)读取将获得 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,读缓冲区); }
我得到:
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 作为字符串终止符。
所以规则是当您在管道中或通过套接字传递文本时,永远不要发送终止空值,除非您对它们进行特殊处理 (*)
你应该在 child 中使用:
errorfi = write(fd0[1], text2, strlen(text1));
仅发送非空字符并避免竞争条件的风险。
(*) 根据 Mat 的评论进行编辑:在串行 channel (管道、套接字或...串行线)中,必须使用大小 + 数据或分隔符来清楚地标识数据 block 。我通常避免将 NULL 作为分隔符(在 C 或 C++ 程序中),因为它会阻止所有字符串函数的使用。但如果您处理好它并继续处理第一个 null 之后的内容,NULL 是一个很好的分隔符,因为它不在文本中使用(这就是它是 C 分隔符的原因)。
不要忘记:单个读取的大小(至少在串行线路和管道上)可以大于或小于单个写入的大小:例如,这里 sleep 连接两个写入。
关于无法在 C 中的管道中写入两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27176673/