Как я могу получить общее количество дочерних процессов, вызванных fork () из этой рекурсивной функции? - PullRequest
0 голосов
/ 23 октября 2018

Я возился с этой рекурсивной функцией, пытаясь получить общее количество дочерних процессов, созданных fork().Хотя я не могу понять это правильно.Когда я попытался использовать WEXITSTATUS(), вывод программы стал очень неустойчивым.Есть ли способ суммировать общее количество дочерних процессов, порожденных в этой функции?Является ли конвейер единственным способом сделать это, или есть более простой способ?

Он пропускается через ".", Чтобы отключить функцию в текущем рабочем каталоге.Он пересекает этот каталог и все его подкаталоги, разветвляясь при каждом обнаружении подкаталога.verifyFileType() просто проверяет, является ли найденный файл CSV.

** Отредактированная функция для большей ясности

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>

int traverse(char* directory)
{
    struct dirent *currentDir;  
    DIR *traverser = opendir(directory);

    if (traverser == NULL)
    {
        printf("Error: Could not open directory.\n");
        return 0;
    }

    while ((currentDir = readdir(traverser)) != NULL)
    {       
        if (currentDir->d_type == DT_DIR && strcmp(currentDir->d_name, ".") != 0 && strcmp(currentDir->d_name, "..") != 0)
        {
            PID = fork();

            if (PID == 0)
            {       
                char pathBuffer[1024];
                snprintf(pathBuffer, sizeof(pathBuffer), "%s/%s", directory, currentDir->d_name);

                traverse(pathBuffer);
                //int childProc = traverse(pathBuffer);
                //return childProc + 1;
                exit(0);
            }
            else
            {
                //do parent stuff?
            }
        }
        else if (strcmp(currentDir->d_name, ".") != 0 && strcmp(currentDir->d_name, "..") != 0)
        {
            if (verifyFileType(currentDir->d_name) == 0)
            {
                //if directory = '.', only send file name as fopen() arg
                printf("%s%s\n", directory, currentDir->d_name);
            }
        }
    }

    if (PID > 0)
    {
        int status = 0;
        wait(&status);
        //int returned = WEXITSTATUS(status);
        //return returned + 1;
    }
    else if (PID == -1)
    {
        printf("Error waiting on children.  Aborting.\n");
        _exit(0);
    }

    closedir(traverser);
    return 0;
}

int main (int argc, char **argv)
{
    char* beginningDir = ".";
    rootPID = getpid();

    /*
    int procCount = 0;
    procCount = traverse(beginningDir);
    printf("Proc Count: %d\n", procCount);
    */

    traverse(beginningDir);
    return 0;
}

Ответы [ 3 ]

0 голосов
/ 23 октября 2018

Вы порождаете количество дочерних элементов в каждом процессе, соответствующее количеству подкаталогов, но вы не ждете всех из них.

Вы ждете только последнего ребенка.Вам нужно хранить все PID в массиве и ждать их в цикле.Затем сложите все WEXITSTATUS с этого момента и верните это значение (после добавления одного для себя).

0 голосов
/ 24 октября 2018

Я думаю, что вы можете достичь этого только с помощью внешней сущности, поскольку разветвленные процессы не имеют ничего общего (кроме родителей).
Давайте использовать файл.Мы можем получить файл, используя tmpfile().Нам понадобится некоторая блокировка, используя flock(fileno(FILE *), ...).Каждый ребенок записывает один байт во временный файл.После того, как все дочерние элементы запущены, я могу получить размер файла - таким образом, я получу количество дочерних элементов:

#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdbool.h>
#include <sys/file.h>
#include <sys/types.h>
#include <unistd.h>

typedef FILE cnt_t;

/**
 * Create interprocess counter.
 * Should be created once (and only once) by the parent process.
 */
cnt_t *cnt_new(void)
{
    return tmpfile();
}

/**
 * Remove interprocess counter.
 * Should be called by all childs and parent
 */
void cnt_delete(cnt_t *t)
{
    fclose(t);
}

void _cnt_lock(cnt_t *t)
{
    for (int ret; (ret = flock(fileno(t), LOCK_EX)) != 0;) {
        assert(ret == EWOULDBLOCK);
    }
}

void _cnt_unlock(cnt_t *t)
{
    if (flock(fileno(t), LOCK_UN) != 0) {
        assert(0);
    }
}

/**
 * Increments counter by 1.
 */
void cnt_inc(cnt_t *t) {
    assert(t != NULL);
    _cnt_lock(t);

    if (fwrite((char[1]){'X'}, sizeof(char), 1, t) < 0) {
        assert(0);
    }

    if (fflush(t) != 0) {
        assert(0);
    }

    _cnt_unlock(t);
}

void cnt_println(cnt_t *t)
{
    _cnt_lock(t);

    if (fseek(t, 0L, SEEK_SET) < 0) {
        assert(0);
    }

    char buf[124];
    size_t cnt = fread(buf, sizeof(char), 124, t);
    printf("cnt(%p) = %ld  '%.*s'\n", cnt, (void*)t, cnt, buf);

    _cnt_unlock(t);
}

/**
 * Get's counter value.
 */
long cnt_getCount(cnt_t *t)
{
    assert(t != NULL);

    _cnt_lock(t);

    if (fseek(t, 0L, SEEK_END) < 0) {
        assert(0);
    }

    const long sz = ftell(t);
    if (sz < 0) {
        assert(0);
    }

    _cnt_unlock(t);

    return sz;
}

