首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用互斥锁等待退出信号

使用互斥锁等待退出信号
EN

Stack Overflow用户
提问于 2022-10-21 09:15:11
回答 3查看 73关注 0票数 1

当进程接收到退出信号时,我希望进程退出。所以主线程需要等待退出信号,我以前这样写过。

代码语言:javascript
复制
int signal(int a) {
    exit_flag = 0;
}
//wait in main thread
while(exit_flag){
    sleep(1);
}

但反应有点慢。所以我找到了这样一种新方法,

代码语言:javascript
复制
//signal function
int signal(int a) {
    pthread_mutex_unlock(&mutex);
}
//main thread

pthread_mutex_lock(&lock);
pthread_mutex_lock(&lock);

我不确定这是否正确。我的问题是,如果这是等待出口信号的首选方式,或者你可以告诉我一个更好的方法?

衷心感谢大家的回复。我知道在信号处理程序中使用互斥对象是不安全的。但是,如果我通过其他方法发送退出消息,比如jrpc调用之类的,那么上面的代码正确吗?

EN

回答 3

Stack Overflow用户

发布于 2022-10-21 13:19:53

我的问题是,如果这是等待出口信号的首选方式,或者你可以告诉我一个更好的方法?

不,因为@AndrewHenle在他的回答中描述得很好的原因。

有多种正确的方法可以做到这一点,但其中许多方法重新发明了pause()函数的轮子及其改进的替代sigsuspend()。这两种方法都是专门为等待信号的传递而设计的。Glibc手册包含关于如何将它们用于此目的的一节。

或者,如果您的进程是多线程的,并且只希望将一个线程用于等待,那么就会出现sigwait()sigtimedwait()

等待整个过程

假设您希望整个进程停止,直到一个SIGUSR1被交付给它,然后退出。安装信号处理程序后,可以使用以下内容:

代码语言:javascript
复制
// sufficient for this case even in a multi-threaded program:
volatile sig_atomic_t exit_flag;

// ...

/*
 * Wait, if necessary, until a SIGUSR1 is received, then exit with status 0.
 *
 * Assumes that a signal handler for SIGUSR1 is already installed, that the
 * handler will set variable `exit_flag` to nonzero when it runs, and that
 * nothing else will modify exit_flag incompatibly.
 */
void wait_to_exit(void) {
    sigset_t temp_mask, mask;

    sigemptyset(&temp_mask);
    sigaddset(&temp_mask, SIGUSR1);

    /*
     * Temporarily block the signal we plan to wait for, to ensure that we
     * don't miss a signal.
     */
    sigprocmask(SIG_BLOCK, &temp_mask, &mask);

    // Prepare to wait for the expected signal even if it is presently blocked
    sigdelset(&mask, SIGUSR1);

    // if we haven't already received the signal, then block the whole process until we do
    while (!exit_flag) {
        sigsuspend(&mask);
    }

    // No need to reset any signal masks because we're about to ...
    exit(0);
}

关于标志的数据类型

为了扩展上述代码中的注释之一,即使在多线程程序中,volatile sig_atomic_t也是一个足够的类型。sigsuspend()被指定为在信号处理程序返回后返回,并且信号处理程序直到对标志的写入实际发生之后才会返回(因为不稳定)。然后,调用σ挂起的线程必须读取处理程序写入的值,或者随后写入同一变量的其他值,同样是因为易变性。

然而,对于线程安全来说,volatile通常是不够的,因此即使在这种情况下没有必要,您也可以通过使用atomic_flag (在stdatomic.h中声明)来考虑避开这个问题和任何关于它的不确定性;这需要对C11或更高版本的支持。

只需要等待一个线程

对于在许多等待信号的线程中有一个线程的情况,它的结构应该是非常不同的。在这种情况下,您不需要信号处理程序或标志,但是您应该通过sigprocmask()阻塞所有线程的预期信号。

代码语言:javascript
复制
    sigset_t mask;

    sigemptyset(&mask);
    sigaddset(&mask, SIGUSR1);

    sigprocmask(SIG_BLOCK, &mask, NULL);

这将防止在任何线程中执行预期信号的默认(或自定义)处理,并确保等待线程以外的线程不会消耗该信号。通常,它应该在程序执行的早期就完成。

然后,为了等待信号,线程执行以下操作:

代码语言:javascript
复制
void wait_to_exit(void) {
    sigset_t mask;
    int sig;

    sigemptyset(&temp_mask);
    sigaddset(&temp_mask, SIGUSR1);

    // if we haven't already received the signal, then block this thread until we do
    if (sigwait(&mask, &sig) != 0) {
        // Something is terribly wrong
        fputs("sigwait failed\n", stderr);
        abort();
    }
    assert(sig == SIGUSR1);

    // Terminate the process (all threads)
    exit(0);
}

如果你是说一个普通的“信号”

如果您的意思是“信号”作为同步通知的通用术语,而不是使用C信号处理工具,那么@AndrewHenle关于信号量的建议将是完美的。在这种情况下,一定要接受这个答案。

票数 2
EN

Stack Overflow用户

发布于 2022-10-21 12:31:21

不,不正确,不对。

首先,pthread_mutex_unlock()不是异步信号安全功能,不能从信号处理程序中安全地调用。

第二,互斥锁由线程锁定。如果信号处理程序运行在与具有互斥锁的线程不同的线程中,则为无法解锁互斥

如果线程试图解锁未锁定的互斥对象或未锁定的互斥锁,则当下表的not所有者列时,pthread_mutex_unlock()的行为应与解锁中所描述的相同。

该表中唯一的条目是“未定义的行为”和“返回的错误”。而且您无法真正控制信号将被传递到哪个线程(至少在编写复杂的信号处理代码的情况下不会如此)。

第三,这个代码

代码语言:javascript
复制
pthread_mutex_lock(&lock);
pthread_mutex_lock(&lock);

对于任何类型的互斥对象,在同一张桌子上都不会安全阻塞。该代码要么死锁,要么继续锁定互斥锁,要么调用未定义的行为,这些行为甚至可能“工作”,但会使程序处于未知状态,这可能会在稍后导致错误。

编辑:

第四,如果信号被多次传递,对pthread_mutex_unlock()的多次调用将再次导致错误或未定义的行为。

但是有一种异步信号安全的方法来阻止等待信号:sem_wait()

sem_post() 异步信号安全的,可以从信号处理程序中安全地调用,也可以安全地多次调用--对sem_post()的多次调用将只允许对sem_wait()的相应次数调用以获得信号量,但您只需要使用一个:

代码语言:javascript
复制
//signal function
int signal(int a) {
    sem_post(&sem);
}
//main thread

sem_wait(&sem);

请注意,在信号处理程序中调用而不是安全。

票数 1
EN

Stack Overflow用户

发布于 2022-10-21 14:13:40

除了接受信号(通过sigwaitsigtimedwait)之外,我也是受人尊敬的自管戏法的粉丝:“维护管道并选择管道输入的可读性。在信号处理程序中,向管道输出写入一个字节(非阻塞,以防万一)。”

我还要进一步指出,信号处理程序应该由sigaction作为SA_RESTARTable来安装。

现在,您可以安全地将信号传递与IO混合(例如,通过selectpoll,或者只是阻塞的read,直到该字节出现)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74151419

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档