简体   繁体   English

sem_wait不使用EINTR解除阻止

[英]sem_wait not unblocking with EINTR

I'm new with semaphores and want to add multithreading to my program, but I cannot get around the following problem: sem_wait() should be able to receive a EINTR and unblock, as long as I didn't set the SA_RESTART flag. 我是信号灯的新手,想在程序中添加多线程,但是我无法解决以下问题:只要我没有设置SA_RESTART标志,sem_wait()就应该能够接收EINTR并解除阻塞。 I am sending a SIGUSR1 to the worker thread that is blocking in sem_wait(), it does receive the signal and get interrupted, but it will then continue to block and so it will never give me a -1 return code together with errno = EINTR. 我正在向sem_wait()中阻塞的工作线程发送SIGUSR1,它确实接收到信号并被中断,但是它将继续阻塞,因此它将永远不会给我-1返回代码以及errno = EINTR 。 However, if I do a sem_post from the main thread, it will unblock, give me an errno of EINTR but a RC of 0. I am totally puzzled with this behavior. 但是,如果我从主线程执行sem_post,它将取消阻塞,给我一个EINTR的错误号,但RC为0。我对此行为完全感到困惑。 Is it some weird NetBSD implementation or am I doing something wrong here? 是某种怪异的NetBSD实现,还是我在这里做错了什么? According to the man page, sem_wait is conform POSIX.1 (ISO/IEC 9945-1:1996). 根据手册页,sem_wait符合POSIX.1(ISO / IEC 9945-1:1996)。 A simple code: 一个简单的代码:

#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 );
}

I know the printf is not aloud in the signal handler, but just for the heck of it, if I remove it I get the same results. 我知道在信号处理程序中printf并不大声,但是仅仅是它的麻烦,如果我删除它,我会得到相同的结果。

These are the results without sem_post: 这些是没有sem_post的结果:

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

And with the sem_post: 并与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

I know I don't really need to unblock and can simply do an exit from main, but I want to see it working anyway. 我知道我真的不需要取消阻止,只需从main退出即可,但是我仍然希望它能正常工作。 The reason I'm using sem_wait is because I want to keep the worker threads alive and wake the one up waiting the longest from the main thread with sem_post, as soon as there is a new client connection from Postfix. 我使用sem_wait的原因是因为一旦Postfix建立了新的客户端连接,我想使工作线程保持活动状态,并使用sem_post唤醒等待主线程最长的工作线程。 I don't want to do pthread_create all the time, since I will receive calls multiple times per second and I don't want to lose speed and make Postfix unresponsive to new smtpd clients. 我不想一直做pthread_create,因为我每秒会收到多次呼叫,而且我不想失去速度,并使Postfix对新的smtpd客户端无响应。 It is a policydaemon for Postfix and the server is quite busy. 它是Postfix的policydaemon,服务器非常忙。

Am I missing something here? 我在这里想念什么吗? Is NetBSD just messed up with this? NetBSD只是搞砸了吗?

My post is about behaviour on Linux, but I think you may have similar behaviour, or at least I thought could be helpful. 我的帖子是关于Linux上的行为的,但是我认为您可能会有类似的行为,或者至少我认为可能会有所帮助。 If not, let me know, I'll remove this useless 'noise'. 如果没有,请告诉我,我将消除这种无用的“噪音”。

I tried to reproduce your setup and I was quite astonished of seeing what you describe happening. 我试图重现您的设置,而看到您所描述的情况我感到非常惊讶。 Looking deeper helped me figure out that there was actually something more subtil; 深入研究有助于我弄清楚实际上还有一些枯燥的东西。 if you have a look to strace, you'll see somthing like: 如果您想追踪一下,就会看到类似以下内容:

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

see the lines with ERESTARTSYS and the EINTR : the sistem call being interrupted is actually rt_sigreturn resumed , not futex (the system call underlying the sem_wait) as you expected. 参见带有ERESTARTSYSEINTR :被中断的sistem调用实际上是rt_sigreturn resumed ,而不是您期望的futex (作为sem_wait的系统调用)。 I must say I was quite puzzled but reading the man gave some interesting clues (man 7 signal): 我必须说我很困惑,但是读了那个男人给出了一些有趣的线索(男人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).

So I guess you have a kernel that has a similar behaviour (see netBSD doc?) and you can observe that the system call automatically restart without any chance for you to see it. 因此,我猜您有一个行为类似的内核(请参见netBSD doc?),并且您可以观察到系统调用自动重新启动,而您没有机会看到它。

That said, I completely removed the sem_post() from your program and just sent signal to 'break' the sem_wait() ans looking at strace I saw (filtering on the bee thread): 就是说,我从程序中完全删除了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)

I must say I don't master the details, but the kernel seems to find out where I'm trying to stand and make the whole thing have the correct behaviour: 我必须说我不掌握细节,但是内核似乎可以找出我要站立的地方并使整个东西具有正确的行为:

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

Thanks for your answer OznOg, if I remove the last sem_post and make the last sleep a little longer, I get this with ktrace: 感谢您的回答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

Seems like sem_wait will only return by either an exit or a sem_post.... 似乎sem_wait只会通过出口或sem_post返回。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM