使用pthread_sigmask()保护sem_wait()不受信号影响

时间:2015-03-16 21:51:51

标签: c linux multithreading

我有一个通过第三方库访问硬件资源(SPI)的库。我的库,反过来又是SPI资源,由多个进程访问,所以我需要用信号量锁定资源,锁定函数如下:

static int spi_lock(void)
{
    struct timespec ts;

    if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
    {
        syslog(LOG_ERR,"failed to read clock: %s\n", SPISEM, strerror(errno));
        return 3;
    }
    ts.tv_sec += 5;
    if (sem_timedwait(bcoms->spisem, &ts) == -1)
    {
        syslog(LOG_ERR,"timed out trying to acquire %s: %s\n", SPISEM, strerror(errno));
        return 1;
    }
    return 0;
}

static int spi_unlock(void)
{
    int ret = 1;

    if (sem_post(bcoms->spisem))
    {
        syslog(LOG_ERR,"failed to release %s: %s\n", SPISEM, strerror(errno));
        goto done;
    }
    ret = 0;
done:
    return ret;
}

现在我的问题是守护进程中使用了库,并且该守护进程通过kill信号停止。有时候,当我持有信号量锁时,我得到了kill信号,因此服务器无法成功重启,因为锁是永久性的。为了解决这个问题,我试图阻止信号,如下所示(我正在等待硬件在atm上测试):

static int spi_lock(void)
{
    sigset_t nset;
    struct timespec ts;

    sigfillset(&nset);
    sigprocmask(SIG_BLOCK, &nset, NULL);

    if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
    {
        syslog(LOG_ERR,"failed to read clock: %s\n", SPISEM, strerror(errno));
        return 3;
    }
    ts.tv_sec += 5; // 5 seconds to acquire the semaphore is HEAPS, so we better bloody get it !!! 
    if (sem_timedwait(bcoms->spisem, &ts) == -1)
    {
        syslog(LOG_ERR,"timed out trying to acquire %s: %s\n", SPISEM, strerror(errno));
        return 1;
    }
    return 0;
}

static int spi_unlock(void)
{
    sigset_t nset;
    int ret = 1;

    if (sem_post(bcoms->spisem))
    {
        syslog(LOG_ERR,"failed to release %s: %s\n", SPISEM, strerror(errno));
        goto done;
    }

    sigfillset(&nset);
    sigprocmask(SIG_UNBLOCK, &nset, NULL);
    ret = 0;
done:
    return ret;
}

但是在阅读了sigprocmask()的手册页之后,它在多线程系统中说使用了pthread_sigmask(),我要保护的其中一个服务器将是多线程的。我不明白的是,如果我在库中使用pthread_sigmask(),并且主父线程产生一个SPI读取线程,该线程使用我的库中的那些锁定函数,读取线程将受到保护,但不能是主线程仍然接收到kill信号并取下守护进程,同时我拿着互斥锁,读取线程上禁用信号让我无处可去?如果有,这个锁定问题有更好的解决方案吗?

感谢。

2 个答案:

答案 0 :(得分:2)

确实,您已正确分析了问题 - 屏蔽信号并不能保护您。屏蔽信号不是防止在不一致状态下共享数据(如文件或共享信号量)终止进程的正确工具。

你可能应该做什么,如果你想优雅地退出某些信号,就是让程序安装信号处理程序来捕获终止请求并将其提供给正常的程序逻辑。您可以使用以下几种方法:

  1. 通过管道向您自己发送终止请求。如果您的程序是围绕可以等待管道输入的poll循环构建的,那么这很有效。

  2. 使用sem_post,一个异步信号安全同步功能,将信号报告给程序的其余部分。

  3. 从主线程启动一个专用的信号处理线程,然后阻塞主线程中的所有信号(并通过继承,阻止所有其他新线程)。这个线程只能执行for(;;) pause();,因为pause是异步信号安全的,你可以从信号处理程序中调用你想要的任何函数 - 包括与其他线程同步所需的pthread同步函数。 / p>

  4. 请注意,这种方法仍然不会是完美的#34;因为你永远无法捕获或阻止SIGKILL。如果用户决定使用SIGKILLkill -9)终止您的流程,那么信号量可能会处于不良状态,您无法做任何事情。

答案 1 :(得分:0)

我不认为你的方法会奏效。你不能阻止SIGKILL或SIGSTOP。除非你说守护进程获得了不同的信号(如SIGHUP)。但即便如此,我认为阻止来自库调用的所有信号是不好的做法。这可能会对调用应用程序产生负面影响。例如,应用程序可能依赖于特定信号,并且缺少任何此类信号可能导致其无法正常工作。

事实证明,使用信号量可能不是一种简单的方法来解决您的问题。因此,另一种方法是使用“flock”之类的东西。这解决了您的问题,因为它基于打开的文件描述符。如果进程因持有flock而死亡,则关联的文件描述符将自动关闭,因此将释放flock。