C:暂停系统()调用

时间:2014-10-22 22:22:30

标签: c multithreading signals system

当我尝试暂停执行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()命令?有没有办法停止系统调用的应用程序而无需等到程序完成?

非常感谢你!

2 个答案:

答案 0 :(得分:3)

system在一个单独的进程中运行该命令,该进程甚至不与调用程序共享地址空间,更不用说信号处理程序了。调用system的过程位于waitpid(或等效),因此暂停和取消暂停它几乎没有效果(除非暂停,否则它将无法返回循环再次致电system。)

简而言之,无法使用发送到父进程的信号来暂停在子进程中运行的可执行文件,例如使用system()调用或使用fork()/exec()

如果可执行文件本身实现了该功能(这不太可能,除非您自己编写),您可以将信号传递给该进程,而不是调用system的进程。

或者,您可以将SIGSTOP信号发送到可执行文件的进程,该进程将无条件地暂停执行。要做到这一点,你需要知道它的pid,它建议使用fork()/exec()/waitpid()序列 - 比system()更多的工作,但更清洁,更安全,通常更有效 - - 而且你需要处理几个问题:

  • 进程无法阻止或捕获SIGSTOP,但它可以捕获SIGCONT,因此序列不一定是100%透明的。

  • 如果停止的进程是终端的控制进程,则需要特别小心,因为当它以SIGCONT恢复时,它将需要重新获取终端。此外,如果应用程序将终端置于非标准状态 - 例如,通过使用通常将终端置于原始模式并禁用回显的readlinecurses库,则终端可能无法使用。

  • 由于处理的子项被停止,您的进程将收到SIGCHLD信号。所以你需要正确处理。

答案 1 :(得分:0)

我想在@rici的帮助下向您展示我的(缩短的)结果代码。再次,非常感谢你。 简而言之,代码分叉一个新进程(调用fork)并在那里用exec执行命令。然后,父母捕获用户定义的信号SIGNAL_PAUSESIGNAL_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);
}