当我尝试暂停执行system()
调用时,我在C中遇到了问题。
线程重复调用某个应用程序(例如某些基准测试)。每当它收到信号SIGUSR1
时,执行将暂停并在接收SIGUSR2
时恢复。
源代码如下:
#include <signal.h>
#include <pthread.h>
void* run_app(sigset_t* signalsBetweenControllerandLoad)
{
/* assign handler */
signal(SIGUSR1, pausesignal_handler)
signal(SIGUSR2, pausesignal_handler)
pthread_sigmask(SIG_UNBLOCK, signalsBetweenControllerandLoad, NULL))
/* call application repeatedly */
while(1) {
system(SOMECOMMAND);
}
return(0);
}
static void pausesignal_handler(int signo)
{
int caughtSignal;
caughtSignal = 0;
/* when SIGUSR1 is received, wait until SIGUSR2 to continue execution */
if (signo == SIGUSR1) {
signal(signo, pausesignal_handler);
while (caughtSignal != SIGUSR2) {
sigwait (signalsBetweenControllerandLoad, &caughtSignal);
}
}
}
当我使用一些命令(例如下面的for循环进行一些计算)而不是system(SOMECOMMAND)
时,此代码可以正常工作。但是当处理程序处于活动状态时,system()调用的程序不会暂停。
int i;
for(i=0;i<10;i++) {
sleep(1);
printf("Just a text");
}
有没有办法通过线程信号暂停执行system()命令?有没有办法停止系统调用的应用程序而无需等到程序完成?
非常感谢你!
答案 0 :(得分:3)
system
在一个单独的进程中运行该命令,该进程甚至不与调用程序共享地址空间,更不用说信号处理程序了。调用system
的过程位于waitpid
(或等效),因此暂停和取消暂停它几乎没有效果(除非暂停,否则它将无法返回循环再次致电system
。)
简而言之,无法使用发送到父进程的信号来暂停在子进程中运行的可执行文件,例如使用system()
调用或使用fork()/exec()
。
如果可执行文件本身实现了该功能(这不太可能,除非您自己编写),您可以将信号传递给该进程,而不是调用system
的进程。
或者,您可以将SIGSTOP
信号发送到可执行文件的进程,该进程将无条件地暂停执行。要做到这一点,你需要知道它的pid,它建议使用fork()/exec()/waitpid()
序列 - 比system()
更多的工作,但更清洁,更安全,通常更有效 - - 而且你需要处理几个问题:
进程无法阻止或捕获SIGSTOP
,但它可以捕获SIGCONT
,因此序列不一定是100%透明的。
如果停止的进程是终端的控制进程,则需要特别小心,因为当它以SIGCONT
恢复时,它将需要重新获取终端。此外,如果应用程序将终端置于非标准状态 - 例如,通过使用通常将终端置于原始模式并禁用回显的readline
或curses
库,则终端可能无法使用。
由于处理的子项被停止,您的进程将收到SIGCHLD
信号。所以你需要正确处理。
答案 1 :(得分:0)
我想在@rici的帮助下向您展示我的(缩短的)结果代码。再次,非常感谢你。
简而言之,代码分叉一个新进程(调用fork
)并在那里用exec
执行命令。然后,父母捕获用户定义的信号SIGNAL_PAUSE
和SIGNAL_RESUME
,并相应地将信号转发给分叉的孩子。每当命令完成 - 由waitpid
捕获时 - 父级再次分叉并重新启动加载。
这将重复进行,直到SIGNAL_STOP
发送给孩子获得SIGINT
并被取消。
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#define SIGNAL_PAUSE (SIGUSR1)
#define SIGNAL_RESUME (SIGUSR2)
#define SIGNAL_STOP (SIGSYS)
/* File scoped functions */
static void pausesignal_handler(int signo);
static void stopsignal_handler(int signo);
void send_signal_to_load_child(int signo);
/*Set file scope variables as handlers can only have signal-number as argument */
sigset_t* signalsBetweenControllerandLoad;
int restart_benchmark;
pid_t child_pid;
void* Load(char* load_arguments[MAX_NR_LOAD_ARGS], sigset_t* signalsToCatch) {
int load_ID;
pid_t p;
signalsBetweenControllerandLoad = signalsToCatch;
/* set signal handlers to catch signals from controller */
signal(SIGNAL_PAUSE, pausesignal_handler)
signal(SIGNAL_RESUME, pausesignal_handler)
signal(SIGNAL_STOP, stopsignal_handler)
pthread_sigmask(SIG_UNBLOCK, signalsBetweenControllerandLoad[load_ID], NULL)
/* Keep restarting benchmark until Stop signal was received */
restart_benchmark[load_ID] = 1;
/* execute benchmark, repeat until stop signal received */
while(restart_benchmark[load_ID])
{
if (child_pid == 0) {
if ((p = fork()) == 0) {
execv(load_arguments[0],load_arguments);
exit(0);
}
}
/* Parent process: Wait until child with benchmark finished and restart it */
if (p>0) {
child_pid = p; /* Make PID available for helper functions */
wait(child_pid); /* Wait until child finished */
child_pid = 0; /* Reset PID when benchmark finished */
}
}
return(0);
}
static void pausesignal_handler(int signo) {
static double elapsedTime;
int caughtSignal;
caughtSignal = 0;
if (signo == SIGNAL_PAUSE) {
send_signal_to_load_child(SIGSTOP);
printf("Load Paused, waiting for resume signal\n");
while (restart_benchmark == 1 && caughtSignal != SIGNAL_RESUME) {
sigwait (signalsBetweenControllerandLoad, &caughtSignal);
if (caughtSignal == SIGNAL_STOP) {
printf("Load caught stop signal when waiting for resume\n");
stopsignal_handler(caughtSignal);
} else if (caughtSignal != SIGNAL_RESUME) {
printf("Load caught signal %d which is not Resume (%d), keep waiting...\n",caughtSignal,SIGNAL_RESUME);
}
}
if (restart_benchmark[load_ID]) {
send_signal_to_load_child(SIGCONT, load_ID);
printf("Load resumed\n");
}
} else {
printf("Load caught unexpected signal %d.\n",signo);
}
/* reassign signals for compatibility reasons */
signal(SIGNAL_PAUSE, pausesignal_handler);
signal(SIGNAL_RESUME, pausesignal_handler);
}
static void stopsignal_handler(int signo) {
double elapsedTime;
signal(SIGNAL_STOP, stopsignal_handler);
if (signo == SIGNAL_STOP) {
restart_benchmark = 0;
send_signal_to_load_child(SIGINT);
printf("Load stopped.\n");
} else {
printf("catched unexpected stop-signal %d\n",signo);
}
}
void send_signal_to_load_child(int signo) {
int dest_pid;
dest_pid = child_pid;
printf("Error sending %d to Child: PID not set.\n",signo);
kill(dest_pid, signo);
}