使用fork()

时间:2016-03-15 19:38:49

标签: c unix fork posix

我尝试使用fork()进入文件夹并读取文件。我使用file tree walk函数递归地进入文件夹。基本思想是在目录中会有子项数量的文件和目录。孩子们分别和同时阅读每个文件。但是,如果有目录,孩子们将成为孩子的父母来阅读文件。

static int soner_each_time(const char *filepath, const struct stat *info,
                    int typeflag, struct FTW *ftwinfo)
{

    pid_t   pid = 0;
    char    buf[BUFSIZE];
    int     status;
    int     i = 0;

    /* The variables are related to functions of reading file  */

    int     totalLines;
    char    arr[TOTALNUMBEROFLINES][BUFSIZE];
    int     retval;


    const char *const filename = filepath + ftwinfo->base;

    if (( pid = fork()) < 0) {
        const int cause = errno;
        fprintf(stderr, "Fork error: %s\n", strerror(cause));
        errno = cause;
        return -1;
    }
    else if( pid > 0 ) // parent
    {
        if (typeflag == FTW_DP || typeflag == FTW_D)
        {
            sprintf(buf, "%*s%s\n\n", ftwinfo->level * 4, "", filepath);
            write(1, buf, strlen(buf));
            pid = wait(&status);
            if (pid == -1)
                perror("Failed to wait for child");
            else if (WIFEXITED(status) && !WEXITSTATUS(status))
                printf("parent [%d] reaped child [%d]\n", getpid(), pid);
            else if (WIFEXITED(status))
                printf("Child %ld terminated with return status %d\n",
                       (long)pid, WEXITSTATUS(status));
            else if (WIFSIGNALED(status))
                printf("Child %ld terminated due to uncaught signal %d\n",
                       (long)pid, WTERMSIG(status));
            else if (WIFSTOPPED(status))
                printf("Child %ld stopped due to signal %d\n",
                       (long)pid, WSTOPSIG(status));
        }
    }


    if (pid == 0) // child
    {
        if (typeflag == FTW_F)
        {
            sprintf(buf, "||| Child [%d] of parent [%d]: %s |||\n", getpid(), getppid(), filename);
            write(1, buf, strlen(buf));

            /* Both of them are about reading function */
            totalLines = storeLinesInArray(filename, arr);

            retval = for_each_file(filename, totalLines, key, arr);

            sprintf(buf, "||| Child [%d] of parent [%d] is about to exit |||\n", getpid(), getppid());
            write(1, buf, strlen(buf));
        }

        else if (typeflag == FTW_DP || typeflag == FTW_D)
        {
            sprintf(buf, "%*s%s\n\n", ftwinfo->level * 4, "", filepath);
            write(1, buf, strlen(buf));
        }

    }
        return 0;
}

FTW_DPFTW_D表示文件夹FTW_F表示文件。基本上,我每次都尝试使用代码fork()。如果是父级,则等待其子级读取文件。因为函数是递归的,所以它会分叉每个调用。但是,有一些关于它我无法让它为一个文件分叉多个。例如,1a.txt应该有一个孩子,但对于这个方案,它是8.分叉主题真的很难。我每天都做练习并试着去理解它。您的解释和帮助将提高我在该分支的技能。

@edit:mcve code

#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 700
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <locale.h>
#include <string.h>
#include <ftw.h>
#include <stdio.h>

#define TOTALNUMBEROFLINES 1000
#define BUFSIZE 1000


void err_sys(const char *const str)
{
    perror(str);
    fflush(stdout);
    exit(1);
}

int storeLinesInArray(const char *file, char lines[][BUFSIZE])
{
    return 0;
}

static int for_each_file(const char *filepath, int totalLines, const char *key, const char arr[][BUFSIZE])
{

    fprintf(stdout, "File name is = %s\n", filepath);
    fflush(stdout);


    return 0;
}

