当我从 Bash shell 运行可执行文件(如 a.out)时,该可执行文件是否在某种“子”shell 中运行,即与我输入的 shell 不同?
我将尝试用一个例子来说明我的问题。以下程序获取并打印环境变量的值,对其进行更改,然后重新获取并再次打印:
#include <iostream>
#include <string>
#include <cstdlib>
int main( int argc, char* argv[] )
{
std::string str( getenv( "FOO" ) );
std::cout << str << std::endl;
setenv( "FOO", "bar", 1 );
str = getenv( "FOO" );
std::cout << str << std::endl;
return 0;
}
现在,当我在 Bash 提示符下运行以下命令时,请注意输出:
>unset FOO && export FOO=foo && printf "$FOO\n" && ./a.out && printf "$FOO\n"
foo
foo
bar
foo
>
>unset FOO && export FOO=baz && printf "$FOO\n" && ./a.out && printf "$FOO\n"
baz
baz
bar
baz
所以我正在导出FOO
,所以它可以从可执行文件中获取 - 我明白这一点。并且可执行文件的输出显示正在更改的环境变量。
但最终printf "$FOO\n"
打印出可预执行的值。这是因为可执行文件在与我键入命令的“不同环境”中运行吗?
在 Unix 上,每个进程都有自己独立的环境副本。
fork()
进程在创建时(通过)通过复制父进程的环境来获取其初始环境。因此,如果您在调用 a.out 之前将变量添加到 shell 的环境中,a.out 会看到它(因为 a.out 收到了包含该变量的 shell 环境的副本)。
如果 a.out 改变了环境,那就改变了 a.out 的环境——而不是 shell 的。如果 a.out 要调用另一个程序(例如,使用
system()
),那么该程序将看到更改的环境,因为它会得到 a.out 环境的副本。当 a.out 退出时,其环境变量被销毁;当然,如果一个子进程正在运行,它仍然会保留它的副本(直到它退出)。
如果您在 shell 中修改环境,而 a.out 仍在运行(例如,在后台
a.out &
:),那么 a.out 将看不到更改:环境仅在进程创建时被复制。[注意这是典型的方式;系统
execve
调用允许您使用您指定的环境执行程序,而不是从父进程复制的环境。]