Могу ли я очистить несколько дочерних процессов с одним и тем же родителем, используя wait (& status) и l oop in C? - PullRequest
0 голосов
/ 22 марта 2020

Я пишу программу, которая может читать команды linux из файла и запускать их параллельно, используя fork() и execvp(), и она отлично работает.

 while((current = GetNextCommand(current)) != NULL){
            char currentCommand[WIDTH - 1];
            current->active = true;
            strcpy(currentCommand, current->command);
            int j=0;
            int ctr=0;
            char newString[LENGTH][WIDTH];
            for(int i = 0; i <= strlen(currentCommand); i++){
                // if space or NULL found, assign NULL into newString[ctr]
                if(currentCommand[i]==' '|| currentCommand[i]=='\0')
                {
                    newString[ctr][j]='\0';
                    ctr++;  //for next word
                    j=0;    //for next word, init index to 0
                }
                else
                {
                    newString[ctr][j] = currentCommand[i];
                    j++;
                }
            }
            char *exe[ctr + 1];
            for(int i = 0; i < ctr; i++){
                exe[i] = strdup(newString[i]);
            }
            exe[ctr] = NULL;
            t = clock();
            clock_gettime(CLOCK_MONOTONIC, &start);
            current->starttime = start.tv_sec;
            current->PID = (pid = fork());
            if(pid < 0){
                fprintf(stderr, "fork Failed\n"); //output in stderr if fork fails and return
                exit(1);
            }
            else if(pid == 0){
                execvp(exe[0], exe);
                //fails
                exit(2);
            }
        }

Мой файл

sleep 3
ls -latr
sleep 1
pwd
sleep 1
wc /etc/passwd
sleep 10

В родительском процессе мне нужно получить время выполнения каждого дочернего процесса и повторно запустить команду, если время выполнения команды превышает 2 секунды. Если время выполнения команды больше 2, я буду продолжать ее до тех пор, пока пользователь не убьет процесс, используя kill -sig pid или pkill sleep. Я использую для l oop и установить соответствующие числа ожидания (и статус). В моем файле sleep 3 и sleep 10 будут больше 2 секунд. Когда процесс sleep 3 выполнит свою работу, он вернется к родителю, а sleep 10 все еще выполняется. Когда я сейчас использую pkill sleep, все будет хорошо, потому что sleep 10 продолжит работу в родительском процессе. Однако, когда они оба выходят из дочернего процесса, и я использую kill -sig pid, вся программа завершает работу. Таким образом, как я могу запустить их (sleep 3 и sleep 10) одновременно в этой ситуации?

