首先让我说这里有很多问题。
本文的一个任务要求我编写一个执行子程序的程序,如果它的运行时间(不是wall time,而是user+sys)大于一个特定值,或者它的RAM消耗大于另一个指定值,则将其杀死。
我还没弄清楚内存部分。我用setitmer和ITIMER教授的信号来消磨时间。(因为ITIMER_PROF收集实际的CPU使用情况,而不是设置一个时间起点,然后计算x个时间量)
我使用setimer的原因是我需要的精度比第二精度低。(例如,在1.75秒(1750000微秒)后终止进程)。setrlimit方法只有一秒钟的时间。
问题1当设置在父进程中时,带ITIME_PROF的setitimer为什么不工作?子进程的CPU/系统调用不是由它收集的?

childPID = fork();

if (childPID == -1){
        printf( "Puff paff ... fork() did not work !\n" );
        exit(1);
}

// Child
if(childPID == 0) {
    execvp(args[0], args);
    exit(1);
}
// Parent
else{
    // Using a ITIMER_PROF inside the parent program will not work!
    // The child may take 1 hour to execute and the parent will wait it out!
    // To fix this we need to use a ITIMER_REAL ( wall-time ) but that's not an accurate measurement
    struct itimerval timer;
    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = 500000;
    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = 500000;
    setitimer ( ITIMER_PROF, &timer, NULL);

    int status;
    waitpid(childPID,&status,0);
    if (WIFEXITED(status)) {
        fprintf(stderr, "Nice nice, the child exited ... with cPID = %d with status = %d \n", cPID, WEXITSTATUS(status) );
    }
}

问题2为什么这样做!?execvp是否覆盖所有函数(timeout_sigprof、main和其他函数)?难道不能有人捕捉到子程序中的信号并取代原来的函数吗?
void timeout_sigprof( int signum ){
    fprintf(stderr, "The alarm SIGPROF is here !\nThe actual pid: %d\n", getpid());
    //TODO: Write output and say the child terminated with
    // ram or time limit exceeded
    exit(105); // Note the 105 !
}

childPID = fork();

if (childPID == -1){
        printf( "Puff paff ... fork() did not work !\n" );
        exit(1);
}

// Child
if(childPID == 0) {
    //
    struct sigaction sa;
    memset (&sa, 0, sizeof (sa));
    sa.sa_handler = &timeout_sigprof;
    sigaction (SIGPROF, &sa, NULL);

    struct itimerval timer;
    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = 250000;
    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = 250000;
    setitimer ( ITIMER_PROF, &timer, NULL);

    execvp(args[0], args);
    exit(1);
}
// Parent process
else {
    // Waiting for the child
    int status;
    waitpid(childPID,&status,0);
    if (WIFEXITED(status)) {
        fprintf(stderr, "Nice nice, the child exited ... with cPID = %d with status = %d \n", cPID, WEXITSTATUS(status) );
    }
    exit(0);
}

问题3为什么放在这里的dup2实际工作,让孩子的输入/输出被重定向?
childPID = fork();

if (childPID == -1){
        printf( "Puff paff ... fork() did not work !\n" );
        exit(1);
}

// Child
if(childPID == 0) {

    // Redirect all I/O to/from a file
    int outFileId = open("output", O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);

    // Redirect the output for the CHILD program. Still don't know why it works.
    dup2(outFileId, 1)

    // No idea why these dup2's work ! As i close the file descriptors here ?!
    close(outFileId);

    execvp(args[0], args);
    exit(1);
}
// Parent process
else {
    // Waiting for the child
    int status;
    waitpid(childPID,&status,0);
    if (WIFEXITED(status)) {
        fprintf(stderr, "Nice nice, the child exited ... with cPID = %d with status = %d \n", cPID, WEXITSTATUS(status) );
    }
    exit(0);
}

下面是我编写的代码,只有在程序运行了X个时间(X=500ms)后,它才会运行并终止程序。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/time.h>

volatile pid_t childPID;

// This function should exist only in the parent! The child show not have it after a exec* acording to :
// The  exec()  family  of  functions  replaces  the current process image with a new process image.
void timeout_sigprof( int signum ){
    fprintf(stderr, "The alarm SIGPROF is here !\nThe actual pid: %d\n", getpid());
    //TODO: Write output and say the child terminated with a ram or time limit exceeded
    exit(105); // Note the 105 !
}

