2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему
 
 Язык Си. Linux. Сигналы. alarm(); signal(); sigaction();
Сообщение06.09.2024, 02:42 


02/10/12
308
Я написал программу для отладки самодельного сервера. Сервер это мастер-процес, который создаёт и поддерживает пул процессов, т.е. группу процессов сессии, они для обработки запросов. При выключении сервера мастер-процес посылает сигналы всем процессам группы и сам завершается через exit(); Приёмным родителем группы становится системный процесс init, он и удадяет процессы зомби.

Программа отладки это мастер-процесс. Она включается из консоли. Предусмотрено выключение сервера по таймеру, и принудиельное выключение из консоли. По таймеру - на случай забывания выключить и кабы чего не вышло, из консоли - чтобы внести изменения и перекомпилировать сервер.

Моя программа отладки на моём компьютере работает так, как и хотелось бы, что уже неплохо. Но я не понимаю почему она так работает.

Привожу минимальный пример, в нём есть то, чего я не понимаю.


код: [ скачать ] [ спрятать ]
Используется синтаксис C


//   /home/test/SI/SI/serv_micron_1/dxdy/test_4.c
//   cd /home/test/SI/SI/serv_micron_1/dxdy/
//   gcc test_4.c -o test_4.cgi -Wall -Werror -O3
//   ./test_4.cgi


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <signal.h>



#define  M_TIME  10 // врмя работы сервра (секунды)


int volatile fl_alarm=0;  // глобальный флаг обработчика сигнала
                          // (ожидается SIGALRM)

//--------- timer ------

void timer(int n)
{
    fl_alarm=1;
    printf("T3: ====(n=%d)====\n", n); //========================== METKA-3====
    return;
}

//---------------- my_signal ----------------------

int my_signal(int signo, void (*fun)(int), struct sigaction  *oact)
{
    int k;
    struct sigaction  act;

    memset(&act, 0, sizeof(act));
    act.sa_handler = fun;

    k=sigaction(signo, &act, oact);
    return(k);
}

//----------- main --------------------------

int main()
{
    int k=0, err0=0, fd=0; // fd -стандартный ввод
    char buf[20];

/*
 Это выжимка из родительского мастер-процесса. При правильной
 работе как хотелось бы, должен быть возврат из k=read(), хоть
 с ошибкой, хоть без, но чтоб был возврат, и чтоб была вся
 распечатка ниже. Это пример, а в рабочем мастер-процессе
 после возврата из k=read() есть ещё посылка сигналов всем
 процессам группы на завершение.
*/


    my_signal(SIGALRM, timer, NULL);  //========================= METKA-1====
    //signal(SIGALRM, timer);  //================================== METKA-2====

    alarm(M_TIME);

    printf("==========================\n");
    printf("alarm(%d)==>press Enter for stop\n", M_TIME);
    printf("--------------------------\n");

    k=read(fd, buf, 1); // подвисаю на вводе с клавиатуры, жду либо ввод,
                        // либо сигнал таймера.
    printf("T4: fl_alarm=%d;\n", fl_alarm);
    if(k<=0){
        err0 = errno;
        printf("T1: k=read()=%d; errno=%s; %s\n",
               k, err0==EINTR ? "EINTR":"unknow", strerror(err0));
    }
    else{
        printf("T2: k=read=\"0x%02X\"\n", (*buf & 255));
    }
    printf("==========================\n");
    exit(0);
}
 



В примере есть метки. METKA-1 и METKA-2 задают 3 возможных режима работы. Эти метки включают или выключают кусочки кода при них путём закомментирования. Есть ещё METKA-3, но она ни на что не влияет и здесь служит для визуализации (знаю, что не положено, что не Safe, но от безысходности и только в тестовом примере). Есть три режима, привожу распечатку примера для каждого из них.

1). Режим-1 Обе метки METKA-1 и METKA-2 выключены.
Код:
По Enter:
==========================
alarm(10)==>press Enter for stop
--------------------------

T4: fl_alarm=0;
T2: k=read="0x0A"
==========================

Код:
По таймеру:
==========================
alarm(10)==>press Enter for stop
--------------------------
Сигнал таймера


Как я понимаю, SIGALRM убивает процесс и всё, возврата из k=read() нет. Слова "Сигнал таймера" пишет система в консоли.

2). Режим-2 METKA-1 выключена, METKA-2 включена, т. е. работает системный signal():
Код:
По Enter:
==========================
alarm(10)==>press Enter for stop
--------------------------

T4: fl_alarm=0;
T2: k=read="0x0A"
==========================


Код:
По таймеру:
==========================
alarm(10)==>press Enter for stop
--------------------------
T3: ====(n=14)====

И процесс продолжает висеть на read(), выключить его можно нажатием Enter, тогда добавляется распечатка:
Код:
По Enter после таймера:
==========================
alarm(10)==>press Enter for stop
--------------------------
T3: ====(n=14)====

T4: fl_alarm=1;     // это добавляется
T2: k=read="0x0A"   // и это добавляется, эти две строки по Enter
==========================

