Я пытаюсь написать обработчик сигнала, который должен знать pid процесса, отправляющего сигнал. Мне не удалось получить что-то полезное из siginfo_t, переданного в мой обработчик на macOS 10.14 с Xcode 10.
Я сократил свой код до приведенного ниже минимального образца, чтобы продемонстрировать проблему. В этом примере я порождаю дочерний процесс для отправки сигнала, который хочу протестировать, по умолчанию это SIGTERM, но ни один другой сигнал, который я пробовал, не работает лучше.
Предполагая, что вы хотите создать и протестировать это на Mac, вы, вероятно, захотите сказать lldb, чтобы он не останавливался при получении сигнала. Вы можете использовать эту команду lldb: pro hand -p true -s false SIGTERM.
Я также компилирую с C++, но я считаю, что исключил все это, и теперь образец кода должен быть чистым C.
Обратите внимание, что не имеет значения, исходит ли сигнал от дочернего, терминального или другого процесса, результатом всегда будет то, что si_pid всегда равен 0 (вместе со всем, кроме si_signo и si_addr). Не имеет значения, сколько раз я отправляю сигнал, так что это, похоже, не просто состояние гонки.
Как я могу получить pid процесса, отправляющего сигнал в macOS 10.14? Я не помню, чтобы у меня была эта проблема 10.12, которую я использовал раньше.
Это всего лишь образец, демонстрирующий проблему, поэтому, пожалуйста, игнорируйте все, что на самом деле не вызывает проблемы.
Если кажется, что код должен работать так, как я ожидал, то мне было бы интересно увидеть комментарии о системах, на которых он работает.
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
volatile sig_atomic_t histogram[3] = {0,0,0};
volatile sig_atomic_t signaled = 0;
const int testsig = SIGTERM;
void sigaction_handler(int sig, siginfo_t* info, void* context)
{
switch (info->si_pid) {
case 0:
case 1:
histogram[info->si_pid]++;
break;
default:
histogram[2]++;
break;
}
signaled = 1;
}
int main(int argc, const char * argv[]) {
pid_t mainpid = getpid();
pid_t pid = fork();
if (pid == 0) {
while (kill(mainpid, 0) == 0) {
sleep(1);
kill(mainpid, testsig);
}
_exit(0);
}
struct sigaction sigAction;
memset( &sigAction, 0, sizeof( sigAction ) );
sigAction.sa_sigaction = sigaction_handler;
sigemptyset (&sigAction.sa_mask);
sigAction.sa_flags = SA_SIGINFO;
sigaction(testsig, &sigAction, NULL);
while (1) {
if (signaled) {
printf("pid 0: %d, pid 1: %d, others: %d\n", histogram[0], histogram[1], histogram[2]);
signaled = 0;
}
sleep(1);
}
}
@Shawn спасибо, я отредактировал код.
Обработчик сигналов также вызывает неопределенное поведение в соответствии с POSIX, потому что он назначает переменные, которые не являются переменными volatile sig_atomic_t. (Ссылка). Я могу изобразить цикл в основном, всегда видящий signaled как ложный с агрессивными настройками оптимизации (но не может дублироваться с помощью gcc или clang в Linux). Однако у вас нет Mac для реального полезного тестирования.
@Shawn Я ценю это, однако мой IRL-код этого не делает. Я подумаю об обновлении образца с помощью совместимого кода, но не понимаю, как это может быть проблемой. Отладчик тоже всегда говорит, что это 0.
Я почти уверен, что struct sigaction sigAction = {}; - это недопустимый код C. Фактически, cppreference.com заявляет «В C список инициализаторов в фигурных скобках не может быть пустым.», Но я не собираюсь анализировать 6.7.9 Инициализация стандарта C и проверять, что это правильно.
@AndrewHenle, конечно, я обновил код соответствующим образом. Все еще не проблема :(
Теперь он полностью неинициализирован, кроме явно установленных вами полей. memset( &sigAction, 0, sizeof( sigAction ) ); вместе с нынешним sigemptyset() были бы хороши.
@AndrewHenle на Mac это единственные поля, но я все равно добавлю это.





Сейчас я использую macOS Mojave 10.14.1.
How can I get the pid of the process sending the signal on macOS 10.14? I don't recall having this issue on 10.12 which is what I was using before.
Следующий код просто соответствует вашему желанию. Если вы отправляете SIGTERM, вы можете увидеть pid процесса отправителя.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
static void hdl (int sig, siginfo_t *siginfo, void *context)
{
printf ("Sending PID: %ld, UID: %ld\n",
(long)siginfo->si_pid, (long)siginfo->si_uid);
}
int main (int argc, char *argv[])
{
struct sigaction act;
fprintf(stderr, "%i pp %i\n",getpid(), getppid());
memset (&act, '\0', sizeof(act));
/* Use the sa_sigaction field because the handles has two additional parameters */
act.sa_sigaction = &hdl;
/* The SA_SIGINFO flag tells sigaction() to use the sa_sigaction field, not sa_handler. */
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGTERM, &act, NULL) < 0) {
perror ("sigaction");
return 1;
}
while (1)
sleep (10);
return 0;
}
Для вашего кода
Rule of thumb: Don't forget to carry burial procedures out even though you are sure that child process ends prior parent process. By invoking
wait(...)you tell the operating system that I'm done my things for my child so now you can clean allocated fields etc.
Я бы предпочел инициализировать сигнальные утилиты перед разветвлением. А что, если родительский процесс не имеет возможности зарегистрировать сигнальное действие? Более того, я не понимаю, зачем вы обрабатываете кейсы 0 и 1 в switch. По сути дела, случаи не затрагиваются, поэтому всегда опускаются.
Кроме того, вы не использовали break в своем состоянии if в main(). Через некоторое время она не попадает в if, но следующее обстоятельство, которое не ожидается и желательно, заключается в том, что программа навсегда остается в цикле while(). Я бы предпочел привести signaled в состояние петли while().
Наконец, что не менее важно, из-за вызова sleep() в дочернем процессе, пока signaled не выключен, 0, SIGTERM успешно ловится несколько раз. Когда сигнализируется 0, цикл останавливается.
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <memory.h>
#include <sys/wait.h>
volatile sig_atomic_t histogram[3] = {0,0,0};
volatile sig_atomic_t signaled = 0;
const int testsig = SIGTERM;
void sigaction_handler(int sig, siginfo_t* info, void* context)
{
switch (info->si_pid) {
case 0:
case 1:
histogram[info->si_pid]++;
break;
default:
fprintf(stderr, "sender pid -> %i\n", info->si_pid);
histogram[2]++;
break;
}
signaled = 1;
}
int main(int argc, const char * argv[]) {
struct sigaction sigAction;
memset( &sigAction, 0, sizeof( sigAction ) );
sigAction.sa_sigaction = sigaction_handler;
sigemptyset (&sigAction.sa_mask);
sigAction.sa_flags = SA_SIGINFO;
sigaction(testsig, &sigAction, NULL);
pid_t mainpid = getpid();
pid_t pid = fork();
if (pid == 0) {
fprintf(stderr, "my pid -> %i parent's pid-> %i\n", getpid(), getppid());
if (kill(mainpid, 0) == 0) { // signals are not queued not need loop
sleep(1);
kill(mainpid, testsig);
}
_exit(0);
} else {
wait(NULL); // play with this line to see what the difference is
while ( signaled ) {
printf("pid 0: %d, pid 1: %d, others: %d\n", histogram[0], histogram[1], histogram[2]);
signaled = 0;
sleep(1);
}
// wait(NULL); // play with this line to see what the difference is
}
}
странно, ваш безвилочный образец у меня тоже не работает на моей машине. и pid, и uid по-прежнему равны 0.
Чтобы было ясно, SIGTERM, которого я ожидаю, должен исходить либо от launchd, либо с терминала (у меня ни то, ни другое не работает). wait и цикл, о котором вы говорите, спорный и просто часть того, что я надеялся, будет ясным примером того, что pid отправителя всегда равен 0 в mojave; «исправление» их ничего не меняет.
Хорошо, поэтому в вашем втором примере wait, кажется, проясняет ситуацию в отношении сигналов, отправленных от детей. К сожалению, мой код IRL требует этого от терминального процесса, и я все еще не могу заставить это работать. Есть какие-нибудь идеи?
Оказывается, виновата отладка через Xcode LLDB. Если я собираю и запускаю программу в обычном режиме, она работает нормально. Если узнаю, зачем обновлю этот ответ.
У меня уже есть «PASS», установленный для SIGTERM в lldb, как указано в вопросе, поэтому кажется, что каким-то образом есть ошибка в версии lldb, поставляемой с Xcode 10.0, и он «передает» сигнал, создавая новую структуру и установка номера сигнала, а не структуры, которая обычно была бы получена. Как я уже говорил ранее, раньше это работало нормально в любой версии lldb, поставляемой с macos 10.12.
Если у кого-то есть более подробное объяснение, отправьте ответ, и я приму награду и вручу награду.
Необходимо добавить
#include <stdbool.h>для компиляции как C, кстати.