for(int i = 0; i < nodeIndex - 1; i++){
    int status;
    int pid = wait(&status);
    clock_gettime(CLOCK_MONOTONIC, &finish);
    CommandNode* cNode;
    cNode = FindCommand(head->nextCommandPtr, pid);
    elapsed = finish.tv_sec - cNode->starttime;
    printf("%ld\n", elapsed);
    if(elapsed < 2){
        cNode->active = false;
    }
    else{
        char rerunCommand[WIDTH - 1];
        strcpy(rerunCommand, cNode->command);
        int j=0;
        int ctr=0;
        char newString[LENGTH][WIDTH];
        for(int i = 0; i <= strlen(rerunCommand); i++){
            // if space or NULL found, assign NULL into newString[ctr]
            if(rerunCommand[i]==' '|| rerunCommand[i]=='\0')
            {
                newString[ctr][j]='\0';
                ctr++;  //for next word
                j=0;    //for next word, init index to 0
            }
            else
            {
                newString[ctr][j] = rerunCommand[i];
                j++;
            }
        }
        char *exe[ctr + 1];
        for(int i = 0; i < ctr; i++){
            exe[i] = strdup(newString[i]);
        }
        exe[ctr] = NULL;
        while(elapsed > 2){
            int pid2;
            t = clock();
            clock_gettime(CLOCK_MONOTONIC, &start);
            cNode->starttime = start.tv_sec;
            cNode->PID = (pid2 = fork());
            if(pid2 < 0){
                fprintf(stderr, "fork Failed"); //output in stderr if fork fails and return
                exit(1);
            }
            else if(pid2 == 0){
                printf("What happened here.\n");
                execvp(exe[0], exe);
                exit(2);
            }
            else{
                wait(&status);
                clock_gettime(CLOCK_MONOTONIC, &finish);
                elapsed = finish.tv_sec - cNode->starttime;
                if(elapsed > 2) {
                    printf("What is this: %d %d\n", pid2, cNode->PID);
                }
            }
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 26 марта 2020

Да, действительно, это правильный способ убедиться, что все ваши дети закончили, прежде чем вы продолжите:

pid_t pid;
...
while((pid = wait(NULL)) >= 0)
    printf("child pid=%d ended.\n", pid); /* or whatever you want */

/* no more children active after this point, you don't need 
 * to check the value of errno, except if you allow this
 * process to be interrupted by a signal. */

Это старое наследие процессов zomb ie (например, название для ходячие мертвецы mov ie): процесс zomb ie - это процесс, у которого есть exit(2) ed и родительский элемент которого не имеет wait(2) ed. У него освобождены все ресурсы, кроме записи в таблице процессов (в которой хранятся код выхода и учетные записи). Таким образом, когда родитель выполняет системный вызов wait(2), ядро ​​может перемещаться по таблице своих дочерних процессов и проверять, должно ли оно вернуть ошибку (если список пуст), или есть какой-то дочерний элемент, который завершился и может вернуть свой код exit(2) родителю. Вы можете правильно использовать wait(2) (например, без ошибок), если вы ранее сделали fork(2), а wait(2) сигнализирует об ошибке, если вы не создали дочерних процессов.

Чтобы получить время выполнения из детей вам нужно использовать один из альтернативных системных вызовов для wait(2) (wait3(2) или wait4(2), как указано в справочной странице FreeBSD, на linux есть аналогичные системные вызовы)

Попробуйте это просто пример:

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

#define N                    7
#define PARENT_SLEEP        30
#define CHILD_MAXSLEEP      60

int main()
{
    int i;
    pid_t mypid = getpid();

    for(i = 0; i < N; i++) {

        pid_t chpid = fork();

        switch (chpid) {

        case -1: /* error */
            fprintf(stderr,
                "Parent [pid=%d]: fork: %s\n",
                mypid,
                strerror(errno));
            goto out; /* we want to break the loop */

        case 0: /* child */
            /* reinit mypid to reflect proper value */
            mypid = getpid();

            printf("Child [pid=%d]: start\n", mypid);

            /* we initialize random module after fork, so
             * all children don't get the same random
             * sequence. Probably all of them will take
             * the same value for the time, so we use also
             * the pid for the seed to be different for
             * each child. */
            srandom( mypid ^ time(NULL) );

            /* a random time between 1 and CHILD_MAXSLEEP */
            int myrandom = random() % CHILD_MAXSLEEP + 1;
            printf("Child [pid=%d]: wait for %d secs.\n",
                    mypid, myrandom);
            sleep( myrandom );

            printf("Child [pid=%d]: exiting with code=%d\n",
                mypid, myrandom);
            exit( myrandom );

        default: /* parent */
            printf("Parent[pid=%d]: I have started"
                    " child (pid = %d)\n", mypid, chpid);
            break;
        } /* switch */
    } /* for */

out:
    /* if you put a delay here, before doing wait()s,
     * and you execute a ps(1) command before the parent
     * begins to make waits, and some of the children have
     * already died, you'll see the zombie processes
     * (they are marked with a Z in status) */
    printf("Parent [pid=%d]: sleeping for %ds.\n",
            mypid, PARENT_SLEEP);
    sleep(PARENT_SLEEP);
    printf("Parent [pid=%d]: beginning to wait.\n",
            mypid);

    int status;
    pid_t child;

    while ((child = wait(&status)) >= 0) {
        printf("Parent[pid=%d]: "
                "detected exit(%d) from "
                "child(pid=%d)\n",
                mypid,
                WEXITSTATUS(status),
                child);
    }

    printf("Parent[pid=%d]: exiting\n", mypid);

    exit(0);

} /* main */

и один пример одного исполнения:

$ a.out
Parent[pid=81452]: I have started child (pid = 81453)
Parent[pid=81452]: I have started child (pid = 81454)
Parent[pid=81452]: I have started child (pid = 81455)
Parent[pid=81452]: I have started child (pid = 81456)
Child [pid=81453]: start
Child [pid=81453]: wait for 34 secs.
Child [pid=81455]: start
Child [pid=81455]: wait for 56 secs.
Child [pid=81456]: start
Child [pid=81456]: wait for 42 secs.
Parent[pid=81452]: I have started child (pid = 81457)
Parent[pid=81452]: I have started child (pid = 81458)
Parent[pid=81452]: I have started child (pid = 81459)
Parent [pid=81452]: sleeping for 30s.
Child [pid=81454]: start
Child [pid=81454]: wait for 9 secs.
Child [pid=81457]: start
Child [pid=81457]: wait for 58 secs.
Child [pid=81458]: start
Child [pid=81458]: wait for 30 secs.
Child [pid=81459]: start
Child [pid=81459]: wait for 14 secs.
Child [pid=81454]: exiting with code=9
Child [pid=81459]: exiting with code=14
Child [pid=81458]: exiting with code=30
Parent [pid=81452]: beginning to wait.   <<<<< before this message, you can see Zombie processes.
Parent[pid=81452]: detected exit(14) from child(pid=81459)
Parent[pid=81452]: detected exit(30) from child(pid=81458)
Parent[pid=81452]: detected exit(9) from child(pid=81454)
Child [pid=81453]: exiting with code=34
Parent[pid=81452]: detected exit(34) from child(pid=81453)
Child [pid=81456]: exiting with code=42
Parent[pid=81452]: detected exit(42) from child(pid=81456)
Child [pid=81455]: exiting with code=56
Parent[pid=81452]: detected exit(56) from child(pid=81455)
Child [pid=81457]: exiting with code=58
Parent[pid=81452]: detected exit(58) from child(pid=81457)
Parent[pid=81452]: exiting
$ _
0 голосов
/ 22 марта 2020

относительно вопроса в заголовке:

Да, вы можете, просто l oop, пока возвращаемое значение не будет равно -1

...