1

I have a fork business and I have 4 child processes. I want to send a signal from parent process to each of 4 active child processes before they terminated. If child process id is odd, I send SIGUSR1, otherwise, I send SIGUSR2. I also want to print the receive time of each SIGUSR1 and SIGUSR2 signals in children processes.

Here is my source code:

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

void sigusr1();
void sigusr2();

void main()
{
int status;
pid_t pid[4];
int process_id;

for (int i = 0; i < 4; i++)
{
    pid[i] = fork();

    if (pid[i] == 0)
    {
        process_id = getpid();
        if (process_id % 2 == 1)
        {
            signal(SIGUSR1, sigusr1);
            sleep(1);
        }
        else
        {
            signal(SIGUSR2, sigusr2);
            sleep(1);
        }
        exit(&status);
    }
    else
    {
        printf("\nPARENT: sending SIGUSR1\n\n");
        kill(pid, SIGUSR1);

        printf("\nPARENT: sending SIGUSR2\n\n");
        kill(pid, SIGUSR2);
    }
  }
}

void sigusr1()

{
    signal(SIGUSR1, sigusr1);
    printf("CHILD:  SIGUSR1\n");
    time_t current_time;
    char *c_time_string;
    c_time_string = ctime(&current_time);

    printf("SIGUSR1 received time is %s", c_time_string);
}

void sigusr2()

{
    signal(SIGUSR2, sigusr2);
    printf("CHILD: SIGUSR2\n");

    time_t current_time;
    char *c_time_string;
    c_time_string = ctime(&current_time);
    printf("SIGUSR2 received time is %s", c_time_string);
}

I got following output 4 times:

PARENT: sending SIGUSR1
PARENT: sending SIGUSR2