int main(int argc, char *argv[]) {
    int cstatus;
    pid_t cPID;

    char *args[2];
    args[0] = "/home/ddanailov/Projects/thesis/programs/prime/prime";
    args[1] = NULL; // Indicates the end of arguments.

    // Handle the SIGPROF signal in the function time_handler in both the child and
    struct sigaction sa;
    memset (&sa, 0, sizeof (sa));
    sa.sa_handler = &timeout_sigprof;
    sigaction (SIGPROF, &sa, NULL);

    childPID = fork();

    if (childPID == -1){
            printf( "Puff paff ... fork() did not work !\n" );
            exit(1);
    }

    // Child
    if(childPID == 0) {
        struct itimerval timer;
        timer.it_value.tv_sec = 0;
        timer.it_value.tv_usec = 250000;
        timer.it_interval.tv_sec = 0;
        timer.it_interval.tv_usec = 250000;
        setitimer ( ITIMER_PROF, &timer, NULL);

        // Redirect all I/O to/from a file
        int outFileId = open("output", O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
        // int inFileId = open("input");

        // Redirect the output for the CHILD program. Still don't know why it works.
        //dup2(inFileId, 0);
        dup2(outFileId, 1);
        //dup2(outFileId, 2);

        // No idea why these dup2's work ! As i close the file descriptors here ?!
        close(outFileId);
        close(inFileId);

        execvp(args[0], args);
        exit(1);
    }
    // Parent process
    else {
        // Waiting for the child
        int status;
        waitpid(childPID,&status,0);
        if (WIFEXITED(status)) {
            fprintf(stderr, "Nice nice, the child exited ... with cPID = %d with status = %d \n", cPID, WEXITSTATUS(status) );
        }
        exit(0);
    }

    return 0;
}

任何帮助/解释将不胜感激!
提前谢谢大家,
前任

最佳答案

问题1
为什么它的设置程序在
在父进程中设置?子进程的CPU/系统调用是
不是它收集的?
不,他们不是。与ITIME_PROF相关的计时器仅在具有计时器集的进程正在执行时,或当系统调用代表它执行时,而不是在子进程正在执行时递减。
这些信号通常由分析工具使用,该工具包含在链接到要分析的程序的库中。
但是:无论如何,您可能不需要将信号发送到父进程。如果您的目标是一旦程序超过了允许的使用,那么就让它接收SIGPROF并退出(如我在下面的Q2中所见)。然后,在waitpid返回并检测到由于SigPROF退出程序后,您可以找到调用“AA>或AA>”所用的实际时间量。
这样做的唯一缺点是,子程序可以通过在SIGPROF上设置自己的信号处理程序来破坏这个进程。
问题2
为什么这样做!?execvp是否覆盖了所有
函数(timeout\u sigprof、main和其他函数)?不能有人
可能捕获子程序中的信号并取代
原始功能?
不是,或者至少不是你想的那样。正如您所说,已安装在父进程中的信号处理程序将被execvp加载的新映像替换。
它似乎起作用的原因是,如果新程序没有为SIGPROF设置信号处理程序,那么当该信号发送到进程时,它将终止。回想一下,发送到进程的任何信号,如果该进程没有为其设置处理程序,或者特别决定忽略,都将导致进程终止。
如果execvp正在执行的程序没有为SIGPROF设置信号处理程序,那么它将不会被终止。
更新
看到你的评论后,我想我最好试试你的程序。我在waitpid之后向if语句添加了另一个分支,如下所示:

    waitpid(childPID,&status,0);
    if (WIFEXITED(status)) {
        fprintf(stderr, "Nice nice, the child exited ... with cPID = %d with status = %d \n", childPID, WEXITSTATUS(status) );
    } else if (WIFSIGNALED(status)) {
        fprintf(stderr, "Process pid=%d received signal %d\n",childPID,WTERMSIG(status));
    }

当我运行这个时,我看到以下内容:
$ ./watcher
Process pid=1045 received signal 27

这证实了我上面所说的。我看不到字符串“警报信号在这里!”打印出来的,我确实在父母身上看到一个信号,孩子是被信号27杀死的,这个信号是SIGPROF。
我只能想到一种情况,在这种情况下,您将看到信号处理程序执行,如果计时器设置得太低,以至于它在execv实际加载新映像之前触发。不过,这看起来并不完全可能。
另一种可能是您无意中在目标程序中安装了相同的信号处理程序(复制粘贴错误?).
问题3
为什么放在这里的dup2真的起作用了
要重定向的子项输入/输出?
根据代码中的注释,我假设您的意思是“为什么即使在dup2之后立即关闭了原始文件描述符,它仍然有效?”
dup2将旧FD复制到新FD中,因此在执行之后:
dup2(outFileId, 1);

有两个文件描述符引用同一个文件描述:一个包含在变量outFileId中,另一个包含在FD 1(即stdout)中。还要注意,原始stdout将被此操作关闭。
文件描述符类似于对底层文件描述数据结构的引用,该结构表示打开的文件。调用dup2之后,有两个文件描述符指向同一个文件描述。
times的手册页显示:
如果fd是最后一个引用底层open的文件描述符
文件描述(请参见打开(2)),与打开关联的资源
释放文件描述
所以它按原样工作:您仍然有一个FD open,FD 1(stdout),新的子进程可以对它进行写操作。

10-07 16:43
查看更多