0

私はセマフォに不慣れで、自分のプログラムにマルチスレッドを追加したいと考えていますが、SA_RESTART フラグを設定していない限り、sem_wait() は EINTR を受信して​​ブロックを解除できる必要があるという問題を回避できません。sem_wait() でブロックしているワーカー スレッドに SIGUSR1 を送信しています。シグナルを受信して​​中断されますが、その後ブロックし続けるため、errno = EINTR と一緒に -1 のリターン コードが返されることはありません。 . ただし、メイン スレッドから sem_post を実行すると、ブロックが解除され、EINTR の errno が返されますが、RC は 0 になります。この動作には完全に困惑しています。それは奇妙な NetBSD 実装ですか、それともここで何か間違ったことをしていますか? man ページによると、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

ブロックを解除する必要がなく、単にメインから終了できることはわかっていますが、とにかく動作することを確認したいと思います。私が sem_wait を使用している理由は、Postfix からの新しいクライアント接続があるとすぐに、ワーカー スレッドを有効に保ち、sem_post を使用してメイン スレッドから最も長く待機しているスレッドを起こしたいからです。pthread_create を常に実行したくはありません。1 秒間に複数回呼び出しを受信し、速度を落として Postfix を新しい smtpd クライアントに応答させたくないからです。これは Postfix のポリシーデーモンであり、サーバーは非常にビジーです。

ここで何か不足していますか?NetBSD はこれを台無しにしてしまったのでしょうか?

4

2 に答える 2

0

私の投稿は Linux での動作に関するものですが、同様の動作をしている可能性があるか、少なくとも参考になると思います。そうでない場合は、お知らせください。この無用な「ノイズ」を削除します。

私はあなたのセットアップを再現しようとしましたが、あなたが説明したことが起こっているのを見て非常に驚きました. もっと深く見ると、実際にはもっと微妙なものがあることがわかりました。strace を見ると、次のように表示されます。

[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 の基礎となるシステム コール) は期待どおりではありません。私はかなり困惑したと言わざるを得ませんが、男性を読むといくつかの興味深い手がかりが得られました (man 7 signal):

   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() を完全に削除し、見た strace を見て 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
于 2015-11-22T20:38:46.673 に答える
0

回答ありがとうございます。最後の 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 は、exit または sem_post によってのみ返されるようです....

于 2015-11-23T10:37:04.143 に答える