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

时间:2018-01-22 12:27:46

标签: c linux operating-system producer-consumer

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

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

4 个答案:

答案 0 :(得分:2)

...我们将不胜感激。

关于错误:用户定义信号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(和family)实际上不应该用在信号处理程序中 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 的。

答案 1 :(得分:0)

除了@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;                                     
}                                               

答案 2 :(得分:0)

尝试在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
 }

答案 3 :(得分:0)

After a lot of research and reading all of the suggestions I finally managed to make the program work. Here is my implementation. If you see any mistakes or perhaps something could have been done better, then feel free to correct my code. I'm open to suggestions.

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