В настоящее время я изучаю сигналы на языке C в операционной системе Linux.
У меня есть родительский процесс, который хочет отправить структуру целочисленных значений с именем struct Data
дочернему процессу, который он развил, используя структуру siginfo_t, которая является параметром обработчика сигнала, когда мы определяем обработчик сигнала с помощью sigcation: void handler(int, siginfo_t*, void*)
родительский процесс определяет union sigval
и устанавливает поле sival_ptr
в объединении в значение
(void*) data
где данные — это struct Data
, содержащий два целых числа.
Затем родитель использует sigqueue(child_pid, SIGUSR1, my_sigval)
Дочерний процесс использует sigaction, как я упоминал ранее, и настраивает обработчик сигнала SIGUSR1 с помощью флага SA_SIGINFO
.
Внутри обработчика сигнала в дочернем коде я определил указатель struct Data* data
и выполните эту строку: data = (struct Data*) (si->si_value.sival_ptr)
а затем я пытаюсь распечатать данные, которые я отправил от родителя.
Я получаю следующее: дочерний процесс завершается до того, как напечатает значения внутри struct Data data
, и все, что находится до этой строки, выполняется и печатается на экране, как и ожидалось.
Где я неправ? разве я не передаю указатель на struct Data
от родителя?
или я делаю что-то не так в процессе приведения внутри функции обработчика дочернего сигнала?
Вот коды:
Родитель:
#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;
}
ребенок:
#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;
}
@EricPostpischil Спасибо, я подумал, что это так, сразу после публикации сообщения. Знаешь ли ты, как мне добиться того, чего я хочу?
Используйте правильный инструмент для работы. Вы мучаете sigqueue
[и себя ;-)], пытаясь сделать то, что сообщения SysV IPC (msgsnd/msgrcv
) уже делают лучше/чище. Пример см. в моем ответе: В C хранение данных в локальных переменных в потоках похоже на создание локальной копии? AKA Имеет ли смысл эта синхронизация Threadpool? Поскольку ваша структура данных небольшая, отправьте ее копию, а не указатель. Или вы можете отправить указатель, если используете общую память (shm*
и/или mmap
). Я с большим успехом использовал это в коммерческих продуктах R/T.
При использовании msgsnd/msgrcv
после msgsnd
получатель (выполняющий msgrcv
) немедленно помечается как работоспособный. И, что более важно, системные вызовы вызывают немедленное перепланирование, поэтому приемник [скорее всего] запустится немедленно. Это сокращает задержку (время от выдачи msgsnd
до момента возврата msgrcv
).
Ваш план вряд ли сработает, поскольку указатели действительны только внутри процесса, поскольку каждый процесс имеет свое собственное адресное пространство.
Даже если у вас есть память, совместно используемая двумя процессами, блок может не быть найден по одному и тому же адресу в обоих процессах.
Поскольку здесь существуют отношения «родитель-потомок», вы можете настроить некоторую общую память перед разветвлением и использовать эту память для обмена данными. Убедитесь, что один процесс не может записывать в память одновременно с чтением, и наоборот.
(а) Передача указателя на данные в родительском процессе не может работать. Каждый процесс имеет свою собственную виртуальную память. Его данные находятся в его виртуальной памяти, а не в виртуальной памяти другого процесса, за исключением страниц памяти, которые предназначены для совместного использования между процессами. Если дочерний элемент получит адрес, то, что находится по этому адресу в его виртуальной памяти, будет отличаться от того, что находится по этому адресу в виртуальной памяти родителя. (б) Набор функций, которые можно вызывать в обработчике сигнала, очень ограничен, и
printf
среди них нет.