[C, UNIX] два SIGTERM "склеиваются" в один
Возможно он пока в обработчике сигнала, то не принимает ещё один такой же сигнал? Обычные сигналы, они как флаги потому что, в очередь не выстраиваются.
Возможно он пока в обработчике сигнала, то не принимает ещё один такой же сигнал? Обычные сигналы, они как флаги потому что, в очередь не выстраиваются.Вот мы тоже так решили, но это где-нибудь документировано? Нашлись весьма косвенные упоминания в man setrlimit, например. Обработчик, по идее, завершается, но возможно, что просто нельзя последовательно отправить два одинаковых сигнала, на уровне ОС.
Алсо, спросил на SO (если кому интересно).
Возможно он пока в обработчике сигналане
обработчик сигнала делает "do_quit = 1;" и выходит.
pthread_kill со вторым сигтермом вызывается после завершения обработчика первого.
вопрос в том, насколько стандартизовано поведение "не посылать второй из двух одинаковых последовательных сигналов".
имеются ли гарантии на время через которое следующий сигнал уже точно дойдет?
На время обработки сигнала он возможно блокирует его получение. Хотя по идее после разблокирования должно бы доставиться. Да и не знаю как там в тредах сигналы блокируются — отдельно или нет.
Сейчас в мане нашёл только то, что в обработчике нельзя делать raise.
Сейчас в мане нашёл только то, что в обработчике нельзя делать raise.
Вот в этом и вопрос, как они там блокируются. Косвенное свидительство в мане следующее:
However, the limit is only enforced for sigqueue(2); it is always possible to use kill(2) to queue one instance of any of the signals that are not already queued to the process.Т.е., очередь сигналов вообще существует для процесса и сигнал игнорируется, если он уже есть в очереди. А обрабатывается сигнал произвольным из незамаскированных на данный сигнал тредов.
обработчик сигнала делает "do_quit = 1;" и выходит.Ну я вообще не помню ничего такого. Т.е. если 2 сразу придёт до обработчика, то 2-й стопудово склется (в single-thread по крайней мере). Но тут-то у него сигнал уже обработан. Поэтому нет никаких причин для 2-го не быть полученным. Тупо взять какой-нибудь демон, который по HUP конфиг перечитывает. Они же на каждый HUP перечитывают, а не только на первый.
pthread_kill со вторым сигтермом вызывается после завершения обработчика первого.
вопрос в том, насколько стандартизовано поведение "не посылать второй из двух одинаковых последовательных сигналов".
имеются ли гарантии на время через которое следующий сигнал уже точно дойдет?
Т.е., очередь сигналов вообще существует для процесса, а сигнал обрабатывается произвольным из незамаскированных на данный сигнал тредов.Ну да, очередь-то есть, чтобы в порядке обрабатывался, но необработанные сигналы не дублируются. В Linux-е для этих целей даже специальные сигналы вроде есть, которые именно накапливаются. SIGRT* кажется.
но второй тред не получает SIGTERM, и соответственно не завершается и висит (как только на сокет приходят данные, то тред завершаетсяНа StackOverflow написано, что когда данные приходят, то TERM всё же обрабатывается. Так как всё же?
Он обрабатывается один раз и тред завершается, так сказать, естественным образом. Т.е., когда тред висит в read, он может быть прерван двумя способами: сигналом (чего не происходит либо ответом read. Во втором случае этого достаточно, т.к. в другом треде флаг завершения выставлен и тред завершается сам, вопрос в том, как обработать первый случай. Точнее сказать, как уже придумали, но это выглядит хаком и хочется понять, чем обосновано такое поведение системы.
набросал макет кода, может это немного прояснит ситуацию
void signal_handler(int sig)
{
...
case SIGTERM:
do_quit = 1;
...
}
void th2_proc
{
while (!do_quit)
{
ФУНКЦИЯ_КОТОРАЯ_ЗАСЫПАЕТ_В_ПОЛОЖЕНИИ_READ;
}
}
void ~desctructor
{
pthread_kill(th2_proc, SIGTERM);
}
void th1_proc
{
while (!do_quit)
{
sleep(1);
}
// при завершении th1_proc вызывается деструктор класса
}
Надо сказать, что вот в этом работающем тестовом примере второй сигнал у меня доходит:
Может у тебя, Дима, просто линукс какой-то неправильный? =)
#include <stdio.h>
#include <signal.h>
#include <pthread.h>
volatile int do_quit = 0;
void sig_handler(int sig)
{
do_quit = 1;
fprintf(stderr, "sig %d received in %u\n", sig, (unsigned int) pthread_self;
}
void *func(void *arg)
{
fprintf(stderr, "th2: %u\n", (unsigned int) pthread_self;
sleep(1000);
}
int main
{
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
pthread_t th;
pthread_create(&th, NULL, func, NULL);
fprintf(stderr, "th1: %u\n", (unsigned int) pthread_self;
while (!do_quit)
{
}
pthread_kill(th, SIGTERM);
pthread_join(th, NULL);
return 0;
}
Может у тебя, Дима, просто линукс какой-то неправильный? =)
bugaga ~ $ ./a.out
th1: 3076155152
th2: 3076152176
sig 15 received in 3076155152
sig 15 received in 3076152176
ну тогда не только у меня, ибо на енисее такая же проблема была с wz 

надо будет позырить, может ты действительно там из обработчика сигнала не выходишь.
хотя странно, обработчик-то в либе вообще лежит, ты его не менял.
осилишь из моего примера сделать такой, на котором бага проявляется?
у тебя мой пример работает?
хотя странно, обработчик-то в либе вообще лежит, ты его не менял.
осилишь из моего примера сделать такой, на котором бага проявляется?
у тебя мой пример работает?
так, дима умудрился сломать мой пример.
вот в этом примере тред получает сигнал, но из read-а не выходит.
странно, я не думал, что sleep в этом смысле сильно отличается от read.
в оригинальном демоне сигнал вообще не доходил, но это из-за программерской ошибки,
так что на самом деле проблема - почему read не завершается.
щас перечитаю про рестарты по сигналу...
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
volatile int do_quit = 0;
void sig_handler(int sig)
{
do_quit = 1;
fprintf(stderr, "sig %d received in %u\n", sig, (unsigned int) pthread_self;
}
char *host;
void *func(void *arg)
{
fprintf(stderr, "th2: %u\n", (unsigned int) pthread_self;
/* sleep(1000); */
int sd, rc;
sd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(host);
addr.sin_port = htons(80);
rc = connect(sd, (struct sockaddr *) &addr, sizeof(addr;
char buf [4096];
rc = read(sd, buf, 4096);
}
int main(int argc, char **argv)
{
host = argv[1];
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
pthread_t th;
pthread_create(&th, NULL, func, NULL);
fprintf(stderr, "th1: %u\n", (unsigned int) pthread_self;
while (!do_quit)
{
}
pthread_kill(th, SIGINT);
pthread_join(th, NULL);
return 0;
}
вот в этом примере тред получает сигнал, но из read-а не выходит.
странно, я не думал, что sleep в этом смысле сильно отличается от read.
в оригинальном демоне сигнал вообще не доходил, но это из-за программерской ошибки,
так что на самом деле проблема - почему read не завершается.
щас перечитаю про рестарты по сигналу...
проблема 1 (которая моя) разрешилась, просто в обработчике сигнала, в начале, кое-кто написал signal(sig, SIGIGN а я скопипастил...
осилил маны 
надо делать вот так
этот SA_RESTART (иногда?) по дефолту поднят, и он как раз влияет на то, перезапустится ли read после сигнала.

надо делать вот так
struct sigaction sa;
sigaction(SIGINT, NULL, &sa);
sa.sa_flags &= ~SA_RESTART;
sigaction(SIGINT, &sa, NULL);
этот SA_RESTART (иногда?) по дефолту поднят, и он как раз влияет на то, перезапустится ли read после сигнала.
а я так и не добрался сегодня маны на эту тему вспомнить.
круто! спасибо, дрюха.
круто! спасибо, дрюха.
Оставить комментарий

iravik
Есть демон, в котором есть два треда: th1, th2. th2 сидит в чтении сокета.Если послать приложению SIGTERM, th1 ловит сигнал, вызывается деструктор демона, который делает pthread_kill(th2, SIGTERM но второй тред не получает SIGTERM, и соответственно не завершается и висит (как только на сокет приходят данные, то тред завершается).
Если же в декструкторе сделать сначала pthread_kill(th2, 28) (ну или почти любой другой сигнал а потом pthread_kill(th2, SIGTERM то все корректно завершается. Таким образом, возникает ощущение, что приложение игнорирует подряд идущие сигналы.
Подобное "склеивание" сигналов — это стандарт или же зависит от системы? И можно ли как-то послать треду SIGTERM, чтобы этот сигнал точно дошел?