Child 和 Parent 的执行流程

Execution Flow of Child and Parent

在网上看到我无法真正确定 运行 之前是哪个进程,即 child or parent 我计划禁用我 PC 上的 ASLR 和 运行 调试器以查看如果我可以生成执行模式,我所做的观察在下面是 GitGist 到 GDB disas(完整)以及源代码

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
fork();
//fork();
printf("LINUX\n");
//printf("my pid is %d",(int) getpid());
fork();
printf("REDHAT\n");
//printf("my pid is %d",(int) getpid());

//fork();
return 0;
}

这是我在 gdb 中取消它时所说的代码,它给了我:-

gdb-peda$ disas main
Dump of assembler code for function main:
   0x000000000000068a <+0>: push   rbp
   0x000000000000068b <+1>: mov    rbp,rsp
   0x000000000000068e <+4>: call   0x560 <fork@plt>
   0x0000000000000693 <+9>: lea    rdi,[rip+0xaa]        # 0x744
   0x000000000000069a <+16>:    call   0x550 <puts@plt>
   0x000000000000069f <+21>:    call   0x560 <fork@plt>
   0x00000000000006a4 <+26>:    lea    rdi,[rip+0x9f]        # 0x74a
   0x00000000000006ab <+33>:    call   0x550 <puts@plt>
   0x00000000000006b0 <+38>:    mov    eax,0x0
   0x00000000000006b5 <+43>:    pop    rbp
   0x00000000000006b6 <+44>:    ret    
End of assembler dump.

所以基本上它给了我一个执行的固定模式,所以我认为这应该意味着程序应该总是以特定的顺序执行我尝试了 disas main 大约 3 次以查看顺序是否真的改变了并且确实如此不是,但是当我最终 运行 生成的二进制文件给了我不同的输出

root@localhost:~/os/fork analysis# ./forkagain
LINUX
REDHAT
LINUX
REDHAT
REDHAT
REDHAT
root@localhost:~/os/fork analysis# ./forkagain
LINUX
LINUX
REDHAT
REDHAT
REDHAT
REDHAT

这与我在disas中的观察不一致,请有人填补我理解中的空白吗?

Fork Analysis Full

每个进程都按照完美定义的顺序执行。这里的技巧是不能保证每个进程都会在一个 tick(进程占用执行单元的时间段)内执行,也不能保证从同一个进程派生的两个进程会在分叉顺序。

如果我们假设 A (LINUX) 和 B (REDHAT) 的打印是基准,那么您可以获得 A 和 B 的任意序列,前提是:

  • 序列以 A 开头
  • 一共有两个A,四个B
  • 每个A后面有两个B

AABBBB
ABABBB
阿巴巴

是抢占式多任务处理的所有可能输出 OS。

P.S。如果没有 Employed 的话,这个答案是不完整的。

i tried disas main about 3 times to see if the order actually ever changes

顺序在编译时固定,因此如果不重新编译程序,它永远不会改变。

此外,顺序由您的程序源决定——不允许编译器re-order您的输出。

然后您观察到的是 OS 由于调用 fork 而引入的不确定性——在 fork 之后,无法保证哪个进程将 运行 第一,或者多长时间。 parent可以运行完成,然后child。或者 child 可能 运行 先完成。或者他们可以 运行 和 time-slicing,一次说一行。

此外,当今大多数 non-ancient Linux 系统都 运行ning 在 multi-processor 机器上,两个独立的进程可以 运行 同时分叉后

另一个复杂的问题是您的程序不是 well-defined,因为 stdio 缓冲。当您看到 6 行输出时,您可能很难解释这个结果:

./forkagain | wc -l
8
./forkagain > junk.out; cat junk.out
LINUX
REDHAT
LINUX
REDHAT
LINUX
REDHAT
LINUX
REDHAT

您应该在 fork 之前添加 fflush(stdout); 以避免这种并发症。

P.S。你还应该 un-learn 以 root 身份 运行 的坏习惯——迟早你会犯一个愚蠢的错误(比如在错误的目录中键入 rm -rf *),并且真的抱歉,您是以 root 身份执行的。