【问题标题】:producer-consumer between two children forked [C]两个孩子之间的生产者-消费者分叉 [C]
【发布时间】:2017-02-02 11:22:52
【问题描述】:

我对这个著名的问题有疑问。 我必须分叉两个通过管道进行通信的孩子(生产者和消费者)。 第一个孩子(生产者)必须从标准输入读取字符串,将它们发送给第二个孩子(消费者),第二个孩子(消费者)必须将它们转换为大写并打印到标准输出。

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

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

(我试图在这里写代码,但可能我不明白如何正确执行。解释一下!谢谢)

#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);
}

【问题讨论】:

  • 考虑到您没有显示任何代码,您希望得到什么样的答案?要在您的问题中包含代码,只需 edit 它,添加代码,然后按 Ctrl-K 添加代码格式。
  • 完成。感谢您的帮助!
  • 但是我已经修复了代码(在读取字符串'end'后还有一个关于杀死子进程的错误)。但是还有一个问题:父亲不等孩子。调用第一个 waitpid 应该足够快。

标签: c pipe fork producer-consumer


【解决方案1】:

通过阅读 cmets,我猜问题已经改变,涉及到 kill-wait 程序。

问题应该是孩子从被分叉的那一刻起就与父母的记忆无关。

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

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

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

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

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

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

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

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

另外,一个好的模式是关闭所有你不需要的 fds(即使 exit 函数会在某些时候这样做),所以在产生所有孩子之后的父级可以关闭 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++)
...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-12
    • 1970-01-01
    • 1970-01-01
    • 2018-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多