【问题标题】:How to implement producer-consumer using processes?如何使用流程实现生产者-消费者?
【发布时间】:2018-01-22 12:27:46
【问题描述】:

我正在尝试使用 1 个父进程和 1 个子进程来实现生产者-消费者应用程序。该程序应该像这样工作:

1 - 父进程是生产者,子进程是消费者。
2 - 生产者创建文件,消费者删除文件。
3 - 创建文件后,父进程向子进程发送 SIGUSR1 信号,然后子进程删除文件并向父进程发送 SIGUSR2 信号,表示可以再次创建文件。

我已尝试实现此问题,但我不断收到此错误:

User defined signal 1: 30.   

我真的不明白可能是什么问题。我刚刚开始学习过程和信号,也许我错过了一些东西。任何帮助,将不胜感激。这是我的实现:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

pid_t child, parent;

void producer()
{
    system("touch file");
    printf("File was created.\n");
}

void consumer()
{
    system("rm file");
    printf("File was deleted.\n");
    kill(parent, SIGUSR2); // signal -> file can created by parent
}

int main(void)
{
    system("touch file");

    pid_t pid = fork();

    for(int i = 0; i < 10; ++i)
    {
        if(pid < 0) // error fork()
        {
            perror("fork()");
            return -1;
        }
        else if(pid == 0) // child proces - consumer
        {
                child = getpid();
                signal(SIGUSR1, consumer);
                pause();
        }
        else // parent process - producer
        {
                parent = getpid();
                signal(SIGUSR2, producer);
                // signal -> file can be deleted by child
                kill(child, SIGUSR1); 
        }
    }

    return 0;
}

编辑:我忘了说一次只能有一个文件。

【问题讨论】:

  • 我没有详细阅读您的问题,但您的应用中似乎存在竞争条件,因此我为您提供了一个替代方案,并且可能对您有用:您可以设置内存父进程和子进程之间共享的区域并将其用作互斥数据结构(例如自旋锁),并且在父进程和子进程之间引入互斥体可能会有所帮助。
  • 互斥锁是否在进程之间工作?我以为它只能在多个线程之间工作。
  • @sziko - 是的,有 process-shared mutexes。 (这将是一种解决方法,但不能解决您问题中的主要问题)。您是否尝试过以下答案中的同步链接?如果你能让它发挥作用,它将让你了解可能导致你的代码挂起的原因。

标签: c linux operating-system producer-consumer


【解决方案1】:

...任何帮助将不胜感激。

关于错误:用户定义的信号 1:30,执行速度可能会导致竞争条件,导致在您的处理程序函数注册之前终止。请记住,每个signal 都有一个默认处置(或操作)。对于SIGUSR1SIGUSR2S,处置是term,(来自下面链接的信号(7)页面中的表格)

   SIGUSR1   30,10,16    Term    User-defined signal 1
   SIGUSR2   31,12,17    Term    User-defined signal 2

(注意SIGUSR1 列出的值 30 与您引用的退出条件匹配。)

这里的含义是您的处理函数在第一次遇到SIGUSR1 之前没有注册,导致默认操作终止您的应用程序并抛出signal 相关错误。

同步和定时之间的关系是值得关注的。我找到了几篇关于同步的东西,并在下面链接了一篇。

可以通过适当的同步方法来隐式地解决时序问题,从而消除对任何显式执行流控制功能的需求。但是,如果需要帮助,请尝试使用 sleep family of functions

以下是其他一些一般性建议:

1) printf(和家族)真的不应该在信号处理程序中使用。
2) 但是,如果使用,换行符 (\n) 是个好主意(你有),或者使用 fflush 来强制写入。
3) 添加一个strace() call 来检查是否有任何系统调用流量发生。

Another code example of Synchronizing using signal().

Take a look at the signal(7) page.。 (这是很多信息,但暗示了为什么首先在信号处理程序中使用 printf 或 fprintf 可能不是一个好主意。)

Another collection of detailed information on Signal Handling

