2015-11-22 2 views
0

Я новичок в семафорах и хочу добавить многопоточность в свою программу, но я не могу обойти следующую проблему: sem_wait() должен иметь возможность получать EINTR и разблокировать, если бы я не делал 'установите флаг SA_RESTART. Я отправляю SIGUSR1 в рабочий поток, который блокируется в sem_wait(), он получает сигнал и прерывается, но он будет продолжать блокироваться, и поэтому он никогда не даст мне код возврата -1 вместе с errno = EINTR , Однако, если я делаю sem_post из основного потока, он будет разблокирован, дайте мне errno EINTR, но RC из 0. Я полностью озадачен этим поведением. Это какая-то странная реализация NetBSD, или я делаю что-то неправильно здесь? Согласно странице man, sem_wait соответствует POSIX.1 (ISO/IEC 9945-1: 1996). Простой код:sem_wait не разблокируется с EINTR

#include <stdio.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <signal.h> 
#include <pthread.h> 
#include <semaphore.h> 

typedef struct workQueue_s 
{ 
    int full; 
    int empty; 
    sem_t work; 
    int sock_c[10]; 
} workQueue_t; 

void signal_handler(int sig) 
{ 
    switch(sig) 
    { 
     case SIGUSR1: 
     printf("Signal: I am pthread %p\n", pthread_self()); 
     break; 
    } 
} 

extern int errno; 
workQueue_t queue; 
pthread_t workerbees[8]; 

void *BeeWork(void *t) 
{ 
    int RC; 
    pthread_t tid; 
    struct sigaction sa; 
    sa.sa_handler = signal_handler; 
    sigaction(SIGUSR1, &sa, NULL); 

    printf("Bee: I am pthread %p\n", pthread_self()); 
    RC = sem_wait(&queue.work); 
    printf("Bee: got RC = %d and errno = %d\n", RC, errno); 

    RC = sem_wait(&queue.work); 
    printf("Bee: got RC = %d and errno = %d\n", RC, errno); 
    pthread_exit((void *) t); 
} 

int main() 
{ 
    int RC; 
    long tid = 0; 
    pthread_attr_t attr; 
    pthread_attr_init(&attr); 
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 

    queue.full = 0; 
    queue.empty = 0; 
    sem_init(&queue.work, 0, 0); 

    printf("I am pthread %p\n", pthread_self()); 
    pthread_create(&workerbees[tid], &attr, BeeWork, (void *) tid); 
    pthread_attr_destroy(&attr); 

    sleep(2); 
    sem_post(&queue.work); 
    sleep(2); 
    pthread_kill(workerbees[tid], SIGUSR1); 
    sleep(2); 

    // Remove this and sem_wait will stay blocked 
    sem_post(&queue.work); 
    sleep(2); 
    return(0); 
} 

Я знаю, что Printf не вслух обработчика сигнала, но просто ради этого, если я удалить его я получаю те же результаты.

Таковы результаты без sem_post:

I am pthread 0x7f7fffc00000 
Bee: I am pthread 0x7f7ff6c00000 
Bee: got RC = 0 and errno = 0 
Signal: I am pthread 0x7f7ff6c00000 

И с sem_post:

I am pthread 0x7f7fffc00000 
Bee: I am pthread 0x7f7ff6c00000 
Bee: got RC = 0 and errno = 0 
Signal: I am pthread 0x7f7ff6c00000 
Bee: got RC = 0 and errno = 4 

Я знаю, что на самом деле не нужно, чтобы разблокировать и просто может сделать выход из основных, но Я хочу, чтобы все это работало. Причина, по которой я использую sem_wait, заключается в том, что я хочу, чтобы рабочие потоки были живы и разбудили одну из них, ожидающую самого длинного из основного потока с помощью sem_post, как только появится новое клиентское соединение из Postfix. Я не хочу делать pthread_create все время, так как я буду получать вызовы несколько раз в секунду, и я не хочу терять скорость и не делаю Postfix невосприимчивым к новым клиентам smtpd. Это политик для Postfix, и сервер довольно занят.

Я что-то упустил? NetBSD просто испортил это?

+0

произойдет ли это, если вы правильно использовать sigaction? Прямо сейчас вы передаете много мусора в sigaction(), и, возможно, вы установили флаг SA_RESTART. Вам абсолютно необходимо инициализировать 's saction s' s 's', либо 'struct sigaction sa = {0};' или 'memset (& sa, 0, sizeof sa);' – nos

+0

Спасибо за ваш отзыв, я просто изменил его, но, к сожалению, Я получаю те же результаты ... – Saskia

+0

