c - 孩子之间的管道
问题描述
我想做一个程序,首先创建3个进程(A),然后再创建一个进程(B),并且这些第一个进程必须写入管道中,最后一个进程在每次该进程写入时读取。我尝试了一些方法,但我不知道该怎么做,因为进程 (B) 是在进程 (A) 之后创建的
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_CHILDREN 3
int main(int argc, char *argv[])
{
pid_t pid;
int fd[2];
char buffer[100];
char str[] = "Hello";
char str2[] = "Hello2";
char str3[] = "Hello3";
for(int num_process = 0; num_process < MAX_CHILDREN; num_process++)
{
if(pipe(fd) == -1)
{
perror( "pipe Failed" );
continue;
}
pid = fork();
if(pid < 0)
{
perror("fork failed");
exit(1);
}
if(pid == 0)
{ //child code
if(num_process == 0){
printf("Child %i (pid= %i) send string %s\n", num_process, getpid(),str);
write(fd[1],str,strlen(str));
}
if(num_process == 1){
printf("Child %i (pid= %i) send string %s\n", num_process, getpid(),str2);
write(fd[1],str2,strlen(str2));
}
if(num_process == 2){
printf("Child %i (pid= %i) send string %s\n", num_process, getpid(),str3);
write(fd[1],str3,strlen(str3));
}
exit(0);
}
else{//parent
printf("Im parent %i\n",getpid());
wait(NULL);
}
}
//Creating another child process from parent, this process recieves string sent from
//childs
pid = fork();
if(pid < 0)
{
perror("fork failed");
exit(1);
}
if(pid == 0){//child
printf("The new process %i read fd pipe\n",getpid());
if( read(fd[0],buffer,sizeof(buffer)) <= 0) //read pipe
{
perror("error read");
exit( EXIT_FAILURE );
}
printf("String readed : %s\n",buffer);
}
else{//parent
wait(NULL);
}
return 0;
}
解决方案
您需要对代码进行一些更改。父母不应该真正等待它的孩子,直到他们全部启动。由于您为前三个子项中的每一个都创建了一个新管道,因此您需要跟踪正在使用的文件描述符。您应该为此使用数组,以及要发送的字符串。和系统都read()
不会write()
调用空终止字符串,并且您不会告诉它在末尾写入空字节,因此您需要告诉printf()
打印正确的信息。
这些变化和其他各种变化导致:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_CHILDREN 3
int main(void)
{
pid_t pid;
int fd[MAX_CHILDREN][2];
char buffer[100];
const char *str[MAX_CHILDREN] = { "Hello 1", "Hello 2", "Hello 3" };
for (int i = 0; i < MAX_CHILDREN; i++)
{
if (pipe(fd[i]) == -1)
{
perror("pipe Failed");
exit(1);
}
pid = fork();
if (pid < 0)
{
perror("fork failed");
exit(1);
}
if (pid == 0)
{
printf("Child %i (pid= %i) send string %s\n", i + 1, getpid(), str[i]);
write(fd[i][1], str[i], strlen(str[i]));
exit(i + 1);
}
}
pid = fork();
if (pid < 0)
{
perror("fork failed");
exit(1);
}
if (pid == 0)
{
printf("The new process %i read fd pipe\n", getpid());
for (int i = MAX_CHILDREN; i-- > 0; )
{
int nbytes;
if ((nbytes = read(fd[i][0], buffer, sizeof(buffer))) <= 0)
{
perror("error read");
exit(EXIT_FAILURE);
}
printf("String read: %.*s\n", nbytes, buffer);
}
exit(4);
}
int corpse;
int status;
while ((corpse = wait(&status)) >= 0)
printf("child %d exited with status 0x%.4X\n", corpse, status);
return 0;
}
运行时,输出可能是:
Child 1 (pid= 91027) send string Hello 1
Child 2 (pid= 91028) send string Hello 2
Child 3 (pid= 91029) send string Hello 3
The new process 91030 read fd pipe
String read: Hello 3
String read: Hello 2
String read: Hello 1
child 91027 exited with status 0x0100
child 91028 exited with status 0x0200
child 91029 exited with status 0x0300
child 91030 exited with status 0x0400
我颠倒了阅读循环中元素的顺序,主要是为了好玩。for (int i = 0; i < MAX_CHILDREN; i++)
如果您愿意,可以改用传统循环。
虽然它在这个程序中并不重要,但您没有在子级或父级中关闭足够多的文件描述符。父级应该关闭管道的写端;它不会使用它们。孩子们应该关闭管道的读取端;他们不会使用它们。此外,第二个和第三个孩子应该关闭为第一个打开的管道,第三个应该关闭第二个的管道,因为他们也不会使用这些。如果您不这样做并且第四个孩子循环等待 EOF(返回 0 字节),它将挂起。
经验法则:如果您
将管道的一端连接到标准输入或标准输出,请
尽快dup2()
关闭返回的两个原始文件描述符
。pipe()
特别是,您应该在使用任何
exec*()
函数系列之前关闭它们。
如果您使用
dup()
或
复制描述符,该规则也fcntl()
适用F_DUPFD
请注意,该程序的另一种设计将在循环外创建一个管道,并且孩子们都将写入同一个管道。您可能希望在消息字符串中添加换行符,以便结果是分开的。您肯定会考虑在第四个孩子中循环读取,并且您需要担心管道是否正确关闭,等等。编写代码将是一个值得的子练习。
推荐阅读
- c# - Groove Music 如何根据图像颜色创建多色阴影效果?
- android - 在 android 中使用 exoplayer 的这两个视频 url 有什么区别?
- html - 使用 XSLT 的子组
- python - PyTorch 按行索引将值粘贴到张量中,列索引增加
- flutter - 如何在颤动中将文档快照转换为流数据?
- python-3.6 - 在 Python 3 中并行化“for”循环
- html - 如何在 Html 中创建表格
- mysql - Laravel 复杂的雄辩关系
- c - 为什么我不能从 malloc 获得 2 GB?
- javascript - 我正在网站上设置 PayPal,并使用液体将购物车价值拉入 JS