【讨论】:

    【解决方案2】:

    除了@ryyker 提到的,另一个问题是,当您的父进程尝试使用全局变量child 向子进程发出信号时,子进程还没有机会运行并收集pid。所以父母会向垃圾pid发送信号。更好的方法是在父级中使用pid 变量,在子级中使用getppid()。这是似乎提供所需输出的代码

    void producer()                                                      
    {                                                                    
        system("touch file");                                            
        printf("File was created.\n");                                   
    }                                                                    
    
    void consumer()                                                      
    {                                                                    
        system("rm file");                                               
        printf("File was deleted.\n");                                   
        kill(getppid(), SIGUSR2); // signal -> file can created by parent
    }                                                                    
    int main(void)                                    
    {                                                 
        system("touch file");                         
    
        pid_t pid = fork();                           
        if(pid < 0) // error fork()                   
        {                                             
            perror("fork()");                         
            return -1;                                
        }                                             
        if(pid > 0) {  //parent
           signal(SIGUSR2, producer);                 
        }                                             
        else { //child                                
           signal(SIGUSR1, consumer);                 
        }                                             
        for(int i = 0; i < 10; ++i)                   
        {                                             
            if(pid == 0) {// child proces - consumer  
               pause();                               
            }                                         
            else // parent process - producer         
            {                                         
               printf("Iter %d\n",i);                 
               kill(pid, SIGUSR1);                    
               pause();                               
            }                                         
        }                                             
        return 0;                                     
    }                                               
    

    【讨论】:

    • 这似乎不起作用。我认为父进程和子进程之间存在僵局。输出是:Iter 1 Iter 2 和它等待之后。
    【解决方案3】:

    尝试在 C++ 中使用信号量而不是信号。 信号在操作系统中真正服务于特殊目的,而信号量则服务于进程同步。

    c++ 中的 Posix 命名信号量可以跨进程使用。

    下面的伪代码会有所帮助。

    Semaphore Full,Empty;
     ------
    Producer()      //producer 
    {
        waitfor(Empty);//wait for an empty slot
        system("touch file");
        printf("File was created.\n");
        Signal(Full);  //Signal one slot is full
    
    }
    Consumer()       //Consumer
    {
          WaitFor(Full); //wait for producer to produce
          system("rm file");
          printf("File was deleted.\n");
          Signal(Empty);//Signal that it has consumed, so one empty slot created
     }
    

    【讨论】:

      【解决方案4】:

      经过大量研究和阅读所有建议后,我终于设法使该程序正常运行。这是我的实现。如果您发现任何错误或者可能可以做得更好,请随时更正我的代码。我愿意接受建议。

      #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h>
      #include <signal.h>
      
      void signal_handler(int signal_number)
      {
          sigset_t mask;
      
          if(sigemptyset(&mask) == -1 || sigfillset(&mask) == -1)
          {// initialize signal set || block all signals
              perror("Failed to initialize the signal mask.");
              return;
          }
      
          switch(signal_number)
          {
              case SIGUSR1:
              {
                  if(sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
                  { // entering critical zone
                      perror("sigprocmask(1)");
                      return;
                  } //---------------------
      
                  sleep(1);
                  system("rm file");          /* critical zone */
                  puts("File was removed.");
      
                    //--------------------
                  if(sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
                  {// exiting critical zone
                      perror("1 : sigprocmask()");
                      return;
                  }
      
                  break;
              }
              case SIGUSR2:
              {
      
                  if(sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
                  {// entering critical zone
                      perror("2 : sigprocmask()");
                      return;
                  } //---------------------
      
                  sleep(1);
                  system("touch file");
                  puts("File was created.");  /* critical zone */
      
      
                    // --------------------
                  if(sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
                  {// exiting critical zone
                      perror("sigprocmask(2)");
                      return;
                  }
      
                  break;
              }
          }
      }
      
      int main(void)
      {
          pid_t pid = fork();
      
          struct sigaction sa;
          sa.sa_handler = &signal_handler; // handler function
          sa.sa_flags = SA_RESTART;
      
          sigaction(SIGUSR1, &sa, NULL);
          sigaction(SIGUSR2, &sa, NULL);
      
          if(pid < 0)
          {
              perror("fork()");
              return -1;
          }
      
          for(int i = 0; i < 10; ++i)
          {
              if(pid > 0) // parent - producer
              {
                  sleep(2);
                  // signal -> file was created
                  kill(pid, SIGUSR1);
                  pause();
              }
              else // child - consumer
              {
                  pause();
                  // signal -> file was removed
                  kill(getppid(), SIGUSR2);
              }
          }
      
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-12
        • 1970-01-01
        • 2018-08-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-10
        • 1970-01-01
        相关资源
        最近更新 更多