static int soner_each_time(const char *filepath, const struct stat *info,
                           int typeflag, struct FTW *ftwinfo)
{

    pid_t   pid = 0;
    char    buf[BUFSIZE];
    int     status;

    /* The variables are related to functions of reading file  */

    int     totalLines;
    char    arr[TOTALNUMBEROFLINES][BUFSIZE];
    int     retval;


    const char *const filename = filepath + ftwinfo->base;

    if (( pid = fork()) < 0) {
        perror("failed fork");
        exit(-1);
    }
    else if( pid > 0 ) // parent
    {
        if (typeflag == FTW_DP || typeflag == FTW_D)
        {
            sprintf(buf, "%*s%s\n\n", ftwinfo->level * 4, "", filepath);
            write(1, buf, strlen(buf));
            pid = wait(&status);
            if (pid == -1)
                perror("Failed to wait for child");
            else if (WIFEXITED(status) && !WEXITSTATUS(status))
                printf("parent [%d] reaped child [%d]\n", getpid(), pid);
            else if (WIFEXITED(status))
                printf("Child %ld terminated with return status %d\n",
                       (long)pid, WEXITSTATUS(status));
            else if (WIFSIGNALED(status))
                printf("Child %ld terminated due to uncaught signal %d\n",
                       (long)pid, WTERMSIG(status));
            else if (WIFSTOPPED(status))
                printf("Child %ld stopped due to signal %d\n",
                       (long)pid, WSTOPSIG(status));
        }
    }


    if (pid == 0) // child
    {
        if (typeflag == FTW_F)
        {
            sprintf(buf, "||| Child [%d] of parent [%d]: %s |||\n", getpid(), getppid(), filename);
            write(1, buf, strlen(buf));

            /* Both of them are about reading function */
            totalLines = storeLinesInArray(filename, arr);

            retval = for_each_file(filename, totalLines, "not needed now", arr);

            sprintf(buf, "||| Child [%d] of parent [%d] is about to exit |||\n", getpid(), getppid());
            write(1, buf, strlen(buf));
        }

        else if (typeflag == FTW_DP || typeflag == FTW_D)
        {
            sprintf(buf, "%*s%s\n\n", ftwinfo->level * 4, "", filepath);
            write(1, buf, strlen(buf));
        }

    }
    return 0;
}



int main(int argc, char *argv[])
{

    if (nftw("here is directory path", soner_each_time, 15, FTW_CHDIR)) {
        fprintf(stderr, "Failed directory.\n");
        exit(-1);
    }

    return 0;
}

1 个答案:

答案 0 :(得分:1)

你有一些错误。更正后的代码如下。

孩子进行exit来电,因此会继续使用自己的nftw,因此会对多个文件进行冗余处理。我添加了exit(0)

fork正在快速完成,系统将耗尽免费pid