I cannot get signals in child processes. Can you help me to solve this issue?

  • 2
    There is no guarantee that the children will have called `signal()` before the parent calls `kill()`. – pmacfarlane Apr 14 '23 at 15:24
  • There is also no guarantee that your child processes will get any particular process IDs. They could all get process IDs that are multiples of 2, which will add the signal handler on `SIGUSR2`. And then you send `SIGUSR1` to them, killing them. – pmacfarlane Apr 14 '23 at 15:37
  • Note that signal handlers are called with an argument, an `int` holding the signal number. Strictly, your signal handlers should expect that argument. Also note that using `signal()` instead of `sigaction()` isn't necessarily a good idea. Your `main()` should be declared as returning `int`, even if you don't include `return 0;` at the end of the function. You are calling `ctime()` with an uninitialized value for `current_time` — not good! You are signalling your children far too quickly, as already pointed out. It would probably be best to set both signal handlers before any forking! – Jonathan Leffler Apr 14 '23 at 19:07
  • You should not be ignoring warnings about mismatched function pointer types, nor about using uninitialized variables. If you aren't getting such warnings, you need to turn up the warning level on your compiler `-Werror -Wall -Wextra` is a good starting point — you can add extra warnings if you want, depending on your coding standards, etc. – Jonathan Leffler Apr 14 '23 at 19:47
  • 1
    Also note [How to avoid using `printf()` in a signal handler?](https://stackoverflow.com/q/16891019/15168) – Jonathan Leffler Apr 14 '23 at 19:48
  • Incidentally, `exit()` takes an `int` argument; you're passing an `int *` (to an uninitialized integer). Most odd. Your compiler should be complaining, and you should be heeding your compiler. You have `kill(pid, SIGUSR1);`, but you're passing an array (effectively 'pointer to `int`) where the system call expects a plain `int`. You need `kill(pid[I], SIGUSR1)`. You can't just make up the interface to system calls! – Jonathan Leffler Apr 14 '23 at 19:53

1 Answers1

2

When I compile your code, I get a lot of errors – some of them would be warnings except that I use -Werror:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -fno-common sig31.c -o sig31
sig31.c:8:1: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
    8 | void sigusr1();
      | ^~~~
sig31.c:9:1: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
    9 | void sigusr2();
      | ^~~~
sig31.c:11:6: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
   11 | void main()
      |      ^~~~
sig31.c:11:6: error: return type of ‘main’ is not ‘int’ [-Werror=main]
sig31.c: In function ‘main’:
sig31.c:34:14: error: passing argument 1 of ‘exit’ makes integer from pointer without a cast [-Werror=int-conversion]
   34 |         exit(&status);
      |              ^~~~~~~
      |              |
      |              int *
In file included from sig31.c:4:
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/stdlib.h:145:15: note: expected ‘int’ but argument is of type ‘int *’
  145 | void     exit(int) __dead2;
      |               ^~~
sig31.c:39:14: error: passing argument 1 of ‘kill’ makes integer from pointer without a cast [-Werror=int-conversion]
   39 |         kill(pid, SIGUSR1);
      |              ^~~
      |              |
      |              pid_t * {aka int *}
In file included from sig31.c:2:
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/signal.h:80:14: note: expected ‘pid_t’ {aka ‘int’} but argument is of type ‘pid_t *’ {aka ‘int *’}
   80 | int     kill(pid_t, int) __DARWIN_ALIAS(kill);
      |              ^~~~~
sig31.c:42:14: error: passing argument 1 of ‘kill’ makes integer from pointer without a cast [-Werror=int-conversion]
   42 |         kill(pid, SIGUSR2);
      |              ^~~
      |              |
      |              pid_t * {aka int *}
In file included from sig31.c:2:
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/signal.h:80:14: note: expected ‘pid_t’ {aka ‘int’} but argument is of type ‘pid_t *’ {aka ‘int *’}
   80 | int     kill(pid_t, int) __DARWIN_ALIAS(kill);
      |              ^~~~~
sig31.c: At top level:
sig31.c:47:6: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
   47 | void sigusr1()
      |      ^~~~~~~
sig31.c: In function ‘sigusr1’:
sig31.c:54:21: error: implicit declaration of function ‘ctime’ [-Werror=implicit-function-declaration]
   54 |     c_time_string = ctime(&current_time);
      |                     ^~~~~
sig31.c:54:19: error: assignment to ‘char *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
   54 |     c_time_string = ctime(&current_time);
      |                   ^
sig31.c: At top level:
sig31.c:59:6: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
   59 | void sigusr2()
      |      ^~~~~~~
sig31.c: In function ‘sigusr2’:
sig31.c:67:19: error: assignment to ‘char *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
   67 |     c_time_string = ctime(&current_time);
      |                   ^
cc1: all warnings being treated as errors

Most of those are issues I mentioned in comments — the complaint about assignment to ‘char *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion] is because you didn't include <time.h>.

Here is reworked code. It still ignores the issue of How to avoid using printf() in a signal handler?.

/* SO 7601-6541 */
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/wait.h>

static void sigusr1(int signum);
static void sigusr2(int signum);

int main(void)
{
    pid_t pid[4];
    struct sigaction sa = { 0 };

    sa.sa_handler = sigusr1;
    sigaction(SIGUSR1, &sa, 0);
    sa.sa_handler = sigusr2;
    sigaction(SIGUSR2, &sa, 0);

    for (int i = 0; i < 4; i++)
    {
        pid[i] = fork();

        if (pid[i] == 0)
        {
            sleep(1);
            exit(0);
        }
        else
        {
            /* Snooze for 10 ms */
            nanosleep(&(struct timespec){ .tv_sec = 0, .tv_nsec = 10000000 }, 0);
            if (pid[i] % 2 == 1)
            {
                printf("PARENT: sending SIGUSR1 to %d\n", pid[i]);
                kill(pid[i], SIGUSR1);
            }
            else
            {
                printf("PARENT: sending SIGUSR2 to %d\n", pid[i]);
                kill(pid[i], SIGUSR2);
            }
        }
    }

    int corpse;
    int status;
    while ((corpse = wait(&status)) > 0)
        printf("%d: child %d exited with status 0x%.4X\n", getpid(), corpse, status);

    return 0;
}

static void sigusr1(int signum)
{
    printf("CHILD %d: %d SIGUSR1\n", getpid(), signum);
    time_t current_time = time(0);
    printf("CHILD %d: SIGUSR1 received - time is %s", getpid(), ctime(&current_time));
}

static void sigusr2(int signum)
{
    printf("CHILD %d: %d SIGUSR2\n", getpid(), signum);
    time_t current_time = time(0);
    printf("CHILD %d: SIGUSR2 received - time is %s", getpid(), ctime(&current_time));
}

When I run that, I get output such as:

PARENT: sending SIGUSR2 to 56842
CHILD 56842: 31 SIGUSR2
CHILD 56842: SIGUSR2 received - time is Fri Apr 14 14:14:24 2023
PARENT: sending SIGUSR1 to 56843
CHILD 56843: 30 SIGUSR1
CHILD 56843: SIGUSR1 received - time is Fri Apr 14 14:14:24 2023
PARENT: sending SIGUSR2 to 56844
CHILD 56844: 31 SIGUSR2
CHILD 56844: SIGUSR2 received - time is Fri Apr 14 14:14:24 2023
PARENT: sending SIGUSR1 to 56845
56841: child 56844 exited with status 0x0000
56841: child 56843 exited with status 0x0000
56841: child 56842 exited with status 0x0000
CHILD 56845: 30 SIGUSR1
CHILD 56845: SIGUSR1 received - time is Fri Apr 14 14:14:24 2023
56841: child 56845 exited with status 0x0000

Without the 10 ms nanosleep, the child processes were sometimes exiting with status 0x0004 — which indicates they died from signal SIGILL. I didn't experiment with smaller naps than 10 ms.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Thanks, actually it works. However, receive time of signals for 4 child processes are exactly the same. Shouldn't it be different? – cereyanci23 Apr 15 '23 at 08:31
  • As discussed elsewhere, you can see that I got the same time for all processes. The children don't get to sleep for a whole second; they receive a signal within a few milliseconds of being forked. You would need to use a fine-grained timer such as [`clock_gettime()`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_gettime.html) to see the difference. – Jonathan Leffler Apr 16 '23 at 03:48