簡體   English   中英

sem_wait不使用EINTR解除阻止

[英]sem_wait not unblocking with EINTR

我是信號燈的新手,想在程序中添加多線程,但是我無法解決以下問題:只要我沒有設置SA_RESTART標志,sem_wait()就應該能夠接收EINTR並解除阻塞。 我正在向sem_wait()中阻塞的工作線程發送SIGUSR1,它確實接收到信號並被中斷,但是它將繼續阻塞,因此它將永遠不會給我-1返回代碼以及errno = EINTR 。 但是,如果我從主線程執行sem_post,它將取消阻塞,給我一個EINTR的錯誤號,但RC為0。我對此行為完全感到困惑。 是某種怪異的NetBSD實現,還是我在這里做錯了什么? 根據手冊頁,sem_wait符合POSIX.1(ISO / IEC 9945-1:1996)。 一個簡單的代碼:

#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

我知道我真的不需要取消阻止,只需從main退出即可,但是我仍然希望它能正常工作。 我使用sem_wait的原因是因為一旦Postfix建立了新的客戶端連接,我想使工作線程保持活動狀態,並使用sem_post喚醒等待主線程最長的工作線程。 我不想一直做pthread_create,因為我每秒會收到多次呼叫,而且我不想失去速度,並使Postfix對新的smtpd客戶端無響應。 它是Postfix的policydaemon,服務器非常忙。

我在這里想念什么嗎? NetBSD只是搞砸了嗎?

我的帖子是關於Linux上的行為的,但是我認為您可能會有類似的行為,或者至少我認為可能會有所幫助。 如果沒有,請告訴我,我將消除這種無用的“噪音”。

我試圖重現您的設置,而看到您所描述的情況我感到非常驚訝。 深入研究有助於我弄清楚實際上還有一些枯燥的東西。 如果您想追蹤一下,就會看到類似以下內容:

[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)

參見帶有ERESTARTSYSEINTR :被中斷的sistem調用實際上是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 doc?),並且您可以觀察到系統調用自動重新啟動,而您沒有機會看到它。

就是說,我從程序中完全刪除了sem_post(),只是發送了信號來“破壞” sem_wait()並查看了我看到的strace(在bee線程上過濾):

[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

感謝您的回答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返回。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM