C sem\u等待未使用EINTR解除阻止

C sem\u等待未使用EINTR解除阻止,c,linux,semaphore,netbsd,C,Linux,Semaphore,Netbsd,我是信号量新手,希望在程序中添加多线程,但我无法回避以下问题:sem_wait()应该能够接收EINTR并取消阻塞,只要我没有设置SA_重启标志。我正在向正在sem_wait()中阻塞的工作线程发送一个SIGUSR1,它确实接收到信号并被中断,但随后它将继续阻塞,因此它将永远不会给我一个-1返回代码以及errno=EINTR。但是,如果我从主线程执行一个sem_post,它将取消阻塞,给我一个errno of EINTR,但RC为0。我对这种行为完全感到困惑。这是某种奇怪的NetBSD实现还是我

我是信号量新手,希望在程序中添加多线程,但我无法回避以下问题:sem_wait()应该能够接收EINTR并取消阻塞,只要我没有设置SA_重启标志。我正在向正在sem_wait()中阻塞的工作线程发送一个SIGUSR1,它确实接收到信号并被中断,但随后它将继续阻塞,因此它将永远不会给我一个-1返回代码以及errno=EINTR。但是,如果我从主线程执行一个sem_post,它将取消阻塞,给我一个errno of 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 );
}
在sem_的帖子中:

I am pthread 0x7f7fffc00000
Bee: I am pthread 0x7f7ff6c00000
Bee: got RC = 0 and errno = 0
Signal: I am pthread 0x7f7ff6c00000
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上的行为的,但我认为你可能有类似的行为,或者至少我认为可能会有所帮助。如果没有,请告诉我,我会消除这些无用的“噪音”

我试着复制你的设置,看到你描述的情况,我非常惊讶。深入观察帮助我发现其实还有一些更微妙的东西;如果你看一看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)
因此,我猜您有一个具有类似行为的内核(请参阅netBSD文档),您可以观察到系统调用自动重新启动,而您没有任何机会看到它

也就是说,我从你的程序中完全删除了sem_post(),只发送了一个信号来“打断”sem_wait()ans,它看着我看到的strace(在蜂线上过滤):


谢谢你的回答,OznOg,如果我删除最后一篇sem_帖子,让最后一次睡眠时间长一点,我会通过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返回…

如果正确使用sigaction,是否会发生这种情况?现在,您正在向sigaction()传递大量垃圾,并且可能设置了sau RESTART标志。您绝对需要初始化
struct sigaction sa
,或者do
struct sigaction sa={0}
memset(&sa,0,sizeof sa)谢谢你的提示,我刚刚更改了它,但不幸的是我得到了相同的结果…这至少在NetBSD 7.0 amd64上可以正常工作,而且我得到了
Bee:get RC=-1和errno=4
(请注意,你应该删除
extern int errno
,因为在多线程程序中这样声明errno是错误的)谢谢,我得到了6.1版本,所以我想我需要更新,这对生产服务器来说是一件痛苦的事情。我猜当你做一个“男人扫描”等待时,EINTR被列出(它没有在6.1中列出)-(为了记录在案,nos帮了我的忙,我升级到了7.0,现在一切都正常了。谢谢你的帮助,我只有ktrace,当我删除最后一个sem_帖子并将最后一个睡眠时间延长一点时,我得到了这个:10631 1 a.out RET_unanosleep50 10631 1 a.out CALL exit(0)10631 2 a.out RET\uuuuuu lwp\u park50-1错误4中断系统调用
[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
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