我已经添加了三件事来解决这个问题:

  1. A&#34;收获&#34;在waitpid(0,&status,WNOHANG)上循环以捕捉已完成的孩子的例程
  2. fork周围添加了一个循环来捕捉&#34;插槽#34;问题
  3. 添加了限制机制,将活动子项数限制为合理/有用值
  4. 我已经注明了消息来源,指出这些地方是错误的。

    虽然没有硬错误,但为每个文件执行fork会增加大量开销。磁盘带宽将使大约四个活动子线程饱和,因此使用更多只会降低速度。为这个目录分配一个孩子并没有做太多事情,因为&#34;多肉的&#34;处理将用于文件。

    无论如何,这里有更正的代码[请原谅无偿的风格清理]:

    #define _POSIX_C_SOURCE 200809L
    #define _XOPEN_SOURCE 700
    #include <unistd.h>
    #include <dirent.h>
    #include <stdlib.h>
    #include <locale.h>
    #include <string.h>
    #include <errno.h>
    #include <ftw.h>
    #include <stdio.h>
    #include <sys/wait.h>
    
    #define TOTALNUMBEROFLINES 1000
    #define BUFSIZE 1000
    
    // output verbose/debug messages
    int opt_v;
    
    // limit of number of children that can be used at one time (if non-zero)
    int opt_T;
    
    int pendcnt;                            // number of active children
    
    void
    err_sys(const char *const str)
    {
        perror(str);
        fflush(stdout);
        exit(1);
    }
    
    int
    storeLinesInArray(const char *file, char lines[][BUFSIZE])
    {
        return 0;
    }
    
    static int
    for_each_file(const char *filepath, int totalLines, const char *key, const char arr[][BUFSIZE])
    {
    
        fprintf(stdout, "File name is = %s\n", filepath);
        fflush(stdout);
    
        return 0;
    }
    
    // reap_some -- reap a few processes
    int
    reap_some(int final)
    {
        pid_t pid;
        int status;
        int reapcnt;
    
        reapcnt = 0;
    
        // reap all completed children
        while (1) {
            pid = waitpid(0,&status,WNOHANG);
            if (pid == 0)
                break;
    
            if (pid == -1) {
                if (errno != ECHILD)
                    perror("Failed to wait for child");
                break;
            }
    
            if (WIFSIGNALED(status)) {
                printf("Child %ld terminated due to uncaught signal %d\n",
                    (long) pid, WTERMSIG(status));
                ++reapcnt;
                continue;
            }
    
            if (WIFSTOPPED(status)) {
                printf("Child %ld stopped due to signal %d\n",
                    (long) pid, WSTOPSIG(status));
                continue;
            }
    
            if (WIFEXITED(status)) {
                ++reapcnt;
                if (WEXITSTATUS(status) == 0) {
                    if (opt_v)
                        printf("parent [%d] reaped child [%d]\n", getpid(), pid);
                }
                else
                    printf("Child %ld terminated with return status %d\n",
                        (long) pid, WEXITSTATUS(status));
                continue;
            }
        }
    
        // bump down the number of children that are "in-flight"
        pendcnt -= reapcnt;
    
        return reapcnt;
    }
    
    static int
    soner_each_time(const char *filepath, const struct stat *info, int typeflag, struct FTW *ftwinfo)
    {
        pid_t pid = 0;
        char *bp;
        int lvl;
        char buf[BUFSIZE];
    
        /* The variables are related to functions of reading file */
    
        int totalLines;
        char arr[TOTALNUMBEROFLINES][BUFSIZE];
        int retval;
    
        const char *const filename = filepath + ftwinfo->base;
    
        switch (typeflag) {
        case FTW_DP:
        case FTW_D:
            bp = buf;
            for (lvl = 0;  lvl < ftwinfo->level;  ++lvl)
                bp += sprintf(bp,"    ");
            bp += sprintf(bp, "%s\n\n",filepath);
            write(1, buf, strlen(buf));
            //reap_some(0);
            break;
    
        case FTW_F:
            // BUGFIX:
            // limit the number of in-flight children
            // too many children serves no purpose -- they saturate the system
            // resources and performance actually goes _down_ because the system
            // spends more time doing context switches between them than the actual
            // work. more than a few children to process files produces little
            // benefit after the disk I/O is running at maximum
            if (opt_T) {
                while (pendcnt > opt_T)
                    reap_some(0);
            }
    
            // BUGFIX:
            // without a throttle, we spawn children so fast we're going to get
            // [many] failures here (i.e. we use up _all_ available pids)
            while (1) {
                pid = fork();
                if (pid >= 0)
                    break;
                reap_some(0);
            }
    
            // parent
            // keep track of the child count
            if (pid > 0) {
                ++pendcnt;
                break;
            }
    
            // child
            sprintf(buf, "||| Child [%d] of parent [%d]: %s |||\n",
                getpid(), getppid(), filename);
            if (opt_v)
                write(1, buf, strlen(buf));
    
            /* Both of them are about reading function */
            totalLines = storeLinesInArray(filename, arr);
    
            retval = for_each_file(filename, totalLines, "not needed now", arr);
    
            sprintf(buf, "||| Child [%d] of parent [%d] is about to exit (RETVAL: %d) |||\n", getpid(), getppid(), retval);
            if (opt_v)
                write(1, buf, strlen(buf));
    
            // BUGFIX:
            // child won't exit without this -- causing multiple children to redo
            // the same files (i.e. they would continue the nftw -- only parent
            // should do that)
            exit(0);
            break;
        }
    
        return 0;
    }
    
    int
    main(int argc, char **argv)
    {
        char *cp;
    
        --argc;
        ++argv;
    
        opt_T = 10;
    
        for (;  argc > 0;  --argc, ++argv) {
            cp = *argv;
            if (*cp != '-')
                break;
    
            switch (cp[1]) {
            case 'T':  // throttle
                cp += 2;
                opt_T = (*cp != 0) ? atoi(cp) : 0;
                break;
            case 'v':  // verbose messages
                opt_v = 1;
                break;
            }
        }
    
        cp = *argv;
        printf("opt_T=%d opt_v=%d -- %s\n",opt_T,opt_v,cp);
        sleep(3);
        printf("away we go ...\n");
    
        if (nftw(cp, soner_each_time, 15, FTW_CHDIR)) {
            fprintf(stderr, "Failed directory.\n");
            exit(1);
        }
    
        // wait for all children to complete
        while (pendcnt > 0)
            reap_some(1);
    
        return 0;
    }
    

    <强>更新

    更改了代码以仅在父代中执行目录处理(即,仅针对文件分叉子代)。修正了一个错误。所以,现在,-T throttle参数的工作价值要低得多,可以相当于&#34;工人数量#34;。更改了程序以使用默认的油门值。

    更新#2:

      

    我说父母是因为父母只有一个。我想知道我是否可能追错了。

    不,你是对的。只有一个父级。那是设计的。

      

    我想为每个目录制作父级,如第一个方案中所述。

    实际上,你并没有正确理解真正涉及的内容。 Obi Wan Kenobi:&#34;这些不是您正在寻找的机器人&#34;

    在每个目录上执行递归分叉时,存在许多技术,性能和系统饱和问题。我编写的示例避免了所有这些,在设计和性能方面做出了最佳折衷。它还允许主人提前跑步#34;无论给定目录中的文件/子目录的数量如何,孩子们都要尽可能地让孩子们保持忙碌。

    旁注:我有40多年的工作经验,并且我已经编写了多个nftw等效课程。所以,以下内容来自所有这些。

    期望的最终结果是什么?

    您只有骨架代码,但实际上[打算做的]会影响架构。您的最终计划可能是:

    1. CPU绑定[不断等待CPU操作,如乘法等]
    2. 内存限制[经常等待读取或写入DRAM完成]
    3. I / O绑定[不断等待I / O操作完成]
    4. 另外,您想要预订或后期订单[如FTW_DEPTH]遍历吗?我推测预订

      您无法再使用nftw

      您需要使用opendir/readdir/closedir [这是nftw所做的]来做同等效。

      您需要的是在层次结构中执行单个级别的过程。让nftw中止并启动一个新的来实现这一目标是一种折磨。

      下面是一些伪代码。

      但是...... 实施变得更加复杂,无法提供更好的性能,实际上可能会降低性能。它还可能导致无关的程序崩溃,例如Firefox,vlc,窗口管理器等。

      您现在需要进程间通信和共享内存

      通过上面的示例,只有一个控件进程。为了保持限制,只需要pendcnt的简单增量/减量。

      当您为目录添加递归分叉时,现在任何子进程分叉为目录必须递增/递减{em>全局副本{{ 1}}在共享内存中。它必须使用进程间信号量来控制对该变量的访问。或者,也许是一些原子增量/减量原语[ala C11 atomics]。

      现在,对该信号量的争用成为[延迟]因素。

      <强>性能:

      实际上有多个活动进程会降低性能。换句话说,分配目录实际上将比单个进程运行更慢

      超过一些&#34;工人&#34;对文件执行某些操作的进程,磁盘I / O带宽将用完。通过添加更多流程,您将获得 no 的进一步利益。

      通过许多流程,它们实际上可能会相互干扰。考虑进程A请求磁盘读取。但是,进程B也是如此。内核中的读取完成,但在它返回到A之前,A读取的内核缓冲区必须重新用于完成B的读取。必须重复阅读。

      这就是所谓的[虚拟记忆]页面&#34; thrashing&#34;。

      锁定并崩溃系统

      随着越来越多的磁盘I / O完成,必须使用越来越多的内核缓冲区来包含数据。内核可能不得不驱逐页面缓冲区以腾出空间。其中一些可能是针对上面提到的无关程序。

      换句话说,您的程序的许多进程可能会独占CPU,磁盘和内存使用情况。像Firefox这样的程序会超时(并且崩溃),因为他们会看到他们不会看到的长时间延迟并假设他们内部的某些东西导致了延迟。

      我运行了这样一个pendcnt程序,看到Firefox说:&#34; Killing锁定了javascript脚本&#34;。

      更糟糕的是,我已经让vlc落后于计时并开始跳帧。这导致窗口管理器感到困惑,因为它认为这是由于某些逻辑错误而不仅仅是非常慢响应系统。最终结果是窗口管理器中止并且必须手动重新启动。

      这也会减慢更多关键程序和内核守护进程的速度。

      在某些情况下,只能通过系统重启来清理。

      此外,在与他人共享的系统上运行多个进程会让你变成一个“坏公民”#34;所以要小心消耗太多资源。

      无论如何,这里是伪代码:

      nftw