/* ----------------------------------------------------------- */

int main()
{
    srand(0);

    cnt_t *cntobj = cnt_new();

    bool child = false;
    for (int i = 0; i < 5; ++i) {
        const int ret = fork();
        switch (ret) {
        case 0:
            cnt_inc(cntobj);
            child = true;
            break;
        case -1:
            fprintf(stderr, "fork error!\n");
            exit(-1);
            break;
        default:
            fprintf(stderr, "%d -> %d\n", getpid(), ret);
            break;
        }
    }

    while (wait(NULL) != -1) continue;

    if (child) {
        cnt_delete(cntobj);
        exit(0);
    }

    const long cnt = cnt_getCount(cntobj);
    cnt_delete(cntobj);

    fprintf(stderr, "childs %ld\n", cnt);

    return 0;
}

Возможно, я слишком много занимался запутыванием (typedef FILE cnt_t выглядит странно), нокод работает и возвращает правильное число 31. Живая версия доступна на jdoodle .

А вот решение, использующее каналы:

#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdbool.h>
#include <sys/file.h>
#include <sys/types.h>
#include <unistd.h>

typedef struct {
    int p[2];
} cnt_t;

/**
 * Create interprocess counter.
 * Should be created once (and only once) by the parent process.
 */
cnt_t *cnt_new(void)
{
    cnt_t *t = malloc(sizeof(*t));
    assert(t != NULL);
    if (pipe(t->p) < 0) {
        assert(0);
    }
    if (fcntl(t->p[0], F_SETFL, O_NONBLOCK) < 0) {
        assert(0);
    }
    return t;
}

/**
 * Remove interprocess counter.
 * Should be called by all childs and parent
 */
void cnt_delete(cnt_t *t)
{
    close(t->p[0]);
    close(t->p[1]);
    t->p[0] = 0;
    t->p[1] = 0;
    free(t);
}

/**
 * Increments counter by 1.
 */
void cnt_inc(cnt_t *t) 
{
    assert(t != NULL);
    if (write(t->p[1], (char[1]){'X'}, 1 * sizeof(char)) < 0) {
        assert(0);
    }
}

/**
 * Get's counter value.
 */
long cnt_getCount(cnt_t *t)
{
    assert(t != NULL);

    char c;
    long cnt = 0;
    ssize_t tmp;
    errno = 0;
    while ((tmp = read(t->p[0], &c, 1)) == 1) {
        ++cnt;
    }
    if (tmp < 0 && errno != EWOULDBLOCK) {
        assert(0);
    }

    const long ret = cnt;

    while (cnt--) {
        if (write(t->p[1], (char[1]){'X'}, 1) < 0) {
            assert(0);
        }
    }

    return ret;
}

/* ----------------------------------------------------------- */

int main()
{
    srand(0);

    cnt_t *cntobj = cnt_new();

    bool child = false;
    for (int i = 0; i < 5; ++i) {
        const int ret = fork();
        switch (ret) {
        case 0:
            cnt_inc(cntobj);
            child = true;
            break;
        case -1:
            fprintf(stderr, "fork error!\n");
            exit(-1);
            break;
        default:
            fprintf(stderr, "%d -> %d\n", getpid(), ret);
            break;
        }
    }

    while (wait(NULL) != -1) continue;

    if (child) {
        cnt_delete(cntobj);
        exit(0);
    }

    const long cnt = cnt_getCount(cntobj);
    cnt_delete(cntobj);

    fprintf(stderr, "childs %ld\n", cnt);

    return 0;
}

Работает хорошо и, вероятно,это намного быстрее.Живая версия по-прежнему jdoodle .

В этих примерах используется глупая обработка ошибок с использованием assert, и они служат только для демонстрации метода и его работы.

Вероятно, мы могли бы также создать решение с использованием posix shemaphores и некоторого межпроцессного взаимодействия, некоторых shmget и semop и, например, подсчета количества семафоров в очереди.

0 голосов
/ 23 октября 2018

Вы можете попробовать это, это действительно близко к тому, что вы делали, но я просто считаю количество рожденных детей и затем жду их.

#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>

int traverse(char* directory) {
    struct dirent *currentDir;
    DIR *traverser = opendir(directory);

    if (traverser == NULL) {
        printf("Error: Could not open directory.\n");
        return 0;
    }

    size_t nb_child = 0;
    while ((currentDir = readdir(traverser)) != NULL)
    {
        if (strcmp(currentDir->d_name, ".") == 0
            || strcmp(currentDir->d_name, "..") == 0)
            continue; // ignore . and ..

        // if subdirectory => fork to explore
        if (currentDir->d_type == DT_DIR) {
            pid_t PID = fork();

            if (PID == 0) {
                char pathBuffer[1024];
                snprintf(pathBuffer, sizeof(pathBuffer), "%s/%s",
                         directory, currentDir->d_name);

                return traverse(pathBuffer);
            } else {
                nb_child++; // keep track of the nomber of children
            }
        } else { // non directory "file"
                // Do you verify here
                printf("%s%s\n", directory, currentDir->d_name);
        }
    }

    // loop until we waited for all children
    for (size_t i = 0; i < nb_child; i++)
        wait(NULL);

    closedir(traverser);
    return 0;
}

int main() {
    return traverse(argv[1]);
}
...