2009-06-01 68 views
4

我想在Linux上创建一个包装器,它控制一次允许多少个并发执行的东西。为此,我正在使用系统范围计数信号量。我创建信号量,执行sem_wait(),启动子进程,然后在子进程结束时执行sem_post()。没事儿。信号安全使用sem_wait()/ sem_post()

问题是如何安全地处理发送到这个包装的信号。如果它没有捕获到信号,那么命令可能会终止而不执行sem_post(),导致信号计数永久减少1。所以,我创建了一个信号处理程序,它的作用是sem_post()。但仍然存在问题。

如果处理程序是sem_wait()连接之前进行,信号可以在sem_wait()完成之前到达,造成sem_post()没有sem_wait()发生。如果我在设置信号处理程序之前执行sem_wait(),则可能会发生相反的情况。

明显的下一步是在设置处理程序和sem_wait()期间阻止信号。这是我现在有伪代码:

void handler(int sig) 
{ 
    sem_post(sem); 
    exit(1); 
} 

... 
sigprocmask(...); /* Block signals */ 
sigaction(...);  /* Set signal handler */ 
sem_wait(sem); 
sigprocmask(...); /* Unblock signals */ 
RunChild(); 
sem_post(sem); 
exit(0); 

现在的问题是,sem_wait()可以阻止在此期间,信号被阻断。试图杀死进程的用户最终可能会诉诸于“kill -9”,这是我不想鼓励的行为,因为无论如何我都无法处理这种情况。我可以使用sem_trywait()一小段时间,并测试sigpending()但影响公平性,因为不再保证等待信号量最长的进程将接下来运行。

这里有一个真正安全的解决方案,它允许我在信号量采集期间处理信号吗?我正在考虑诉诸全球性的“我有信号量”,并取消信号阻塞,但自从获得信号量和设置全局以来,这并非100%安全,这不是原子性的,但可能比等待时阻止信号更好。

回答

6

您确定sem_wait()导致信号被阻止吗?我不认为这是事实。 man page for sem_wait()表示如果EINTR错误代码被信号中断,则返回sem_wait()

你应该能够处理这个错误代码,然后你的信号将被接收。你遇到过没有收到信号的情况吗?

我会确保您处理sem_wait()可以返回的错误代码。虽然可能很少,但如果你想100%确定你想覆盖100%的基数。

+0

对不起,如果我不清楚。sem_wait()不会像您所说的那样导致信号被阻塞,但我正在使用sigprocmask()来阻止它们。但是,我认为你有正确的解决方案,即查看EINTR错误代码,这意味着处理程序不应该退出,但设置一些标志来表示“退出时间”。我会测试一下。谢谢。 – Jeremy 2009-06-01 23:46:29

+0

工作。我删除了信号的阻塞,并将信号处理程序更改为设置全局含义,现在是时候退出了。如果sem_wait()返回一个错误,那么我退出这是很好,并保留信号计数。如果我在等孩子,我也测试timeToQuit global。如果是时候退出,我杀了孩子,并做一个sem_post(),它保留了信号计数。谢谢。这应该是信号安全的,忽略kill -9。 – Jeremy 2009-06-02 00:04:45

0

你确定你正确地解决了这个问题吗?如果您想等待孩子终止,则可能需要使用系统调用waitpid()。正如你所观察到的那样,如果孩子可能接收到信号,那么期望孩子做sem_post()是不可靠的。

+0

实际上,孩子没有意识到信号量,父母在伪代码中的RunChild()函数中执行了一个waitpid(),我没有显示。该部分没有问题,并且正在处理在sem_take()完成后发送给父节点的子节点和信号。我担心设置信号处理程序和sem_wait()之间的关键部分,但检查sem_wait()错误代码是我无法看到的简单解决方案。 – Jeremy 2009-06-02 00:08:08

0

我知道这是旧的,但对于那些还在读书礼貌谷歌的利益......

最简单的(也是唯一的?)强大的解决这个问题是使用System V信号,这允许客户端以内核自动返回的方式获取信号量资源否否问题如何处理退出。