执行子进程时,OS在哪里存储argv和argc?

aud*_*tic 5 c architecture operating-system program-entry-point virtual-address-space

我很难理解操作系统如何将数据从父进程的地址空间传递到子进程的地址空间.也就是说,在一个C程序中,argc和argv在传入main时存储在哪里?

我理解argv本质上是一个双指针.我不理解的是操作系统在将它们加载到内核后对这些值的作用.为子进程创建地址空间后,是否将这些值推送到新空间的堆栈上?我们显然不想将指针传递给另一个地址空间.

为了记录,我正在使用MIPS32架构.

o11*_*11c 7

在Linux上,至少在我使用的架构上,该过程从%esp指向以下内容开始:

argc | argv[0] | argv[1] | ... argv[argc - 1] | argv[argc] == NULL | envp[0] | envp[1] ... envp[?] == NULL
Run Code Online (Sandbox Code Playgroud)

传统上命名的第一个函数是命名的_start,它的工作是计算(argc = %esp, argv = ((char *)%esp) + 1, envp = ((char *)%esp) + argc + 2),然后调用main适当的调用约定.

在x86上,参数在堆栈上传递.

在amd64,他们获得通过寄存器%rdi,%rsi%rdx.

在MIPS,谷歌告诉我有在使用几种不同的调用约定-包括O32,N32,N64 -但所有的人都用$a0,$a1,$a2第一次.


Myk*_*lis 5

对于不同的操作系统,该进程是不同的,并且实际上根据新进程的创建方式而有所不同。由于我更熟悉现代 Microsoft 操作系统如何处理此问题,因此我将从这里开始,并在最后引用 nix 的。

当[Microsoft]操作系统创建进程时,它会分配一个进程环境块来保存特定于该进程的数据。其中包括调用程序所使用的命令行参数。该进程环境块是从目标进程的地址空间中分配的,并且指向它的指针被提供给进程的入口点。子进程的进程环境块通常通过将父进程的环境块复制到新进程的地址空间来初始化 - 不涉及直接共享内存。

对于基于 C 的程序,入口点不是main()程序员提供的函数。相反,它是 C 运行时库提供的例程,负责在将控制权交给程序员的main().

有很多东西需要初始化,但其中之一是设置argcargv值。为此,运行时将查阅进程环境块以查找调用它的程序名称和参数。然后(通常)它会从进程堆中为 argv 分配值(即,使用类似的东西malloc()),并将 argc 分配给找到的参数数量(加一,用于程序名称)。

argc 和 argv 的实际值像在 C 中传递的任何其他参数一样被压入堆栈,因为main()C 运行时的调用只是普通的函数调用。

因此,当您在子进程中编写的代码main()访问 argv 时,它将从自己的进程堆中读取值。这些值的来源是进程环境块(由操作系统存储在本地地址空间中),该块最初是通过从父进程复制进程环境块来初始化的。

在 *nix 平台上,情况有很大不同。当前讨论的主要区别是 nix 会将新进程的命令行参数直接存储到进程初始线程的堆栈空间中。(它还在这里存储环境变量。)因此,在 *nix 上,main使用指向存储在堆栈本身中的值的 argv 参数来调用。

您可以在execve的联机帮助页中收集其中的一些内容,而Michael Kerrisk 的Linux 编程接口在第 6.4 节中提供了很好的描述,您可能会在网上找到摘录的内容。

  • 对于类似 Windows 的操作系统来说确实如此,可以在 [Wine 源:/include/winternl.h](http://source.winehq.org/git/wine.git/blob/HEAD:/include/winternl.h) 中看到。 h)(搜索“CommandLine”)和[ReactOS源:/include/psdk/winternl.h](http://svn.reactos.org/reactos/trunk/reactos/include/psdk/winternl.h)(搜索“ CommandLine”)通过跟踪 [MSDN:GetCommandLine 函数](http://msdn.microsoft.com/en-us/library/windows/desktop/ms683156(v=vs.85).aspx) 的实现。但对于类似 Linux 的操作系统来说也是如此吗?您可以添加证据或相关来源吗? (2认同)