当我从 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"
会打印预执行值。这是因为可执行文件在与我键入命令的“不同环境”中运行吗?
答案1
在 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
调用允许您使用指定的环境执行程序,而不是从父进程复制的环境。]