【问题标题】:Functionality of fork()fork() 的功能
【发布时间】:2010-12-16 10:25:46
【问题描述】:

系统信息:我在 2 个月大的笔记本电脑上运行 64 位 Ubuntu 10.10。

大家好,我有一个关于 C 中的 fork() 函数的问题。从我使用的资源(Stevens/Rago、YoLinux 和 Opengroup)来看,我的理解是,当您分叉一个进程时,两者父母和孩子从下一个命令继续执行。由于fork()0 返回给子进程,而子进程的进程ID 将返回给父进程,因此您可以使用两个if 语句来区分他们的行为,一个if(pid == 0) 代表子进程,一个if(pid > 0),假设您使用@ 分叉987654326@.

现在,我遇到了最奇怪的事情。在我的 main 函数开始时,我打印到 stdout 几个已分配给变量的命令行参数。这是整个程序中的第一个非赋值语句,然而,似乎每次我在程序后面调用fork时,都会执行这些打印语句。

我的程序的目标是创建一个“进程树”,每个进程有两个子进程,深度为 3,从而创建初始可执行文件的总共 15 个子进程。每个进程在分叉前后打印其父进程 ID 及其进程 ID。

我的代码如下并已正确注释,命令行参数应为"ofile 3 2 -p"(我还没有实现-p/-c标志):

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>


int main (int argc, char *argv[])
{
    if(argc != 5)//checks for correct amount of arguments
    {
        return 0;
    }

    FILE * ofile;//file to write to
    pid_t pid = 1;//holds child process id
    int depth = atoi(argv[2]);//depth of the process tree
    int arity = atoi(argv[3]);//number of children each process should have

    printf("%d%d", depth, arity);

    ofile = fopen(argv[1], "w+");//opens specified file for writing



    int a = 0;//counter for arity
    int d = 0;//counter for depth
    while(a < arity && d < depth)//makes sure depth and arity are within limits, if the children reach too high(low?) of a depth, loop fails to execute
                                  //and if the process has forked arity times, then the loop fails to execute
    {
        fprintf(ofile, "before fork: parent's pid: %d, current pid: %d\n", getppid(), getpid());//prints parent and self id to buffer
        pid = fork(); //forks program
        if(pid == 0)//executes for child
        {
            fprintf(ofile, "after fork (child):parent's pid: %d, current pid: %d\n", getppid(), getpid());//prints parent's id and self id to buffer
                    a=-1;//resets arity to 0 (after current iteration of loop is finished), so new process makes correct number of children
            d++;//increases depth counter for child and all of its children
        }
        if(pid > 0)//executes for parent process
        {
        waitpid(pid, NULL, 0);//waits on child to execute to print status
        fprintf(ofile, "after fork (parent):parent's pid: %d, current pid: %d\n", getppid(), getpid());//prints parent's id and self id to buffer
        }
        a++;//increments arity counter
    }


    fclose(ofile);
}

当我运行gcc main.c -o ptree 然后ptree ofile 3 2 -p 时,控制台被"32" 看似无限地重复,并且文件ofile 的格式看似正确,但对于我认为我的程序应该的来说太大了正在做。

任何帮助将不胜感激。

【问题讨论】:

  • 别忘了fflush(NULL);就在之前 fork()

标签: c linux process fork


【解决方案1】:

我不确定为什么要为孩子执行 fputsstdout,并且没有 Unix 盒子可供验证/测试。

但是,跳出以下内容:

int depth = *argv[2];//depth of the process tree
int arity = *argv[3];//number of children each process should have

您将argv[2]argv[3] 中第一个字符的ASCII 代码作为deptharity,因此您的代码试图生成50^51 进程而不是2^3

你想要的是:

int depth = atoi(argv[2]);//depth of the process tree
int arity = atoi(argv[3]);//number of children each process should have

一旦你解决了这个问题,bleh[0] = depth 和它的双胞胎也需要更正。

编辑虽然现在这不是问题,但您将它与sprintfing 中的一些内容的长度相当接近obuf。让一些消息稍长一点,Kaboom! 至少你想在文件中直接使用snprintf,或者更好的是fprintf

edit 我刚刚意识到fork,作为一个操作系统函数,很可能不知道由C I/O 函数完成的内部缓冲。这可以解释为什么你会得到重复(父母和孩子都会在fork 上获得缓冲数据的副本)。在循环之前尝试fflush(stdout)。还有fflush(ofile) 在每个fork 之前。

【讨论】:

  • 代码现在运行更合理了,但是 32 仍然打印了多次,而它应该只执行一次,因为它不在循环内,并且它在所有 fork 命令之前,并且不在文件中仍然比应有的大得多,总共有 34 个子进程,而应该是 2^3+2^2+2^1=14 个子进程。我编辑了我的源代码以反映更改。有什么想法吗?
  • 刚刚在我的答案底部添加了另一个建议。
【解决方案2】:

您的代码中有 2 个错误:

1)

  int depth = *argv[2];//depth of the process tree
  int arity = *argv[3];//number of children each process should have

使用此代码,您将获得字符串 argv[2] 和 argv[3] 的第一个字符。 正确的代码必须是这样的:

int depth = atoi(argv[2]);
int arity = atoi(argv[3]);

2)

bleh[0] = depth;
fputs(bleh, stdout);
bleh[0] = arity;
fputs(bleh, stdout);

您可以执行类似bleh[0] = (char) depth; 的操作,但您只需保留整数的第一个字节,我猜这不是您想要的,如果您想打印整个整数,只需使用:

printf("%d\n%d", depth, arity);

我刚刚对您的代码进行了这些修改,它似乎运行良好:)

安徽

【讨论】:

  • 谢谢!代码现在运行得更合理了,但是 32 仍然打印了多次,而它应该只执行一次,因为它不在循环内,并且它在所有 fork 命令之前,并且 ofile 仍然比它应该的要大得多,共有34个子进程,而应该是2^3+2^2+2^1=14个子进程。我编辑了我的源代码以反映更改。有什么想法吗?
【解决方案3】:

您无法在函数开始时使用该代码打印出数字。它可能通过将非字符串传递给fputs() 来调用未定义的行为。您应该使用sprintf()(或者更好的是snprintf())将数字正确格式化为字符串,当然还要确保缓冲区足够大以容纳整数的字符串表示形式。

此外,您似乎正在向文件发送文本,但它以二进制模式打开,这似乎是错误的。

【讨论】:

  • 我认为可移植程序总是使用带有fopen的二进制模式。当然,如果您的目标是 POSIX,则无关紧要,因为二进制模式和文本模式必须相同,但始终使用二进制模式将确保您的程序在 Windows 上编译时不会中断(可能需要准备好看到并剥离或解释 \r 字符)。
猜你喜欢
  • 2020-06-18
  • 1970-01-01
  • 1970-01-01
  • 2015-01-05
  • 2016-12-11
  • 2012-12-16
  • 2011-05-08
  • 2021-06-07
  • 1970-01-01
相关资源
最近更新 更多