为什么 'ls' 是由 execve() 调用而不是 fork() 创建的

为什么 'ls' 是由 execve() 调用而不是 fork() 创建的

根据我的理解,每当您在 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的子进程那时分叉已经发生:

  1. bash分叉和运行strace

  2. strace叉子

  3. 父进程strace附加到子进程以拦截所有系统调用。

    从此时起您只能看到系统调用。

  4. 孩子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()

相关内容