midma101
midma101

Reputation: 139

Problems sending/handling signals across two processes on same computer - C programming

I'm having trouble handling signals between two process I have running on my computer. scheduler.c is sending the signals and producer.c receiving them. The producer is supposed to print "Printing n" where n is incremented by one each time a SIGUSR1 is received. I have tried using both signal and sigaction to handle the signals but neither is working for me.

scheduler.c:

/*
 * scheduler.c
 */


#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

int n = 1;


int main(int argc, char *argv[])
{
    int a = 0; // This variable will be used for the switch later

    // Check to ensure correct number of command line arguments
    if(argc != 2){
        printf("Usage error. Wrong number of arguments\n");
        return 1;
    }

    // Grab PID of producer.c
    int producer_pid = atoi(argv[1]);       

    while(1){
        printf("Choose an Option: \n");
        printf("1. Request_Production\n");
        printf("2. Stop_Producer\n");
        printf("3. Stop_Scheduler\n");
        scanf("%d", &a);

        switch( a ) 
        {
            case 1:
                kill(producer_pid, 16);     //Send SIGUSR1 to producer.c
                break;

            case 2:
                kill(producer_pid, 2);      //Send SIGINT to producer.c
                break;

            // Successfully exit program
            case 3:
                return 0;

            // Invalid Choice
            default :
                printf("Invalid choice\n");
        }
    }
}

producer.c:

/*
 * producer.c
 */

#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

int n = 1;

void sigusr1(int signo)
{

    printf("Producing %d", n);
    n++;
}


int main()
{


    struct sigaction act;
    sigset_t block_mask;
    sigfillset(&block_mask);
    act.sa_handler = sigusr1;
    act.sa_mask = block_mask;
    act.sa_flags = 0;

    if(sigaction(SIGUSR1, &act, NULL) == 0){
        printf("success");
    }


    while(1) {

        sleep(2);
        fflush(stdout);
    }

}

Upvotes: 0

Views: 2155

Answers (2)

Jonathan Leffler
Jonathan Leffler

Reputation: 754880

This code works for me (on Mac OS X 10.7.5):

producer.c

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

static volatile sig_atomic_t n = 0;

static void sigusr1(int signo)
{
    n += signo / SIGUSR1;
}

int main(void)
{
    struct sigaction act;
    sigset_t block_mask;
    sigfillset(&block_mask);
    act.sa_handler = sigusr1;
    act.sa_mask = block_mask;
    act.sa_flags = 0;

    if (sigaction(SIGUSR1, &act, NULL) == 0)
    {
        printf("success %d\n", (int)getpid());
        while (1)
        {
            pause();
            printf("Producer: %d\n", n);
            fflush(stdout);
        }
    }
}

scheduler.c

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

int main(int argc, char *argv[])
{
    int a = 0; // This variable will be used for the switch later

    // Check to ensure correct number of command line arguments
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s pid\n", argv[0]);
        return 1;
    }

    // Grab PID of producer.c
    int producer_pid = atoi(argv[1]);       

    while(1)
    {
        printf("Choose an Option: \n");
        printf("1. Request Production\n");
        printf("2. Stop Producer\n");
        printf("3. Stop Scheduler\n");
        scanf("%d", &a);

        switch (a) 
        {
            case 1:
                if (kill(producer_pid, SIGUSR1) != 0)
                    fprintf(stderr, "Failed to send signal %d to %d\n", SIGUSR1, producer_pid);
                break;

            case 2:
                if (kill(producer_pid, SIGTERM) != 0)
                    fprintf(stderr, "Failed to send signal %d to %d\n", SIGTERM, producer_pid);
                break;

            // Successfully exit program
            case 3:
                return 0;

            // Invalid Choice
            default :
                fprintf(stderr, "Invalid choice (%d)\n", a);
                break;
        }
    }
}

Sample output

$ (./producer &)
$ success 40119
$ ./scheduler 40119
Choose an Option: 
1. Request Production
2. Stop Producer
3. Stop Scheduler
1
Choose an Option: 
1. Request Production
2. Stop Producer
3. Stop Scheduler
Producer: 1
1
Choose an Option: 
1. Request Production
2. Stop Producer
3. Stop Scheduler
Producer: 2
1
Choose an Option: 
1. Request Production
2. Stop Producer
3. Stop Scheduler
Producer: 3
1
Choose an Option: 
1. Request Production
2. Stop Producer
3. Stop Scheduler
Producer: 4
1
Choose an Option: 
1. Request Production
2. Stop Producer
3. Stop Scheduler
Producer: 5
1
Choose an Option: 
1. Request Production
2. Stop Producer
3. Stop Scheduler
Producer: 6
1
Choose an Option: 
1. Request Production
2. Stop Producer
3. Stop Scheduler
Producer: 7
1
Choose an Option: 
1. Request Production
2. Stop Producer
3. Stop Scheduler
Producer: 8
2
Choose an Option: 
1. Request Production
2. Stop Producer
3. Stop Scheduler
1
Failed to send signal 30 to 40119
Choose an Option: 
1. Request Production
2. Stop Producer
3. Stop Scheduler
3
$

What changed?

Various changes, but the key ones are:

  1. Ensure that output messages end with a newline.
  2. Make n into a volatile sig_atomic_t variable; that's what the C standard says you can access in a signal handler.
  3. Have the main loop in the producer pause() and then print. The pause() system call only returns when interrupted by a signal.
  4. Use symbolic signal names in the scheduler too.
  5. Have the scheduler send SIGTERM rather than SIGINT to terminate the producer. If the producer is run in background, it is ignoring interrupts.
  6. Have the scheduler identify when the kill() calls fail.
  7. Have the producer identify its PID.

I've removed the superfluous headers from the file headings.

The funny signo / SIGUSR1 in the signal handler avoids warnings about unused arguments; it serves no other purpose. As shown, the programs compile cleanly under:

gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
    -Wold-style-definition scheduler.c -o scheduler  
gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
    -Wold-style-definition producer.c -o producer

This is using GCC 4.7.1.

Upvotes: 2

sstn
sstn

Reputation: 3069

One remark:

There are functions which are safe and others which are not safe to be called from signal handlers.

printf may not be called from a signal handler. write on the other hand is safe.

The list is specified by POSIX-1, but details may vary between operating systems. For Linux, you will find the list in the signal(7):

http://linux.die.net/man/7/signal

Upvotes: 1

Related Questions