根据我的理解,每当您在 shell 中键入诸如“ls”之类的命令时,父进程(即我的 shell)会使用 fork() 系统调用复制自身,然后使用 exec() 系统调用将其替换为新进程,这种情况下“ls”一旦退出,控制权就会交回给我的外壳。
然而,当我在“ls”上运行 strace 时,我只看到 execve() 调用,没有 fork,并且控制权仍然交回给我的 shell。这里有点困惑...
$ strace ls
execve("/usr/bin/ls", ["ls"], 0x7ffd938934e0 /* 25 vars */) = 0
brk(NULL) = 0x1134000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f6ea9e38000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=23255, ...}) = 0
答案1
你的理解是正确的。当你跑步时,strace ls
甚至会有两个叉子。 shell fork 自身并用于exec()
run strace
,strace 也对 run 执行相同的操作ls
。
您在 strace 输出中看不到 fork,因为 strace 打印了源自以下位置的所有系统调用:strace的子进程那时分叉已经发生:
bash
分叉和运行strace
strace
叉子父进程
strace
附加到子进程以拦截所有系统调用。从此时起您只能看到系统调用。
孩子
strace
跑步时ls
使用execve()
strace
查看分叉发生的一种方法是“从外部”连接:
- 用于
echo $$
获取shell的进程id - 运行时
strace -f --attach=PID
将“PID”替换为上面的进程 ID在另一个控制台中。 ls
在第一个 shell 中运行- 在另一个控制台窗口中,您将看到 shell 和分叉子项中发生的所有系统调用(包括
fork()
/clone()
调用)。 - 在第二个控制台中使用 CTRL+C 来停止 strace。
另一件需要提到的事情是,fork()
在当前的 Linux 内核上是使用clone()
系统调用来实现的,因此您可能会在 strace 输出中看到clone(…)
而不是。fork()