Я написал программу для отладки самодельного сервера. Сервер это мастер-процес, который создаёт и поддерживает пул процессов, т.е. группу процессов сессии, они для обработки запросов. При выключении сервера мастер-процес посылает сигналы всем процессам группы и сам завершается через exit(); Приёмным родителем группы становится системный процесс init, он и удадяет процессы зомби.
Программа отладки это мастер-процесс. Она включается из консоли. Предусмотрено выключение сервера по таймеру, и принудиельное выключение из консоли. По таймеру - на случай забывания выключить и кабы чего не вышло, из консоли - чтобы внести изменения и перекомпилировать сервер.
Моя программа отладки на моём компьютере работает так, как и хотелось бы, что уже неплохо. Но я не понимаю почему она так работает.
Привожу минимальный пример, в нём есть то, чего я не понимаю.
// /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=0signal
https://www.opennet.ru/man.shtml?topic= ... &russian=0Небольшие английские тексты могу с яндекс-перводчиком, а большие английские тексты мне не прочитать.