fork()系统调用的工作(C程序)
Working of fork() system call(C program)
当我 运行 这段代码时,它打印 "hello world" 20 次。根据我的理解,它应该打印 32 次。此外,每个 运行 的换行符打印方式不同。我不明白这一点。请帮忙。
int
main(int argc, char** argv){
if(fork() || fork())
fork();
if(fork() && fork())
fork();
printf("\nhello world");
return 0;
}
要理解你必须剖析这两个结构。首先构造
if( fork() || fork() )
fork();
或者展开短路的另一种写法或者:
if( !fork() )
if( !fork() )
goto not_taken;
fork();
not_taken:
或没有goto
的
if( !fork() ) {
if( fork() )
fork();
}
else
fork();
进程数将增加五倍。这是因为首先原始进程分叉,然后在子进程中 fork
将 return 归零,它将再次 fork
(短路或)。然后,如果这些 returned 中的任何一个非零(即分叉的父进程),它将再次分叉 - 这恰好在父进程中完成,它是子进程。即条件中的 fork
将被调用一次,最后一个 fork
将被调用一次 运行。
现在是第二个构造:
if( fork() && fork() )
fork();
或展开短路和:
if( fork() )
if( fork() )
fork();
进程数将翻四倍。原因是第一个 fork
将是 运行 在父进程中,如果 return 非零(即父进程),第二个 fork
将被调用并且如果 returns 非零,则最后会。所以这里所有三个 fork
都在原始过程中被调用 - 这意味着它被调用了三次。
所以第一个构造将使它们成为五个进程,然后第二个构造将为每个进程再创建三个。这意味着我们将有 5*4
个进程。
你没有获得一致的换行符的原因可能主要是由于 printf
缓冲。 printf
只会立即打印出一个换行符,然后在它终止时写入其余数据,这意味着它们分两步打印字符串。我们有 20 个进程首先打印一个换行符,然后同时打印 "hello world" - 无法保证不同进程打印内容之间的顺序。
一个小的可能性是实际的打印输出不需要是原子的。那是一个进程可能会写入 "hell",然后第二个已经写入 "hello " 的进程开始写入,这可能是 "world" - 这将导致 "hellworld"。
让我们数一数每个fork被调用了多少次。在这个区块中有 3 个分叉:
if(fork() || fork())
fork();
第一个 fork 被第一个进程调用 1
次。
第二个 fork 仅被调用 1
次,由第二个进程调用。 (第一个过程没有达到)
第 3 个分支被第一个和第二个进程调用 2
次。
所以,此时您已经有 5 个进程。
对于这个区块还有另外 3 个分叉:
if(fork() && fork())
fork();
第一个 fork 被调用 1
x 5 次。这里fork出来的子进程直接去printf
第二个 fork 被调用 1
x 5 次。仍然,分叉的子进程转到 printf
.
第 3 个 fork 被调用 1
x 5 次。
所以你总共 20
。
如果您分别评估两个 if 结构,这会容易得多。计算第一个中创建的进程数,然后将其乘以第二个中创建的进程数。结果是进程总数和调用 printf 调用的次数。
(我们假设 fork 调用不会失败。)
这个方便的图表显示了第一个 if 语句,其中列是分支,行是进程。
叉子 - f1, f2, f3
进程 - c0, c1, ...,c4
xN - 标记进程的创建,其中 N 是已创建进程的索引
. - 标记进程创建
_ - 进程存在但什么都不做
(没有字符表示该进程此时不存在)
f1 f2 f3
c0 _ x1 _ x2 _
c1 . x3 x4 _
c2 . _
c3 . _ _
c4 . _
此时存在 5 个进程,另外创建了 4 个。下图显示了第二个 if 语句,但仅针对一个进程:
f1 f2 f3
c0 _ x1 x2 x3 _
c1 . _ _ _
c2 . _ _
c3 . _
这条 if 语句创建了 3 个额外的进程,因此存在 4 个。但是请记住,我们之前有 5 个进程,而不是一个。所以两个 if 语句一起创建 5 x 4 个进程,总共 20 个。
当我 运行 这段代码时,它打印 "hello world" 20 次。根据我的理解,它应该打印 32 次。此外,每个 运行 的换行符打印方式不同。我不明白这一点。请帮忙。
int
main(int argc, char** argv){
if(fork() || fork())
fork();
if(fork() && fork())
fork();
printf("\nhello world");
return 0;
}
要理解你必须剖析这两个结构。首先构造
if( fork() || fork() )
fork();
或者展开短路的另一种写法或者:
if( !fork() )
if( !fork() )
goto not_taken;
fork();
not_taken:
或没有goto
的
if( !fork() ) {
if( fork() )
fork();
}
else
fork();
进程数将增加五倍。这是因为首先原始进程分叉,然后在子进程中 fork
将 return 归零,它将再次 fork
(短路或)。然后,如果这些 returned 中的任何一个非零(即分叉的父进程),它将再次分叉 - 这恰好在父进程中完成,它是子进程。即条件中的 fork
将被调用一次,最后一个 fork
将被调用一次 运行。
现在是第二个构造:
if( fork() && fork() )
fork();
或展开短路和:
if( fork() )
if( fork() )
fork();
进程数将翻四倍。原因是第一个 fork
将是 运行 在父进程中,如果 return 非零(即父进程),第二个 fork
将被调用并且如果 returns 非零,则最后会。所以这里所有三个 fork
都在原始过程中被调用 - 这意味着它被调用了三次。
所以第一个构造将使它们成为五个进程,然后第二个构造将为每个进程再创建三个。这意味着我们将有 5*4
个进程。
你没有获得一致的换行符的原因可能主要是由于 printf
缓冲。 printf
只会立即打印出一个换行符,然后在它终止时写入其余数据,这意味着它们分两步打印字符串。我们有 20 个进程首先打印一个换行符,然后同时打印 "hello world" - 无法保证不同进程打印内容之间的顺序。
一个小的可能性是实际的打印输出不需要是原子的。那是一个进程可能会写入 "hell",然后第二个已经写入 "hello " 的进程开始写入,这可能是 "world" - 这将导致 "hellworld"。
让我们数一数每个fork被调用了多少次。在这个区块中有 3 个分叉:
if(fork() || fork())
fork();
第一个 fork 被第一个进程调用 1
次。
第二个 fork 仅被调用 1
次,由第二个进程调用。 (第一个过程没有达到)
第 3 个分支被第一个和第二个进程调用 2
次。
所以,此时您已经有 5 个进程。
对于这个区块还有另外 3 个分叉:
if(fork() && fork())
fork();
第一个 fork 被调用 1
x 5 次。这里fork出来的子进程直接去printf
第二个 fork 被调用 1
x 5 次。仍然,分叉的子进程转到 printf
.
第 3 个 fork 被调用 1
x 5 次。
所以你总共 20
。
如果您分别评估两个 if 结构,这会容易得多。计算第一个中创建的进程数,然后将其乘以第二个中创建的进程数。结果是进程总数和调用 printf 调用的次数。
(我们假设 fork 调用不会失败。)
这个方便的图表显示了第一个 if 语句,其中列是分支,行是进程。
叉子 - f1, f2, f3
进程 - c0, c1, ...,c4
xN - 标记进程的创建,其中 N 是已创建进程的索引
. - 标记进程创建
_ - 进程存在但什么都不做
(没有字符表示该进程此时不存在)
f1 f2 f3
c0 _ x1 _ x2 _
c1 . x3 x4 _
c2 . _
c3 . _ _
c4 . _
此时存在 5 个进程,另外创建了 4 个。下图显示了第二个 if 语句,但仅针对一个进程:
f1 f2 f3
c0 _ x1 x2 x3 _
c1 . _ _ _
c2 . _ _
c3 . _
这条 if 语句创建了 3 个额外的进程,因此存在 4 个。但是请记住,我们之前有 5 个进程,而不是一个。所以两个 if 语句一起创建 5 x 4 个进程,总共 20 个。