macOS `sigaction ()` обработчик с `SA_SIGINFO` не включает` si_pid` - PullRequest
0 голосов
/ 20 ноября 2018

Я пытаюсь написать обработчик сигнала, который должен знать 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);
    }
}

Ответы [ 2 ]

0 голосов
/ 04 декабря 2018

Оказывается, что отладка с помощью Xcode LLDB является виновником.Если я собираю и запускаю программу нормально, она работает нормально.Если я выясню, почему я буду обновлять этот ответ.

У меня уже есть «PASS» для SIGTERM в lldb, как отмечено в вопросе, поэтому кажется, что в версии lldb есть ошибкас Xcode 10.0, и он «передает» сигнал, создавая новую структуру и устанавливая номер сигнала, а не структуру, которая обычно получалась бы.Как я уже говорил, раньше это работало нормально в любой версии lldb, поставляемой с macos. 10.12

Если у кого-то есть объяснение получше, пожалуйста, напишите ответ, и я приму и назначу награду.

0 голосов
/ 03 декабря 2018

В настоящее время я использую macOS Mojave 10.14.1.

Как я могу получить pid процесса, отправляющего сигнал на macOS 10.14?Я не припоминаю, чтобы эта проблема возникла 10.12, и я использовал ее раньше.

Следующий код просто отвечает вашему желанию.Если вы отправите 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;
}

Для вашего кода

Правило большого пальца: Не забудьте выполнить процедуры захоронения, даже если вы уверены, что дочерний процесс завершается предшествующим родительским процессом.Вызвав wait(...), вы сообщаете операционной системе, что я сделал все для своего ребенка, чтобы теперь вы могли очистить выделенные поля и т. Д.

Я бы предпочел инициализировать утилиты сигналов до разветвления, что еслиродительский процесс не имеет возможности зарегистрировать действие сигнала?Более того, я не понимаю, почему вы рассматриваете дела 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

    }
}
...