0

I'm trying to implement a producer-consumer application using 1 parent process and 1 child process. The program should work like this:

1 - The parent process is the producer and the child process is the consumer.
2 - The producer creates a file, the consumer removes the file.
3 - After the file has been created, the parent process sends a SIGUSR1 signal to the child process which then removes the file and sends a SIGUSR2 signal to the parent, signaling it that the file can be created again.

I've tried implementing this problem but I keep getting this error:

User defined signal 1: 30.   

I don't really understand what could be the problem. I've just started learning about process and signals and maybe I'm missing something. Any help would be appreciated. Here's my implementation:

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

Edit: I forgot to mention that there can only be one file at a time.

ryyker
  • 22,849
  • 3
  • 43
  • 87
sziko
  • 53
  • 2
  • 11
  • I haven't read your question in very detail, but it seems that there're race conditions in your app, so I have an alternative for you and maybe useful to you: you can setup a memory region shared between parent process and child process and use it as a mutex data structure(e.g. spinlock), and it could be helpful to introduce mutex between parent process and child process. – Gamer.Godot Jan 23 '18 at 00:12
  • Does mutex work between processes? I thought it only worked between multiple threads. – sziko Jan 23 '18 at 09:49
  • @sziko - Yes, there are _[process-shared mutexes](http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_mutexattr_getpshared.html)_. (This would be a work around, but does not address the primary issue in your question). Have you experimented with the synchronization link in the answer below? If you can get that to work, it will put you on the path to understanding what may be causing your code hang-ups. – ryyker Jan 23 '18 at 13:10

4 Answers4

2

...Any help would be appreciated.

Regarding the Error: User defined signal 1: 30, it is possible that the speed of execution is precipitating a race condition, causing termination before your handler functions are registered. Keep in mind, each signal has a default disposition (or action). For SIGUSR1 and SIGUSR2S the disposition is term, (from table in signal(7) page linked below)

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

(Note the value 30 listed by SIGUSR1 matches the exit condition you cite.)

The implication here would be that your handler functions had not registered before the first encounter with SIGUSR1, causing the default action of terminating your application and throwing the signal related error.

The relationship between synchronization and timing come to mind as something to look at. I found several things written on synchronization, and linked one below.

Timing may be implicitly addressed with an adequate approach to synchronization, negating the need for any explicit execution flow control functions. However, if help is needed, experiment with the sleep family of functions.

Here are a couple of other general suggestions:

1) printf (and family) should really not be used in a signal handler.
2) But, if used, a newline ( \n ) is a good idea (which you have), or use fflush to force a write.
3) Add a strace() call to check if any system call traffic is occurring.

Another code example of Synchronizing using signal().

Take a look at the signal(7) page.. (which is a lot of information, but implies why using printf or fprintf inside a signal handler in the first place may not be a good idea.)

Another collection of detailed information on Signal Handling.

ryyker
  • 22,849
  • 3
  • 43
  • 87
0

Apart from what @ryyker mentioned, another problem is that by the time your parent process tries to signal the child using global variable child, the child has not got a chance to run and collect the pid. So the parent will send signal to a junk pid. A better approach is to use the pid variable in the parent and getppid() in the child. Here is the code which seems to give desired output

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;                                     
}                                               
Ketan Mukadam
  • 789
  • 3
  • 7
  • this doesn't seem to work ether. I think there is a deadlock between the parent and the child process. The output is: Iter 1 Iter 2 and after it waits. – sziko Jan 23 '18 at 09:30
0

Try using semaphores in c++ instead of signals. Signals truly serve special purposes in OS whereas semaphores serve process synchronization.

Posix named semaphores in c++ can be used across processes.

The following pseudocode will help.

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
 }
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;
}
sziko
  • 53
  • 2
  • 11