如何通过信号重复重启程序
How to repeatedly restart a program via signal
我想要一种方便的方式来重新启动程序。我以为我可以捕捉到一个信号(示例中的 USR1)并调用 exec.
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
char* const* args;
void restart() {
printf("restarting\n");
execv(args[0], args);
}
int main(int argc, char* const argv[]) {
printf("Starting\n");
args = argv;
signal(SIGUSR1, restart);
raise(SIGUSR1); // could use pkill -SIGUSR1 file-name instead
pause();
printf("Terminated normally\n");
return 0;
}
上面的例子还算可以。输出是
Starting
restarting
Starting
然后挂起。仍然可以接收到其他信号。
我想我只是未能清除信号。预期的行为是程序无限期地重新启动。
Linux 的 man 2 signal
解释了当您使用 signal
设置信号处理程序时会发生什么。有两种可能:
- 信号的处理重置为
SIG_DFL
,然后调用处理程序。要再次处理此信号,您需要重新建立信号处理程序。这会如您预期的那样工作。
或者:
- 信号被阻塞,然后处理程序被调用。当 handler returns 时,信号被解除阻塞。如果处理程序没有 return,信号仍然被阻塞,这就是你遇到的情况。
所以一个可以使用您的代码,而另一个不能。但两者中哪一个适用?没有可靠的方法可以知道。 Posix 允许两种可能性,并且两种可能性都存在于不同的平台上。为此,Linux 联机帮助页建议:
The only portable use of signal() is to set a signal's disposition to SIG_DFL
or SIG_IGN
... [D]o not use it for [the purpose of establishing a signal handler].
POSIX.1 solved the portability mess by specifying sigaction(2)
, whichprovides explicit control of the semantics when a signal handler is invoked; use that interface instead of signal()
.
这是个好建议。当您更改为使用 sigaction
时,您可能希望选择上面的第一个选项,这需要:
sa.sa_flags = SA_RESETHAND | SA_NODEFER
这也来自 Linux 联机帮助页,非常值得完整阅读。 (当然,sigaction
联机帮助页更为相关。)
在您的平台上,SIGUSR1 被屏蔽在信号处理程序中,这是 signal
的典型但非强制行为(相比之下,参见 SA_NODEFER and sigaction
)。
这 mask is inherited across the execve
,因此 SIGUSR1 在您的第二次执行中处于等待状态,从未交付。
在 main()
的顶部尝试这样的事情,也许在处理程序内部,看看发生了什么:
static int
is_blocked(int sig) {
sigset_t ss;
sigemptyset(&ss);
(void)sigprocmask(SIG_BLOCK, NULL, &ss);
return sigismember(&ss, sig);
}
不确定为什么会这样 - 用 sigset 替换 signal
首先,定义__USER_XOPEN_EXTENDED否则sigset未定义
#define __USE_XOPEN_EXTENDED
#include <signal.h>
将信号更改为 sigset
...
sigset(SIGUSR1, restart);
...
然后它会一直重启,直到它被杀死。也许有人可以解释为什么 sigset 有效而 signal 无效。
我想要一种方便的方式来重新启动程序。我以为我可以捕捉到一个信号(示例中的 USR1)并调用 exec.
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
char* const* args;
void restart() {
printf("restarting\n");
execv(args[0], args);
}
int main(int argc, char* const argv[]) {
printf("Starting\n");
args = argv;
signal(SIGUSR1, restart);
raise(SIGUSR1); // could use pkill -SIGUSR1 file-name instead
pause();
printf("Terminated normally\n");
return 0;
}
上面的例子还算可以。输出是
Starting
restarting
Starting
然后挂起。仍然可以接收到其他信号。
我想我只是未能清除信号。预期的行为是程序无限期地重新启动。
Linux 的 man 2 signal
解释了当您使用 signal
设置信号处理程序时会发生什么。有两种可能:
- 信号的处理重置为
SIG_DFL
,然后调用处理程序。要再次处理此信号,您需要重新建立信号处理程序。这会如您预期的那样工作。
或者:
- 信号被阻塞,然后处理程序被调用。当 handler returns 时,信号被解除阻塞。如果处理程序没有 return,信号仍然被阻塞,这就是你遇到的情况。
所以一个可以使用您的代码,而另一个不能。但两者中哪一个适用?没有可靠的方法可以知道。 Posix 允许两种可能性,并且两种可能性都存在于不同的平台上。为此,Linux 联机帮助页建议:
The only portable use of signal() is to set a signal's disposition to
SIG_DFL
orSIG_IGN
... [D]o not use it for [the purpose of establishing a signal handler].POSIX.1 solved the portability mess by specifying
sigaction(2)
, whichprovides explicit control of the semantics when a signal handler is invoked; use that interface instead ofsignal()
.
这是个好建议。当您更改为使用 sigaction
时,您可能希望选择上面的第一个选项,这需要:
sa.sa_flags = SA_RESETHAND | SA_NODEFER
这也来自 Linux 联机帮助页,非常值得完整阅读。 (当然,sigaction
联机帮助页更为相关。)
在您的平台上,SIGUSR1 被屏蔽在信号处理程序中,这是 signal
的典型但非强制行为(相比之下,参见 SA_NODEFER and sigaction
)。
这 mask is inherited across the execve
,因此 SIGUSR1 在您的第二次执行中处于等待状态,从未交付。
在 main()
的顶部尝试这样的事情,也许在处理程序内部,看看发生了什么:
static int
is_blocked(int sig) {
sigset_t ss;
sigemptyset(&ss);
(void)sigprocmask(SIG_BLOCK, NULL, &ss);
return sigismember(&ss, sig);
}
不确定为什么会这样 - 用 sigset 替换 signal
首先,定义__USER_XOPEN_EXTENDED否则sigset未定义
#define __USE_XOPEN_EXTENDED
#include <signal.h>
将信号更改为 sigset
...
sigset(SIGUSR1, restart);
...
然后它会一直重启,直到它被杀死。也许有人可以解释为什么 sigset 有效而 signal 无效。