我正在学习Linux中的进程管理,我需要让子进程和父进程通过管道进行通信。我宣布了两种结构:
typedef struct
{
double min, max, avg; /*Number stats*/
} Stats;
typedef struct {
pid_t pid; /*Process ID*/
int first; /*First number to process*/
int last; /*Last number to process*/
int fd[2]; /*Pipe descriptor*/
Stats stats; /*Stats computed by process*/
}Process_list;
我需要M个子进程来计算一组数字中的一些统计数据(划分工作)。然后,子进程将读取Process_list结构,从头到尾处理数组中的数字(在那里指定)并将计算的统计数据保存到统计结构中。
我遇到的代码最麻烦的部分如下(我正在添加注释而不是其他代码来解释做什么就好了):
int main(int argc, char *argv[])
{
pList=(Process_list *)malloc((M+1)*sizeof(Process_list));
/*Correct allocation is checked*/
int i;
pid_t pid;
for (i=0 ; i<M ; i++) /*M clones*/
{
/*Fork and pipe creation*/
pid = fork ();
pipe(pList[i].fd);
/*If it's the child*/
if ( pid == 0 )
{
pList[i].pid=pid;
printf("CHILD %d: %d a %d\n",i, pList[i].first,pList[i].last);
/*A function here computes stats and saves them OK in the struct*/
/*(e.g. min is saved in pList[i].stats.max, when printed it's a reasonable value)*/
/*Now I need to send the info to the parent*/
ret1=close(pList[i].fd[0]);
/*ret1=0 => OK */
ret2=write(pList[i].fd[1], &(pList[i].stats), sizeof(Stats));
printf("return write: %d\n",ret2);
/*TROUBLE HERE! This isn't even printed. sizeof(Stats)=24 which I think is OK*/
exit(EXIT_SUCCESS);
}
/*Parent*/
else if ( pid > 0 )
{
wait(NULL); /*Is this really neccesary?*/
ret1=close(pList[i].fd[1]);
ret2=read(pList[i].fd[0], &(pList[i].stats), sizeof(Stats));
/*Both ret1 and ret2 = 0*/
printf("[p]Mín: %lf\n Max: %lf\nAverage: %lf\n",pList[i].stats.min,pList[i].stats.max,pList[i].stats.avg);
/*Everything printed here = 0.0000000000*/
}
else /*fork error*/
return -1;
}
所以我的问题是,孩子们完美地计算他们的统计数据,但父母没有收到它们。 write()函数什么都不做。这发生在M的每个值上(包括M = 1 - 只需一个过程)。
另外我不知道是否需要等待(NULL),因为我已经看到一些不使用它的工作示例。但是,如果我不在那里写,父母的printfs将出现在孩子的前面,所以我认为它不会等待孩子写入管道。无论如何,没有它就行不通。
或许结构方法可能不是很好?
非常感谢你!
答案 0 :(得分:3)
您需要在分叉之前创建管道。否则你要创建两个管道。一个在你父母身上,一个在你孩子身上没有连接。
对于孩子获得所有数据后wait
,否则如果孩子在write
阻止管道缓冲区清除,则可能会永远等待。 wait
释放与死去的孩子相关的资源。如果你的孩子没有死,这将等到它。
另一件事:使用fwrite
将数据块(结构数组)写入文件描述符。 fread
阅读。您可以使用FILE*
将文件描述符转换为fdopen
。
int main(int argc, char *argv[])
{
pList=(Process_list *)malloc((M+1)*sizeof(Process_list));
/*Correct allocation is checked*/
int i;
pid_t pid;
for (i=0 ; i<M ; i++) /*M clones*/
{
/*Fork and pipe creation*/
if(pipe(pList[i].fd)) {
perror("pipe");
return -1;
}
pid = fork ();
/*If it's the child*/
if ( pid == 0 )
{
pList[i].pid=pid;
printf("CHILD %d: %d a %d\n",i, pList[i].first,pList[i].last);
/*A function here computes stats and saves them OK in the struct*/
/*(e.g. min is saved in pList[i].stats.max, when printed it's a reasonable value)*/
/*this just closes your read end of the pipe*/
close(pList[i].fd[0]);
/*ret1=0 => OK */
FILE* fp = fdopen(pList[i].fd[1], "w");
while (!fwrite(&(pList[i].stats), sizeof(Stats), 1, fp) && !feof(fp));
if (feof(fp)) {
fclose(fp);
fprintf(stderr, "reader closed his end of the pipe\n");
return -1;
}
// sends eof to reader
fclose(fp);
printf("%d bytes written successfully",sizeof(Stats));
/*TROUBLE HERE! This isn't even printed. sizeof(Stats)=24 which I think is OK*/
exit(EXIT_SUCCESS);
}
/*Parent*/
else if ( pid > 0 )
{
// this is actually nessesary to ensure that the pipe properly closes
close(pList[i].fd[1]);
FILE* fp = fdopen(pList[i].fd[0], "r");
if (!fread(&(pList[i].stats), sizeof(Stats), 1, fp)) {
fprintf(stderr, "writer sent eof, but not enough data\n");
return -1;
}
fclose(fp);
printf("[p]Mín: %lf\n Max: %lf\nAverage: %lf\n",pList[i].stats.min,pList[i].stats.max,pList[i].stats.avg);
/*Everything printed here = 0.0000000000*/
wait(0);
}
else /*fork error*/
return -1;
}
答案 1 :(得分:0)
对于被终止的孩子,执行wait()
允许系统释放与孩子相关的资源;如果没有执行wait()
,那么被终止的孩子将继续留在&#34; zombie&#34;州。它是一种安全的做法,总是很好用。参考:man -s2 wait
wait(NULL)
系统调用意味着,父进程将等待任何子进程(在您的情况下,它将等待刚刚分叉的子进程),并且不会存储子进程返回的状态。如果您想要更多地控制等待,请查看:man -s2 waitpid
。基本上,wait()
是waitpid()
的香草形式。
现在,管道部分。您正在子进程内创建管道。如何通过父进程访问它?实际上它以这种方式工作:if a pipe is created in a parent process, the pipe fd's will be copied to the child process.
而不是相反。
请参阅:man -s2 pipe
因此,在父进程中创建管道,然后发出fork()
,然后使用子进程中的管道fd发送给父进程。孩子现在可以write()
数据进入管道写入fd,父代码现在可以read()
子进程写的内容。
您编写了从子级到父级的单向IPC通信。因此,您关闭了孩子中的读取fd,并且您也在关闭父级中的写入fd。所以,这样你只能在孩子中写作,只能在父母中阅读。逻辑正确实现。那里没有错。
使用pipe()(在父级中创建管道然后分叉)修复逻辑问题后,您的代码应该按预期工作。管道不应该适用于任何数据类型或您编写的结构。