3). Режим-3 METKA-1 включена, METKA-2 выключена, т. е. работает my_signal():
Код:
По Enter:
==========================
alarm(10)==>press Enter for stop
--------------------------

T4: fl_alarm=0;
T2: k=read="0x0A"
==========================

Код:
По таймеру:
==========================
alarm(10)==>press Enter for stop
--------------------------
T3: ====(n=14)====
T4: fl_alarm=1;
T1: k=read()=-1; errno=EINTR; Interrupted system call
==========================

Третий режим работает так, как и хотелось бы. Но почему? Ведь что получатся, вроде бы мой самодельный my_signal() делает то же самое, что и системный - перехватывает сигнал и запускает мой обработчик, о чём свидетельствует визуализация:
Код:
T3: ====(n=14)====
T4: fl_alarm=1;

Но видимо сигнал перехватывается не напрочь, а при возврате из обработчика ещё прерывает read() с ошибкой EINTR, но не убивает процесс. Так работает на моём компьютере, а опробовать на других компьютерах у мня нет возможности.

Поскольку read() может давать возврат с ошибкой EINTR, то должны бы быть настройки или условия, при которых это возможно без убивания процесса. Но я пока этого не знаю, 3-й режим у меня как-то сам собой получился. А хотелось бы знать.

Короче, системный signal() перхватывает сигнал напрочь, как если бы игнорировать сигнал, а мой my_signal() как-то умудряется и вызов read() прервать, но процесс не убивать.

Источики, которыми пользуюсь:
sigaction
https://www.opennet.ru/man.shtml?topic= ... &russian=0

signal
https://www.opennet.ru/man.shtml?topic= ... &russian=0

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

 Профиль  
                  
 
 Re: Язык Си. Linux. Сигналы. alarm(); signal(); sigaction();
Сообщение06.09.2024, 08:09 
Заслуженный участник


04/03/09
911
Вам будет интересно почитать про флаг SA_RESTART.

 Профиль  
                  
 
 Re: Язык Си. Linux. Сигналы. alarm(); signal(); sigaction();
Сообщение06.09.2024, 10:20 


02/10/12
308
12d3 в сообщении #1653441 писал(а):
Вам будет интересно почитать про флаг SA_RESTART.

Прочитал по ссылке
https://www.gnu.org/software/libc/manual/html_node/Flags-for-Sigaction.html
Цитата:
Macro: int SA_RESTART

This flag controls what happens when a signal is delivered during certain primitives (such as open, read or write), and the signal handler returns normally. There are two alternatives: the library function can resume, or it can return failure with error code EINTR.

The choice is controlled by the SA_RESTART flag for the particular kind of signal that was delivered. If the flag is set, returning from a handler resumes the library function. If the flag is clear, returning from a handler makes the function fail. See Primitives Interrupted by Signals.

------------------------------------------

Яндекс перевод.

Этот флаг определяет, что происходит, когда сигнал подается во время определенных примитивов (таких как открытие, чтение или запись), и обработчик сигнала возвращается в обычном режиме. Есть две альтернативы: библиотечная функция может возобновить работу или вернуть сбой с кодом ошибки EINTR.

Выбор определяется флагом SA_RESTART для конкретного типа сигнала, который был доставлен. Если флаг установлен, то при возврате из обработчика библиотечная функция возобновляется. Если флаг снят, то при возврате из обработчика функция завершается ошибкой. Смотрите раздел Примитивы, прерываемые сигналами.

И прочитал ещё по ссылке (рус.)
https://www.opennet.ru/cgi-bin/opennet/man.cgi?topic=sigaction&category=2
Цитата:
SA_RESTART
Поведение должно соответствовать семантике сигналов BSD и позволять некоторым системным вызовам работать, в то время как идет обработка сигналов.

Попробовал с флагом SA_RESTART, для этого изменил функцию my_signal()
код: [ скачать ] [ спрятать ]
Используется синтаксис C

//Структура sigaction имеет поле sa_flags, SA_RESTART в этом поле предусмотрен

struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
}

//---------------- my_signal ----------------------

int my_signal(int signo, void (*fun)(int), struct sigaction  *oact)
{
    int k;
    struct sigaction  act;

    memset(&act, 0, sizeof(act));
    act.sa_handler = fun;

    act.sa_flags = SA_RESTART; // это ввёл для проверки реакции.
    //act.sa_flags = 0; // с этим работает как раньше, это чтоб убедиться.

    k=sigaction(signo, &act, oact);
    return(k);
}
 

С установленным SA_RESTART рабтает как с системным signal(), т. е. read() вообще не чувствует сигнала, нет возврата по таймеру с ошибкой EINTR.

Впечатление такое, что использование функции sigaction() само по себе, без флагов, меняет реакцию на сигнал, а именно - прерывает read() с ошибкой EINTR, но не убивает процесс. А если сигнал вообще без обработчиков, то SIGALRM убивает процесс, этот сигнал терминальный (убийственный).

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 3 ] 

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group