【问题标题】:How many times 'a' is printed? (includes fork in a loop)'a' 被打印了多少次? (包括循环中的分叉)
【发布时间】:2021-07-18 16:03:17
【问题描述】:

假设我们有一个全局变量int a = 0a会被打印多少次,第一个值和最后一个值是多少?

for(int i=0; i<6; i++){
    fork();
    a++;
    printf("a = %d\n", a)
    }
printf("a = %d\n", a);

这是我所做的:

  1. i=0:又创建了一个线程,所以我现在有 2 个正在运行,它们都打印(a=1,a=2),不知道哪个打印什么。
  2. i=1:这两个线程产生了更多的 2 个线程,所以我总共有 4 个线程,以及 (a=3,4,5,6) 的 4 个打印。
  3. i=3:现在有了 8,我们得到 (a=7,8,9,10,11,12,13,14)。
  4. 有 16 个,直到 a=30
  5. 使用 32,直到 a=62。

到目前为止,我在循环的一侧有2+4+8+16+32=62,并且所有进程都到达外部循环以获得额外的32个,所以我得到94总共打印a=1第一个值和a=62结束值.


我将不胜感激有关我的解决方案的任何反馈,因为我不确定如何在代码中对其进行测试,因为我无法在 Windows 中使用 fork。
提前致谢!

【问题讨论】:

  • 作为未来的提示:Windows 的 WSL 功能允许 Linux 用户模式代码在 Windows 下运行。它支持分叉。
  • 是时候拿出笔和纸了。绘制正在创建的进程树。开始计算每个进程的循环数(记住大多数进程不会迭代全部数量)。
  • fork 创建了一个新进程,一个完全相同的双胞胎,所有变量的副本完全相同。因此,打印输出应为 1,1,2,2,2,2,3,3,3,3,3,3,3,3...等等。哦,等等,您使用的是 Windows?里面不支持。没有线索。
  • @Someprogrammerdude Tbh 我尝试绘制树形图,但我有点困惑,从一个开始,我是否应该像 5 个箭头一样绘制 5 个进程,因为它有 5 个?然后每个过程中的4个箭头等..?这是我第一次处理这些东西,不想在错误上建立自己的理解
  • @Tarik 不要担心windows,与问题无关,但我知道fork 也会复制全局变量?就像它是一个新变量?所以 a 的最大值只有 6?

标签: c fork


【解决方案1】:

一旦按如下所述修复并完成,程序会在常见条件下打印 190 或 448 行,具体取决于其标准输出的目标位置。

首先,程序不会打印任何东西,因为printf("a = %d\n", a)后面没有分号,所以程序不会编译,因此不会执行。

假设我们添加了缺少的分号,包括 &lt;stdio.h&gt;&lt;unistd.h&gt;,将代码包装在 main 例程中,并在 Unix 环境中编译和执行。然后,在输出到交互式设备的情况下:

  • 标准输出流是行缓冲或非缓冲的。 (通常是前者,但它们将具有相同的效果,因为所有printf 格式字符串都以\n 结尾。)每行将在发送到输出流后立即发送到设备。所以会在下面fork之前发送。
  • i的值对迭代进行编号,在迭代0中,一个进程变为两个,a从0递增到1(在每个进程中单独),每个进程打印“a = 1”。因此打印了两条“a = 1”行。
  • 在迭代 1 中,两个进程变为四个,a 变为 2,并打印四个“a = 2”。
  • 在迭代 2 中,打印了八个“a = 3”。
  • 在第 3 次迭代中,打印了 16 个“a = 4”。
  • 在第 4 次迭代中,打印了 32 个“a = 5”。
  • 在第 5 次迭代中,打印了 64 个“a = 6”。
  • 循环后,在64个进程中,a为6,打印64个“a = 6”。
  • 因此我们有 2 + 4 + 8 + 16 + 32 + 64 + 64 = 190 行。

如果输出到磁盘上的文件或管道,则它是完全缓冲的。假设缓冲区足够大,可以容纳一个进程的所有输出。在这种情况下:

  • 每一行都保存在缓冲区中,仅在程序退出时发送到设备。
  • 在第 0 次迭代中,一个进程变为两个,a 从 0 递增到 1,每个进程将“a = 1”打印到其缓冲区中。现在每个缓冲区都保存“a = 1”。
  • 在迭代 1 中,两个进程变为四个,每个进程将“a = 2”打印到其缓冲区中。
  • 在第 2 次迭代中,八个进程中的每一个都将“a = 3”打印到其缓冲区中。
  • 在第 3 次迭代中,16 个进程中的每一个都将“a = 4”打印到其缓冲区中。
  • 在第 4 次迭代中,32 个进程中的每一个都将“a = 5”打印到其缓冲区中。
  • 在第 5 次迭代中,64 个进程中的每一个都将“a = 6”打印到其缓冲区中。
  • 循环结束后,64 个进程中的每一个都将“a = 6”打印到其缓冲区中。
  • 然后每个进程在准备退出时刷新其缓冲区。每个进程在其缓冲区中包含七行“a = 1”、“a = 2”、“a = 3”、“a = 4”、“a = 5”、“a = 6”和“a = 6” ”。所以 64 个进程中的每个进程打印 7 行,总共 448 行。

如果打印的行更长,或者每个进程的行数更多,它们有时会填满缓冲区,并且进程会提前打印它们的行。这些行将不会被以后的分叉复制,并且打印的总数将介于最小值(来自行缓冲输出)和最大值(来自完全适合缓冲区的完全缓冲输出)之间。由于行尾与填充缓冲区不一致,我们也可能会得到零碎的行。

【讨论】:

    【解决方案2】:

    如果您无法运行,请模拟fork。然后,你可以统计fork创建的进程数和a打印的总数。

    如果您想了解这种递归模型发生了什么,您现在可以使用逐步调试。

    #include <stdio.h>
    
    int processes = 1;
    int prints_of_a = 0;
    
    void fake_fork(int a, int i) {
        ++ processes;
    
        // behave like fork, start from inside loop
        goto start_here;
    
        for(; i<6; i++){
            fake_fork(a, i);
    start_here:
            a++;
            //printf("a = %d\n", a); 
            ++ prints_of_a;
        }
    
        //printf("a = %d\n", a); 
        ++ prints_of_a;
    }
    
    int main(int argc, char * argv[]) {
    
        int a = 0;
    
        for(int i=0; i<6; i++){
            fake_fork(a, i);
            a++;
            //printf("a = %d\n", a); 
            ++ prints_of_a;
        }
    
        //printf("a = %d\n", a); 
        ++ prints_of_a;
    
        printf("processes: %d\n", processes);
        printf("prints a : %d\n", prints_of_a);
    
        return 0;
    }
    

    输出:

    processes: 64
    prints a : 190
    

    【讨论】:

      猜你喜欢
      • 2020-05-22
      • 2021-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-31
      • 1970-01-01
      相关资源
      最近更新 更多