LINUX.ORG.RU

Посылка сигнала процессу

 , ,


0

1

Доброго времени суток!

Пишу класс для демона, в функционал которого входит обработка различных сигналов, в частности SIGUSR1. Когда посылаю kill 10 PID, то демон штатно завершается, вместо того, чтобы этот сигнал обработать.

Вот кусок кода, ответственный за обработку этого сигнала:

for (;;)
        {
            // ждем указанных сообщений
            sigwait(&sigset, &signo);
            WriteLog("%s [MONITOR] Received signal: %d %d\n", getTime(), signo, SIGUSR1);//для проверки
            // если то сообщение обновления конфига
            if (signo == SIGUSR1)
            {
                // обновим конфиг
                status = ReloadConfig();
                if (status == 0)
                {
                    WriteLog("%s [DAEMON] Reload config failed\n", getTime());
                }
                else
                {
                    WriteLog("%s [DAEMON] Reload config OK\n", getTime());
                }
            }
            else // если какой-либо другой сигнал, то выйдем из цикла
            {
                break;
            }
        }

В логе при этом следующий вывод: 17:58:42 [MONITOR] Received signal: 15 10

Как так получается, что я посылаю 10 сигнал, а демон принимает 15?

★★

man kill не очень понятен в этом плане, но сигнал надо указывать не просто по номеру иначе он как номер процессе воспримется. Надо

kill -10 PID
а лучше
kill -USR1 PID

xaizek ★★★★★
()

You are doing it wrong.

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

int die_flag = 0;

void
catch(int sig)
{
        switch (sig) {
        case SIGUSR1:
                puts("Got SIGUSR1"); // only for demonstration purposes, don't do expensive i/o in signal handler
                break;
        case SIGINT:
                puts("Got SIGINT"); // see above
                die = 1;
                break;
        default:
                puts("Unknown Signal"); // see above
                break;
        }
}

int
main()
{
        struct sigaction sa;

        sigemptyset(&sa.sa_mask);

        sa.sa_flags = 0;
        sa.sa_handler = catch;

        sigaction(SIGINT, &sa, NULL);
        sigaction(SIGUSR1, &sa, NULL);

        while (!die_flag) {
                puts("sleep");
                sleep(1);
        }

        return 0;
}
beastie ★★★★★
()
Ответ на: комментарий от aido

Желательно нисколько. Устанвить фаг и всё.

Вместо sleep(1) у меня там лучше использовать sigsuspend(&sa.sa_mask);

PS: и вообще, вот тебе пример маленькой тулзы, в которой всё это есть.

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от aido

Там не в expensive дело, а в том, что функция не является «async-signal-safe» (см man 7 signal), её вообще лучше не дёргать из обработчиков. В приведенном примере флаг не совсем того типа (тогда уже volatile sigatomic_t). А вообще sigwait() как раз хороший метод для обхода проблем, связанных с асинхронностью сигналов, ничего «неправильного» в этом нет, это просто другой способ их обработки. Главное до ожидания заблокировать те сигналы, а то будет «undefined behaviour».

xaizek ★★★★★
()
Ответ на: комментарий от beastie

А если надо часть сигналов по-другому обработать? например, при ошибке выводить backtrace, а при SIGUSR1 - обновлять конфиг?