Это работает как минимум на NetBSD 7.0 amd64, и я получаю 'Bee: получил RC = -1 и errno = 4' (учтите, что вы должны удалить' extern int errno', объявив errno, как это не то, что нужно делать в многопоточной программе) – nos

ответ

0

Мое сообщение о поведении в Linux, но я думаю, что у вас может быть подобное поведение или, по крайней мере, я думаю, это может быть полезно. Если нет, дайте мне знать, я удалю этот бесполезный «шум».

Я попытался воспроизвести вашу установку, и я был очень удивлен, увидев, что вы описываете. Глядя глубже помог мне понять, что на самом деле было что-то более тонкое; если вы посмотрите на Трассирование, вы увидите, как Somthing:

[pid 6984] futex(0x6020e8, FUTEX_WAIT_PRIVATE, 0, NULL <unfinished ...> 
[pid 6983] rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0 
[pid 6983] rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0 
[pid 6983] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 
[pid 6983] nanosleep({2, 0}, 0x7fffe5794a70) = 0 
[pid 6983] tgkill(6983, 6984, SIGUSR1 <unfinished ...> 
[pid 6984] <... futex resumed>)  = ? ERESTARTSYS (To be restarted if SA_RESTART is set) 
[pid 6983] <... tgkill resumed>)  = 0 
[pid 6984] --- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_TKILL, si_pid=6983, si_uid=500} --- 
[pid 6983] rt_sigprocmask(SIG_BLOCK, [CHLD], <unfinished ...> 
[pid 6984] rt_sigreturn(<unfinished ...> 
[pid 6983] <... rt_sigprocmask resumed> [], 8) = 0 
[pid 6984] <... rt_sigreturn resumed>) = -1 EINTR (Interrupted system call) 

видеть линии с ERESTARTSYS и EINTR: кинозал вызов прерывается на самом деле rt_sigreturn resumed, не futex (системный вызов, лежащий в основе sem_wait) как вы ожидали. я должен сказать, я был весьма озадачен, но чтение человек дал некоторые интересные подсказки (человек 7 сигнал):

If a blocked call to one of the following interfaces is interrupted by 
    a signal handler, then the call will be automatically restarted after 
    the signal handler returns if the SA_RESTART flag was used; otherwise 
    the call will fail with the error EINTR: 
[...] 

     * futex(2) FUTEX_WAIT (since Linux 2.6.22; beforehand, always 
     failed with EINTR). 

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

То есть, я полностью удалил sem_post() из программы и просто послал сигнал «разрыв» в sem_wait() ANS, глядя на Трассирование я видел (фильтрация на пчелином нити):

[pid 8309] futex(0x7fffc0470990, FUTEX_WAIT_PRIVATE, 0, NULL <unfinished ...> 
[pid 8309] <... futex resumed>)  = ? ERESTARTSYS (To be restarted if SA_RESTART is set) 
[pid 8309] --- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_TKILL, si_pid=8308, si_uid=500} --- 
[pid 8309] rt_sigreturn()    = -1 EINTR (Interrupted system call) 
[pid 8309] madvise(0x7fd5f6019000, 8368128, MADV_DONTNEED) = 0 
[pid 8309] _exit(0) 

Я должен сказать, что я не мастер детали, но ядро, похоже, чтобы выяснить, где я пытаюсь встать и сделать все, что есть правильное поведение:

Bee: got RC = -1 and errno = Interrupted system call 
+0

Спасибо за вашу помощь, у меня только есть ktrace, когда я удаляю последний sem_post и устанавливаю последний сон еще немного, я получаю это: 10631 1 a.out RET __nanosleep50 0 10631 1 a.out ВЫЗОВ CALL (0) 10631 2 a.out RET ___lwp_park50 -1 errno 4 Прерванный системный вызов – Saskia

0

Спасибо за ваш ответ OznOg, если я удалите последний sem_post и сделайте последний сон немного дольше, я получаю это с помощью ktrace:

PSIG SIGUSR1 caught handler=0x40035c mask=(): code=SI_LWP sent by pid=10631, uid=0) 
CALL write(1,0x7f7ff7e04000,0x24) 
GIO fd 1 wrote 36 bytes "Signal: I am pthread 0x7f7ff7800000\n" 
RET write 36/0x24 
CALL setcontext(0x7f7ff7bff970) 
RET setcontext JUSTRETURN 
CALL ___lwp_park50(0,0,0x7f7ff7e01100,0x7f7ff7e01100) 
RET __nanosleep50 0 
CALL exit(0) 
RET ___lwp_park50 -1 errno 4 Interrupted system call 

Похоже sem_wait будет возвращать только либо выход или sem_post ....

Смежные вопросы