当我尝试暂停执行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)
时,此代码就可以工作了。但是,当处理程序处于活动状态时,系统()调用的程序不会暂停。
int i;
for(i=0;i<10;i++) {
sleep(1);
printf("Just a text");
}
有没有一种方法可以通过使用线程信号来暂停system()命令的执行?还有没有一种方法可以停止系统调用的应用程序,而不需要等到程序完成?
提前非常感谢!
system
在一个单独的进程中运行命令,该进程甚至不与调用程序共享地址空间,更不用说信号处理程序了。调用system
的进程位于waitpid
(或等效进程)中,因此暂停和取消暂停它几乎没有效果(除了如果暂停,它不会返回到循环中再次调用system
。)
简而言之,没有办法使用发送给父进程的信号来暂停正在子进程中运行的可执行文件,例如使用system()
调用或使用fork()/exec()
。
如果可执行文件本身实现了该功能(除非您自己编写,否则这是不可能的),那么您可以将信号传递给该进程,而不是调用system
的进程。
或者,您可以向可执行文件的进程发送SIGSTOP
信号,它将无条件地暂停执行。要做到这一点,你需要知道它的pid,它建议使用fork()/exec()/waitpid()
序列——比system()
多做一点工作,但更干净、更安全,而且通常更高效——你需要处理几个问题:
-
进程不能阻塞或捕获
SIGSTOP
,但它可以捕获SIGCONT
,因此序列不一定是100%透明的。 -
如果停止的进程是终端的控制进程,则需要特别小心,因为当用
SIGCONT
恢复时,需要重新获取终端。此外,如果应用程序将终端置于非标准状态——例如,通过使用readline
或curses
库,这些库通常将终端置于原始模式并禁用回显——则终端可能会变得不可用。 -
由于处理的子进程已停止,您的进程将收到
SIGCHLD
信号。所以你需要正确处理。
在@rici的帮助下,我想向您展示我的(缩短的)结果代码。再次,非常感谢。稍后描述,代码分叉一个新进程(调用fork
),并在那里执行一个带有exec
的命令。父级然后捕获用户定义的信号CCD_ 23和CCD_。每当命令完成(被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 signaln");
while (restart_benchmark == 1 && caughtSignal != SIGNAL_RESUME) {
sigwait (signalsBetweenControllerandLoad, &caughtSignal);
if (caughtSignal == SIGNAL_STOP) {
printf("Load caught stop signal when waiting for resumen");
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 resumedn");
}
} 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 %dn",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);
}