На хабре нашел вот такое (извиняйте, но pastebin заблочили за содержание информации, причиняющей вред здоровью и развитию детей=( ):

int TDaemon::WorkProc()
{
    struct sigaction sigact;
    sigset_t         sigset;
    int             signo;
    int             status;

    // сигналы об ошибках в программе будут обрататывать более тщательно
    // указываем что хотим получать расширенную информацию об ошибках
    sigact.sa_flags = SA_SIGINFO;
    // задаем функцию обработчик сигналов
    sigact.sa_sigaction = signal_error;

    sigemptyset(&sigact.sa_mask);

    // установим наш обработчик на сигналы
    sigaction(SIGFPE, &sigact, 0); // ошибка FPU
    sigaction(SIGILL, &sigact, 0); // ошибочная инструкция
    sigaction(SIGSEGV,&sigact, 0); // ошибка доступа к памяти
    sigaction(SIGBUS, &sigact, 0); // ошибка шины, при обращении к физической памяти

    sigemptyset(&sigset);
    
    // блокируем сигналы которые будем ожидать
    // сигнал остановки процесса пользователем
    sigaddset(&sigset, SIGQUIT);
    
    // сигнал для остановки процесса пользователем с терминала
    sigaddset(&sigset, SIGINT);
    
    // сигнал запроса завершения процесса
    sigaddset(&sigset, SIGTERM);
    
    // пользовательский сигнал который мы будем использовать для обновления конфига
    sigaddset(&sigset, SIGUSR1); 
    sigprocmask(SIG_BLOCK, &sigset, NULL);

    // Установим максимальное кол-во дискрипторов которое можно открыть
    SetFdLimit(FD_LIMIT);
    
    // запишем в лог, что наш демон стартовал
    WriteLog("%s [DAEMON] Started\n", getTime());
    
        // цикл ожижания сообщений
        while(1)
        {
            // ждем указанных сообщений
            sigwait(&sigset, &signo);
            // если то сообщение обновления конфига
            if (signo == SIGUSR1)
            {
                // обновим конфиг
                status = ReloadConfig();
                if (status == 0)
                {
                    WriteLog("%s [DAEMON] Reload config failed\n", getTime());
                }
                else
                {
                    WriteLog("%s [DAEMON] Reload config OK\n", getTime());
                }
            }
            else // если какой-либо другой сигнал, то выйдем из цикла
            {
                break;
            }
        }
    WriteLog("%s [DAEMON] Stopped %d\n", getTime(), SIGUSR1);
    
    // вернем код не требующим перезапуска
    return CHILD_NEED_TERMINATE;
}

Но при посылке SIGUSR1, в лог выпадает backtrace сегфолта:

== Backtrace ==
/lib/x86_64-linux-gnu/libc.so.6(fclose+0x4) [0x7f54d4b066b4]
/lib/x86_64-linux-gnu/libc.so.6(fclose+0x4) [0x7f54d4b066b4]
./usb_daemon() [0x4015f6]
./usb_daemon() [0x4017fc]
./usb_daemon() [0x401a41]
./usb_daemon() [0x40113b]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f54d4ab8a40]
./usb_daemon() [0x4011f9]
== End Backtrace ==

aido ★★
() автор топика
Ответ на: комментарий от aido

signal_error - еще одна длинная функция обработчика ошибок.

aido ★★
() автор топика
Ответ на: комментарий от beastie

Ой. в релоадконфиге действительно сегфолт выпадает...

aido ★★
() автор топика

Если это будет работать только под Linux, Я бы заюзал signalfd и добавил потом fd в общий цикл который обрабатывает события.

joy4eg ★★★★★
()
Ответ на: комментарий от xaizek

Кстати, а почему сегфолт вылетает на fclose в этом коде (вдобавок к предыдущему листингу):

int TDaemon::ReloadConfig()
{
	return LoadConfig(ConfigFileName);
}

int TDaemon::LoadConfig(const char* Filename)
{
	FILE* f=fopen(Filename,"r");
	
	fclose(f);
	return 0;
}

aido ★★
() автор топика
Ответ на: комментарий от xaizek

только стоит добраться до манов, как сразу находится ответ =)

f и правда NULL, но по другой причине - создается у демона еще один дочерний процесс, за которым он и наблюдает и который делает всю основную работу. А у этого процесса нет доступа к памяти своего родителя=)

aido ★★
() автор топика
Ответ на: комментарий от aido

Не вижу связи. Тогда видимо ConfigFileName не инициализирован в родительском процессе и соответственно попытка открыть файл не удаётся.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

это статическая переменная класса:

char TDaemon::ConfigFileName[128];
aido ★★
() автор топика
Ответ на: комментарий от aido

Путь относительный, так что ещё от текущего каталога зависит. Для демонов таковым стандартно является /.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

И то верно. Спасибо, не знал.

aido ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.