当执行子进程时,操作系统在哪里存储argv和argc?

当执行子进程时,操作系统在哪里存储argv和argc?,c,architecture,operating-system,main,virtual-address-space,C,Architecture,Operating System,Main,Virtual Address Space,我很难理解操作系统是如何将数据从父进程的地址空间传递到子进程的地址空间的。也就是说,在C程序中,argc和argv在传递到main时存储在哪里 我理解argv本质上是一个双指针。我不理解的是,在将这些值加载到内核之后,操作系统会如何处理它们。为子进程创建地址空间后,是否将这些值推送到新空间的堆栈上?我们显然不想把指针传递到另一个地址空间 作为记录,我正在使用MIPS32体系结构。在Linux上,至少在我玩过的体系结构上,这个过程从指向以下内容的%esp开始: argc | argv[0] | a

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

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


作为记录,我正在使用MIPS32体系结构。

在Linux上,至少在我玩过的体系结构上,这个过程从指向以下内容的
%esp
开始:

argc | argv[0] | argv[1] | ... argv[argc - 1] | argv[argc] == NULL | envp[0] | envp[1] ... envp[?] == NULL
调用的第一个函数传统上命名为
\u start
,其任务是计算
(argc=%esp,argv=((char*)%esp)+1,envp=((char*)%esp)+argc+2)
,然后使用适当的调用约定调用
main

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

在amd64上,它们在寄存器
%rdi
%rsi
%rdx
中传递


在mips上,Google告诉我有几种不同的调用约定在使用,包括O32、N32、N64,但它们都使用
$a0
$a1
$a2

对于不同的操作系统,该过程是不同的,并且确实根据新过程的创建方式而有所不同。由于我更熟悉现代微软操作系统如何处理这个问题,我将从这里开始,并在最后参考nix

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

对于基于C的程序,入口点不是程序员提供的
main()
函数。相反,它是由C运行时库提供的一个例程,负责在将控制权交给程序员的
main()
之前初始化运行时环境

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

argc和argv的实际值被推送到堆栈上,就像其他参数在C中传递一样,因为C运行时对
main()
的调用只是一个普通的函数调用

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

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


您可以在的手册页中收集其中的一些信息,而Michael Kerrisk在第6.4节中有一个很好的描述,您可以在网上找到摘录。

这对于类似Windows的操作系统是正确的,可以通过跟踪的实现在(搜索“命令行”)和(搜索“命令行”)中看到。但对于类似Linux的操作系统也是如此吗?您可以添加证据或相关源吗?您可以添加证据或相关源的链接吗?现在如何处理指向参数的指针(即指向argv的指针)?如果参数本身在堆栈上抛出,指针是否也放在堆栈上,或者是在参数寄存器中传递的指针(我使用的是MIPS体系结构)?@audiFanatic的“参数”是指根据调用约定的函数参数:
argc
argv
,和
envp
;函数参数总是值;前面的计算是如何计算作为指针的值(尽管第一个指针通常使用
pop%esp
,并去掉一堆+4s或+8s)。