Search code examples
clinuxunixsignalsipc

Trying to pass a "pointer to a struct Data" to the signal handler using sigqueue function from one process to another, using the siginfo_t struct


I'm currrently learning about signals in C language under Linux operating system. I have a parent process that wants to send a struct of integer values called struct Data to a child process that it forked - using the siginfo_t struct that is a parameter to the signal handler when we define a signal handler using sigcation: void handler(int, siginfo_t*, void*)

the parent process defines a union sigval, and sets the sival_ptr field in the union to (void*) data where data is a struct Data that contains two integers. The parent then uses sigqueue(child_pid, SIGUSR1, my_sigval)

The child process uses sigaction as I mentioned earlier, and it sets up the signal handler for SIGUSR1 signal using the flag SA_SIGINFO.

Inside the signal handler in the child code, I defined a pointer struct Data* data and do this line: data = (struct Data*) (si->si_value.sival_ptr) and then I try to print the data that I sent from the parent.

The behaviour I get is that the child process exits before it prints the values inside the struct Data data, and everything before that line gets executed and printed on the screen as expected.

Where did I go wrong? am I not passing the pointer to the struct Data correcrly from the parent?

or am I doing something wrong in the casting process inside the child signal handler function?

Here are the codes:

Parent:


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

#define MY_SIGNAL SIGUSR1

// Define a structure to hold two integers
struct Data {
    int value1;
    int value2;
};

int main() {
    // Prepare data structure
    struct Data* data = (struct Data*) malloc(sizeof(struct Data));
    data->value1 = 10;
    data->value2 = 20;

    // Fork a child process
    pid_t pid = fork();

    if (pid == -1) {
        perror("fork failed");
        return 1;
    } else if (pid == 0) { // Child process
        // Child process logic
        execlp("./c2","c2",  NULL);
                  //
    } else { // Parent process
        // Sending the signal with a pointer to the data structure
        union sigval value;
        value.sival_ptr = (void*) data;
//        value.sival_int = 7;
        sleep(1);
        sigqueue(pid, MY_SIGNAL, value);
        sleep(1);
    }

    wait(NULL);
    return 0;
}

child:

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

#define MY_SIGNAL SIGUSR1

// Define a structure to hold two integers
struct Data {
    int value1;
    int value2;
};

// Function to handle the signal
void handler(int sig, siginfo_t *si, void *unused) {
    printf("Received signal: %d\n", sig);
//    printf("Received signal value: %d\n", si->si_value.sival_int);
    struct Data* data;
    data = (struct Data*)(si->si_value.sival_ptr);
    printf("Received values: %d, %d\n", data->value1, data->value2);
    // Copy the received data into the allocated memory
}

int main() {
    // Set up signal handler
    struct sigaction sa;
    sa.sa_sigaction = handler;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    sigaction(MY_SIGNAL, &sa, NULL);

    
    pause();

    return 0;
}


Solution

  • Your plan can't possibly work since pointers are only valid within a process since each process has its own address space.

    Even if you had memory that was shared between two processes, the block might not be found at the same address in both processes.


    Since there's a parent-child relationship here, you could setup some shared memory before the fork, and use that memory to exchange data. Make sure one process can't write to memory at the same time as it is being read and vice-versa.