如何从此递归函数中获取fork()调用的子进程总数?

时间:2018-10-23 18:25:59

标签: c recursion fork

我一直在用这个递归函数来获取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 :(得分:1)

我认为您只能使用外部实体来实现这一点,因为分叉的流程没有什么共同点(父母除外)。
让我们使用一个文件。我们可以使用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和一些进程间通信,一些shmgetsemop以及例如排队的sempahore计数来创建解决方案。

答案 1 :(得分:0)

您可以尝试一下, 这确实很接近您所做的事情,但是我只计算出我产生的孩子数量,然后等待他们。

#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]);
}

答案 2 :(得分:0)

您在每个进程中产生了许多子目录,这些子目录对应于子目录的数目,但您不必等待所有子目录。

您只等待最后一个孩子。 您需要将所有PID都存储在数组中,然后在循环中等待它们。然后对所有WEXITSTATUS进行求和,然后返回该值(在为您自己添加一个值之后)。