Fork 和 exec 机制

Fork 和 exec 机制

我正在阅读有关fork & execLinux 中的机制。以下是有关该主题的问题。

  1. 这是否意味着ls在命令提示符下输入一个简单命令会使 bash 进程fork(复制自身),然后发出exec调用以用 ls 命令代码替换克隆的 bash 代码?
  2. 关于上面的例子并根据文档: child process has the same environment as its parent 这是否意味着ls命令进程可以访问 bash 中迄今为止定义的所有环境变量?
  3. 该机制是否仅适用于内置命令之类的二进制文件或也适用于 shell 脚本?

答案1

  1. 不,它是作为子进程产生的。如果你对这些事情感兴趣,/usr/bin/ls你可以尝试一下:strace

    [foo@turtle ~]$ strace -f -eexecve -o bash.strace bash
    [foo@turtle ~]$ ls
    bash.strace
    [foo@turtle ~]$ exit
    [foo@turtle ~]$ cat bash.strace 
    26213 execve("/usr/bin/bash", ["bash"], [/* 37 vars */]) = 0
    26214 +++ exited with 0 +++
    26213 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=26214, si_uid=10003, si_status=0, si_utime=0, si_stime=0} ---
    26230 execve("/usr/bin/ls", ["ls", "--color=auto"], [/* 37 vars */]) = 0
    26230 +++ exited with 0 +++
    26213 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=26230, si_uid=10003, si_status=0, si_utime=0, si_stime=0} ---
    26213 +++ exited with 0 +++
    
  2. 是的,确实有点,有点:无论是否导出,都是有区别的:

    [foo@turtle ~]$ DIR_I_WANT_TO_LS=/home/foo 
    [foo@turtle ~]$ ls $DIR_I_WANT_TO_LS
    bar  bash.strace  ls_from_a_script.sh
    [foo@turtle ~]$ set -x
    [foo@turtle ~]$ ls $DIR_I_WANT_TO_LS
    bar  bash.strace  ls_from_a_script.sh
    [foo@turtle ~]$ ./ls_from_a_script.sh 
    $DIR_I_WANT_TO_LS=
    bar  bash.strace  ls_from_a_script.sh
    $DIR_I_WANT_TO_LS=/home/foo/bar
    [foo@turtle ~]$ export DIR_I_WANT_TO_LS
    [foo@turtle ~]$ ./ls_from_a_script.sh 
    $DIR_I_WANT_TO_LS=/home/foo
    bar  bash.strace  ls_from_a_script.sh
    $DIR_I_WANT_TO_LS=/home/foo/bar
    
  3. 不,它可以完美地与任何程序(二进制、shell、ruby、python、lua、node……)配合使用。后者通常有一个经常ENV为此调用的结构。

答案2

这是否意味着在命令提示符下输入像 ls 这样的简单命令会使 bash 进程 fork(复制自身),然后发出 exec 调用以用 ls 命令代码替换克隆的 bash 代码?

基本上是的。不过技术细节可能有所不同:

  • 在现代操作系统上,fork() 速度很快,因为使用了写时复制。但在某些较旧的系统上,每次 fork 都必须完整复制进程的内存,因此 shell 改用vfork(2)它只创建了一个新进程但共享相同的内存。

  • (在 Linux 上,libc 中的 fork(3) 函数实际上使用克隆(2)系统调用。但最终结果仍然相同。)

  • libc 有各种执行…(3)函数,它们都转化为执行(2)系统调用。

关于上面的例子,根据文档:子进程具有与其父进程相同的环境,这是否意味着 ls 命令进程可以访问 bash 中迄今为止定义的所有环境变量?

它将有权访问复印件这些环境变量。(因此,如果ls发生任何变化,它们不会向上移动。)

请注意,并非所有 bash 变量都会自动环境变量 – 仅限明确“导出”到环境中的变量。(您可以使用它declare -p来查看所有变量,无论是否导出。)

该机制是否仅适用于内置命令之类的二进制文件或也适用于 shell 脚本?

它也适用于 shell 脚本——内核认为脚本#!可执行,并将它们传递给行中指定的任何“解释器” #!

(Linux 还有“binfmt_misc”,允许定义和执行自定义格式 - 例如,可以通过 Wine 运行 .exe。)

尽管如果内核拒绝 exec 调用,一些 shell 仍然会仍然尝试通过使用该脚本执行 /bin/sh 来手动运行该脚本。(事实上,有时 libc 甚至会这样做 - glibc 的exec(3)手册页中提到了这一点。)

相关内容