gpt4 book ai didi

c - 两个 child 之间的生产者消费者 fork [C]

转载 作者:太空宇宙 更新时间:2023-11-04 08:07:12 25 4
gpt4 key购买 nike

我对这个著名的问题有疑问。我必须 fork 两个通过管道进行通信的 child (生产者和消费者)。第一个 child (生产者)必须从 stdin 读取字符串,将它们发送给第二个 child (消费者),后者必须将它们转换为大写并打印到 stdout。

我写了一段代码,但它不起作用。

  • 消费者从标准输入中读取字符串并将其写入长度旁边的管道中(包括'\0')。
  • 生产者从管道中读取 MAXC 并将其拆分为两个 var lenght 和 string。当我打印时,长度设置为一个大数字。所以即使是必须转换的字符串也不对。此外,整个程序卡住。

(我试着在这里写代码,但也许我不明白如何正确地做。解释我!谢谢)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#define MAXC 30
static void signalHandler(int signo)
{
return;
}
pid_t pids[2];
int main()
{
int fd[2], i, nW;
size_t l;
char string[MAXC+1], stringtoup[MAXC+1], tmp[MAXC+1];

signal(SIGUSR1, signalHandler);
if(pipe(fd)==0)
{
pids[0]=fork();
if(pids[0]==0)
{
fprintf(stdout, "PID=%d PRODUCER\n", getpid());
close(fd[0]); //produttore
sleep(3);
fprintf(stdout, "Insert strings (max 30 chars), finish with 'end':\n");
fscanf(stdin, "%s", string);
while(1)
{
if(strcmp(tmp, "end")==0)
break;
l=strlen(string)+1;
sprintf(tmp, "%2lu%s", l, string);
printf("%s\n", tmp);
nW=write(fd[1], tmp, (l+2));
printf("nW(PRODUCER)=%d", nW);
if(nW!=(l+2))
{
perror("wrote not whole string");
exit(1);
}
sleep(5);
kill(pids[1], SIGUSR1);
pause();
fprintf(stdout, "Insert string:\n");
fscanf(stdin, "%s", string);
}

exit(0);
}
pids[1]=fork();
if(pids[1]==0)
{
fprintf(stdout, "PID=%d CONSUMER\n", getpid());
close(fd[0]); //consumer

while(1)
{
pause();
read(fd[0], tmp, MAXC+1);
printf("tmp(CONSUMER)=%s\n", tmp);
sscanf(tmp, "%2lu%s", &l, stringtoup);
printf("lenght string(CONSUMER)=%2lu\n", l);
printf("stringtoup=%s\n", stringtoup);
for(i=0; i<l; i++)
stringtoup[i]=toupper(stringtoup[i]);

fprintf(stdout, "%s\n", stringtoup);
fflush(stdout);
sleep(1);
kill(pids[0], SIGUSR1);
}
exit(0);
}

sleep(4);
for(i=0; i<2; i++)
{
waitpid(pids[i], NULL, 0);
fprintf(stdout, "PID=%d exited\n", pids[i]);
}

}
return(0);
}

编辑:代码修复

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#define MAXC 30

pid_t pids[2];
int main()
{
int fd[2], i, nW;
size_t l;
char string[MAXC+1], stringtoup[MAXC+1], tmp[MAXC+1];

if(pipe(fd)==0)
{
pids[0]=fork();
if(pids[0]==0)
{
fprintf(stdout, "PID=%d PRODUCER\n", getpid());
close(fd[0]); //produttore
fprintf(stdout, "Insert strings (max 30 chars), finish with 'end':\n");
fscanf(stdin, "%s", string);
while(1)
{
if(strcmp(string, "end")==0)
break;
l=strlen(string)+1;
sprintf(tmp, "%2lu%s", l, string);
nW=write(fd[1], tmp, (l+2));
if(nW!=(l+2))
{
perror("wrote not whole string");
exit(1);
}
sleep(1);

fscanf(stdin, "%s", string);
}
kill(pids[1], SIGINT);
exit(0);
}
pids[1]=fork();
if(pids[1]==0)
{
fprintf(stdout, "PID=%d CONSUMER\n", getpid());
close(fd[1]); //consumer

while(1)
{
read(fd[0], tmp, MAXC+1);
sscanf(tmp, "%2lu%s", &l, stringtoup);
for(i=0; i<l; i++)
stringtoup[i]=toupper(stringtoup[i]);

fprintf(stdout, "%s\n", stringtoup);
fflush(stdout);
sleep(1);
}
exit(0);
}

for(i=0; i<2; i++)
{
waitpid(pids[i], NULL, 0);
fprintf(stdout, "PID=%d exited\n", pids[i]);
}
}
return(0);
}

最佳答案

通过阅读评论,我猜想问题已更改并涉及 kill-wait 程序。

问题应该是 child 从 fork 开始就与 parent 的内存无关。

第一个 child (生产者)的 pids 数组填充为:

pids[0] == 0;
pids[1] == undefined;

pids[1] 的值永远不会改变,因为这个进程永远不会改变它自己的那部分内存。

第二个 child (消费者)的 pids 数组填充为:

pids[0] == first_child's pid;
pids[1] == 0;

总而言之,只有 parent 可以杀死第二个 child (一个有pids[1]),而不是第一个 child 。所以“生产者”用你不知道的一些 pid 杀死了一个进程(如果运气不好,这可能会导致更广泛的系统问题),所以“消费者”永远不会得到它。

当管道上没有写入器时,read() 函数返回零 (0)。这就是你应该利用的,当它发生时中断并退出。另一方面,当有写入器时,read() 函数会阻塞,直到有内容可读,因此不需要 sleep(1)

正如我所见,程序在执行该随机进程的终止时立即失败,这就是父进程不打印任何子进程退出的原因。但是,如果您删除 kill(...),关闭管道的写入端并检查 read(...) 的返回值,程序将运行为它应该。

另外,一个好的模式是关闭所有你不需要的 fds(即使 exit 函数会在某个时候这样做),所以在产生所有 child 之后父级可以关闭 2 fds(它 必须关闭写端以便读者中断!),并且读者可以在退出前关闭剩余的 fd。下面是需要修复的部分代码。

...
if(pids[0]==0)
{
fprintf(stdout, "PID=%d PRODUCER\n", getpid());
close(fd[0]); //produttore
fprintf(stdout, "Insert strings (max 30 chars), finish with 'end':\n");
fscanf(stdin, "%s", string);
while(1)
{
if(strcmp(string, "end")==0)
break;
l=strlen(string)+1;
sprintf(tmp, "%2lu%s", l, string);
nW=write(fd[1], tmp, (l+2));
if(nW!=(l+2))
{
perror("wrote not whole string"); exit(1);
} sleep(1);
fscanf(stdin, "%s", string);
}
close(fd[1]);
exit(0);
}
pids[1]=fork();
if(pids[1]==0)
{
fprintf(stdout, "PID=%d CONSUMER\n", getpid());
close(fd[1]); //consumer

while(1)
{
if (read(fd[0], tmp, MAXC+1) == 0)
break;
sscanf(tmp, "%2lu%s", &l, stringtoup);
for(i=0; i<l; i++)
stringtoup[i]=toupper(stringtoup[i]);

fprintf(stdout, "%s\n", stringtoup);
fflush(stdout);
sleep(1); // this could be removed
}
close(fd[0]);
exit(0);
}

close(fd[0]);
close(fd[1]);
for(i=0; i<2; i++)
...

关于c - 两个 child 之间的生产者消费者 fork [C],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41942281/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com