[C, UNIX] два SIGTERM "склеиваются" в один

iravik

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

tokuchu

Возможно он пока в обработчике сигнала, то не принимает ещё один такой же сигнал? Обычные сигналы, они как флаги потому что, в очередь не выстраиваются.

okis

Возможно он пока в обработчике сигнала, то не принимает ещё один такой же сигнал? Обычные сигналы, они как флаги потому что, в очередь не выстраиваются.
Вот мы тоже так решили, но это где-нибудь документировано? Нашлись весьма косвенные упоминания в man setrlimit, например. Обработчик, по идее, завершается, но возможно, что просто нельзя последовательно отправить два одинаковых сигнала, на уровне ОС.
Алсо, спросил на SO (если кому интересно).

slonishka

Возможно он пока в обработчике сигнала
не
обработчик сигнала делает "do_quit = 1;" и выходит.
pthread_kill со вторым сигтермом вызывается после завершения обработчика первого.
вопрос в том, насколько стандартизовано поведение "не посылать второй из двух одинаковых последовательных сигналов".
имеются ли гарантии на время через которое следующий сигнал уже точно дойдет?

slonishka

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

tokuchu

На время обработки сигнала он возможно блокирует его получение. Хотя по идее после разблокирования должно бы доставиться. Да и не знаю как там в тредах сигналы блокируются — отдельно или нет.
Сейчас в мане нашёл только то, что в обработчике нельзя делать raise.

okis

Вот в этом и вопрос, как они там блокируются. Косвенное свидительство в мане следующее:
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.
Т.е., очередь сигналов вообще существует для процесса и сигнал игнорируется, если он уже есть в очереди. А обрабатывается сигнал произвольным из незамаскированных на данный сигнал тредов.

tokuchu

обработчик сигнала делает "do_quit = 1;" и выходит.
pthread_kill со вторым сигтермом вызывается после завершения обработчика первого.
вопрос в том, насколько стандартизовано поведение "не посылать второй из двух одинаковых последовательных сигналов".
имеются ли гарантии на время через которое следующий сигнал уже точно дойдет?
Ну я вообще не помню ничего такого. Т.е. если 2 сразу придёт до обработчика, то 2-й стопудово склется (в single-thread по крайней мере). Но тут-то у него сигнал уже обработан. Поэтому нет никаких причин для 2-го не быть полученным. Тупо взять какой-нибудь демон, который по HUP конфиг перечитывает. Они же на каждый HUP перечитывают, а не только на первый.

tokuchu

Т.е., очередь сигналов вообще существует для процесса, а сигнал обрабатывается произвольным из незамаскированных на данный сигнал тредов.
Ну да, очередь-то есть, чтобы в порядке обрабатывался, но необработанные сигналы не дублируются. В Linux-е для этих целей даже специальные сигналы вроде есть, которые именно накапливаются. SIGRT* кажется.

tokuchu

но второй тред не получает SIGTERM, и соответственно не завершается и висит (как только на сокет приходят данные, то тред завершается
На StackOverflow написано, что когда данные приходят, то TERM всё же обрабатывается. Так как всё же?

okis

Он обрабатывается один раз и тред завершается, так сказать, естественным образом. Т.е., когда тред висит в read, он может быть прерван двумя способами: сигналом (чего не происходит либо ответом read. Во втором случае этого достаточно, т.к. в другом треде флаг завершения выставлен и тред завершается сам, вопрос в том, как обработать первый случай. Точнее сказать, как уже придумали, но это выглядит хаком и хочется понять, чем обосновано такое поведение системы.

iravik

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

slonishka

Надо сказать, что вот в этом работающем тестовом примере второй сигнал у меня доходит:
#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;
}

Может у тебя, Дима, просто линукс какой-то неправильный? =)

slonishka

bugaga ~ $ ./a.out 
th1: 3076155152
th2: 3076152176
sig 15 received in 3076155152
sig 15 received in 3076152176

iravik

ну тогда не только у меня, ибо на енисее такая же проблема была с wz :)

slonishka

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

slonishka

так, дима умудрился сломать мой пример.
#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 не завершается.
щас перечитаю про рестарты по сигналу...

iravik

проблема 1 (которая моя) разрешилась, просто в обработчике сигнала, в начале, кое-кто написал signal(sig, SIGIGN а я скопипастил...

rosali

осилил маны :)
надо делать вот так
    
struct sigaction sa;
sigaction(SIGINT, NULL, &sa);
sa.sa_flags &= ~SA_RESTART;
sigaction(SIGINT, &sa, NULL);

этот SA_RESTART (иногда?) по дефолту поднят, и он как раз влияет на то, перезапустится ли read после сигнала.

slonishka

а я так и не добрался сегодня маны на эту тему вспомнить.
круто! спасибо, дрюха.
Оставить комментарий
Имя или ник:
Комментарий: