如何实现生产者消费者使用流程?

How to implement producer-consumer using processes?

我正在尝试使用 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;
}

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

...如有任何帮助,我们将不胜感激。

关于错误:User defined signal 1: 30,执行速度可能会引发竞争条件,导致在注册处理函数之前终止。请记住,每个 signal 都有一个默认配置(或操作)。对于 SIGUSR1SIGUSR2S,配置是 term,(来自下面链接的 signal(7) 页面中的 table)

   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来检查是否有任何系统调用流量发生。

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

Another collection of detailed information on Signal Handling

除了@ryyker 提到的,另一个问题是,当您的 parent 进程尝试使用全局变量 child 向 child 发送信号时,child没有机会 运行 并收集 pid。所以 parent 将向垃圾 pid 发送信号。更好的方法是在 parent 中使用 pid 变量,在 child 中使用 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;                                     
}                                               

尝试在 C++ 中使用信号量而不是信号。 信号在 OS 中真正用于特殊用途,而信号量用于进程同步。

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

以下伪代码会有所帮助。

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
 }

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

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