【问题标题】:Calling "fork() execvp() _exit()" , and the process is not exiting调用 "fork() execvp() _exit()" ,进程没有退出
【发布时间】:2014-07-09 12:53:51
【问题描述】:

所以简短的摘要是-我有一个程序可以递归地搜索具有特定扩展名的文件。每次找到一个,它都会制作一个副本,对副本进行一些更改,创建一个补丁(通过使用 diff 和 execvp())并删除原始文件。

我遇到的问题是,在数百个文件之后,fork() 返回“资源暂时不可用”。我添加了一个计数器来查看发生此故障时仍有多少进程在运行,并且看起来没有一个已关闭 - 打开的进程数始终与处理的文件数相同。

现在,我的印象是流程应该是这样的—— fork();//创建子进程 dostuff();//在子进程中 _exit(1);//将控制权交还给父级

但事情似乎没有那么简单。也许这里有人可能会发现我在代码中遗漏了一些明显的东西。

我已经发布了负责分叉和修补的“清理”功能 - 其余部分被拆分为多个文件,所以希望这已经足够了。

(参数“name”是原始文件名,“newname”是修改后的副本。)

void cleanup (char * name, char * newname)
{
    if (pf)
    {
            pid_t patch_pid;

            char * const diffargs[5] = {thisdiff, "-u", newname, name, NULL};

            char * patchname = malloc(strlen(name) + 6);
            strcpy(patchname, name);
            strcat(patchname, ".patch");

            if((patch_pid = fork()) < 0 )
            {
                    printf("fork failed.\n%s\nfilecount: %ld\nopen forks: %d\n", strerror(errno), filecount, pcount);
                    exit(-1);
            }

            pcount++;

            if (patch_pid == 0)
            {
                    FILE *pfp;
                    if ((pfp = fopen(patchname, "w")) == NULL)
                    {
                            printf("Error opening file \"%s\" for writing.\n%s\n", patchname, strerror(errno));
                            exit(-1);
                    }

                    dup2(fileno(pfp), STDOUT_FILENO);
                    fclose(pfp);
                    execvp(diffargs[0], diffargs);
                    free(patchname);

                    if (remove(name) != 0)
                    {
                            printf("Error removing file %s\n%s\n", name, strerror(errno));
                            exit(-1);
                    }

                    if (rename(newname, name) != 0)
                    {
                            printf("Error renaming file %s\n%s\n", newname, strerror(errno));
                            exit(-1);
                    }
                    pcount--;
                    _exit(1);
            }
    }

    else if (!df && !xf)
    {
            if (remove(name) != 0)
            {
                    printf("Error removing file %s\n%s\n", name, strerror(errno));
                    exit(-1);
            }
            if (rename(newname, name) != 0)
            {
                    printf("Error renaming file %s\n%s\n", newname, strerror(errno));
                    exit(-1);
            }
    }
}

【问题讨论】:

  • 您知道如果exec 系列函数成功,它们不会返回吗?这意味着您在execvp 调用之后拥有的任何代码都不会运行,除非调用失败。
  • 您是否使用wait() 在某处获得已退出进程的退出状态?如果没有,你会创建一堆僵尸来填满进程表。
  • 除非出现错误,否则它们不会返回任何值......但它们最终肯定会完成并继续执行吗?
  • 詹斯:不,我没有。我必须承认,我不知道 wait() 的合适位置在哪里。我猜在 cleanup() 例程的最后?
  • exec 系列函数所做的是将当前进程替换为加载程序中的进程。当您调用它时,您当前的代码将不再存在于该进程中。

标签: c fork execvp


【解决方案1】:

两个建议:

  1. 了解 exec*() 替换您的进程(如果成功)。后面的任何代码都无法访问。
  2. 使用 wait*() 函数之一获取已退出进程的退出状态;可能在 SIGCHLD 的信号处理程序中。
  3. 奖励建议:阅读 W. Richard Stevens 的 Unix 环境中的高级编程;这是执行此类任务的圣经。

【讨论】:

  • 后面的任何代码都无法访问 -- 除非 exec* 失败 ;)
【解决方案2】:

您可能还希望在fork 之后使用else 子句来处理父进程。像下面这样的东西可能会起作用;

void cleanup (char * name, char * newname)
{
    if (pf)
    {
        /* SNIP - unchanged */

        if (patch_pid == 0) /* child */
        {
            FILE *pfp;
            if ((pfp = fopen(patchname, "w")) == NULL)
            {
                printf("Error opening file \"%s\" for writing.\n%s\n",
                       patchname, strerror(errno));
                exit(EXIT_FAILURE);
            }
            dup2(fileno(pfp), STDOUT_FILENO);
            fclose(pfp);
            execvp(diffargs[0], diffargs);
            perror("execvp");
            exit(EXIT_FAILURE);
        }
        else /* parent */
        {
            pid_t rc;
            int stat;

            free(patchname);

            rc = waitpid(patch_pid, &stat, 0);
            if (rc < 0)
            {
                perror("waitpid");
                /* do something appropriate here */
            }
            else
            {
                /* if you care about your children */
                if (WIFEXITED(stat))
                {
                    printf("%d exited with status %d\n",
                           (int)rc, WEXITSTATUS(stat));
                }
                else if (WIFSIGNALED(stat))
                {
                    printf("%d terminated because of signal %d\n",
                           (int)rc, WTERMSIG(stat));
                }
                else if (WIFSTOPPED(stat))
                {
                    printf("%d was STOPPED with signal %d\n",
                           (int)rc, WSTOPSIG(stat));
                }
            }
            pcount--;

            if (remove(name) != 0)
            {
                printf("Error removing file %s\n%s\n", name, strerror(errno));
                exit(EXIT_FAILURE);
            }

            if (rename(newname, name) != 0)
            {
                printf("Error renaming file %s\n%s\n", newname, strerror(errno));
                exit(EXIT_FAILURE);
            }
        }
    }
}

我还强烈推荐阅读已故 W. Richard Stevens 的UNIX 环境中的高级编程。实际上,您也应该将 UNIX 网络编程第 1 卷和第 2 卷 添加到您的列表中;)

我注意到的另一件事是您有一个与patchname 相关的内存管理错误。 malloc 调用不考虑字符串末尾的额外 NUL 字符。

【讨论】:

猜你喜欢
  • 2014-12-12
  • 2014-08-16
  • 2011-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-28
相关资源
最近更新 更多