gpt4 book ai didi

c - 为什么sem_wait不会在中断时解除阻塞(并返回-1)?

转载 作者:行者123 更新时间:2023-12-03 16:07:40 26 4
gpt4 key购买 nike

我有一个使用sem_wait的程序。 Posix specification说:

The sem_wait() function is interruptible by the delivery of a signal.



此外,在有关错误的部分中,它表示:

[EINTR] - A signal interrupted this function.



但是,在我的程序中,发送信号不会取消阻止调用(并按照规范中的指示返回 -1)。

一个最小的例子可以在下面找到。发送信号后,该程序会挂起并且 sem_wait永远不会解除阻塞。
#include <semaphore.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

sem_t sem;

void sighandler(int sig) {
printf("Inside sighandler\n");
}

void *thread_listen(void *arg) {
signal(SIGUSR1, &sighandler);
printf("sem_wait = %d\n", sem_wait(&sem));
return NULL;
}

int main(void) {

pthread_t thread;

sem_init(&sem, 0, 0);

pthread_create(&thread, NULL, &thread_listen, NULL);

sleep(1);
raise(SIGUSR1);

pthread_join(thread, NULL);

return 0;
}

程序输出 Inside sighandler然后挂起。

关于此还有另一个问题 here,但实际上并没有提供任何清晰度。

我是否误解了规范说明?仅供引用,我的计算机使用Ubuntu GLIBC 2.31-0ubuntu9。

最佳答案

有三个原因导致该程序无法正常运行的原因,其中只有两个是可修复的。

  • 正如David Schwartz的回答所指出的那样,在多线程程序中,raise向调用raise的线程发送信号。

    为了将信号发送到所需的线程,在此测试程序中,将raise(SIGUSR1)更改为pthread_kill(thread, SIGUSR1)。但是,如果您希望该特定线程在发送给整个过程时处理SIGUSR1,那么您需要做的就是在所有线程中使用pthread_sigmask阻止SIGUSR1,但本应处理该线程的线程除外。 (有关更多详细信息,请参见下文。)
  • 在使用glibc的系统上,signal安装一个不会中断阻塞系统调用的信号处理程序。要获得一个信号处理程序,您需要使用sigaction并将sa_flags设置为不包含SA_RESTART的值。例如,
      struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_handler = sighandler;
    sa.sa_flags = 0;
    sigaction(SIGUSR1, &sa, 0);

    注意:不能保证memset(&sa, 0, sizeof sa)sigemptyset(&sa.sa_mask)具有相同的效果。

    注意:信号处理程序是全局进程的,因此调用sigaction的线程无关紧要。在几乎所有情况下,多线程程序都应在创建任何线程之前执行sigaction中的所有main调用,只是要确保信号处理程序在任何信号发生之前都处于事件状态。
  • 在线程有机会调用sem_wait之前,信号可以传递到线程。如果发生这种情况,将调用信号处理程序并返回,然后调用sem_wait并将其永远阻塞。在此测试程序中,可以通过增加sleepmain的长度来使这种情况变得不可能,但是没有办法使之成为不可能。这是无法解决的原因。

    少数系统调用在 sleep 时自动解除对信号的阻塞,然后在返回用户空间之前再次阻塞它们,例如 sigsuspend sigwaitinfo pselect 。这些是唯一可以避免这种竞争情况的系统调用。

    对于必须处理信号的多线程程序,最佳实践是让一个线程专用于信号处理。为了使该功能可靠地工作,您应该在创建任何线程之前,在SIGABRT的开始处阻塞除同步CPU异常(SIGBUSSIGFPESIGILLSIGSEGVSIGSYSSIGTRAPmain)以外的所有信号。然后,为您要处理的信号设置一个什么都不做的信号处理程序(带有SA_RESTART);这些将永远不会被真正调用,它们的目的是防止由于SIGUSR1或其他默认操作而导致内核杀死进程。您关心的信号集必须包括用于用户中断的所有信号:SIGHUPSIGINTSIGPWRSIGQUITSIGTERMSIGTSTPSIGXCPUSIGXFSZ。最后,创建信号处理线程,该线程循环调用sigwaitinfo来获取适当的信号集,并使用管道或条件变量或除信号之外的任何东西将消息调度到其余线程。除了sigwaitinfo之外,此线程绝不能阻止任何系统调用。

    在此测试程序的情况下,信号处理线程将通过调用SIGUSR1响应sem_post(&sem)。这将唤醒监听器线程,或者首先使监听器线程不会在sem_wait上被阻塞。
  • 关于c - 为什么sem_wait不会在中断时解除阻塞(并返回-1)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61448888/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com