c-执行子进程时,操作系统将argv和argc存储在哪里



我很难理解操作系统如何将数据从父进程的地址空间传递到子进程的地址。也就是说,在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

第一个被调用的函数传统上被命名为_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值。要做到这一点,运行时将查阅进程环境块,以找到调用它的程序名称和参数。然后,它将(通常)为进程堆外的argv分配值(即,使用类似malloc()的东西),并将argc分配给找到的参数数(加上一,表示程序名)。

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

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

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

您可以在execve的手册页中找到其中的一些内容,而Michael Kerrisk的Linux编程界面在第6.4节中有一个很好的描述,您可以在网上找到摘录。

相关内容

  • 没有找到相关文章