1. 思考
-
如下是 main函数 的原型,思考一个问题,我们通过命令行传递的参数,main函数 是如何获得的?环境变量 main函数又是如何获取的。
c/* 基础形式(无参数) */ int main(void); /* 标准形式(可以获取命令行参数) */ int main(int argc, char *argv[]); /* 扩展形式形式(可以获取命令行参数和环境变量), 此形式是非标准的扩展(C 标准未强制要求), 但主流平台(Linux、Windows、macOS)普遍支持 */ int main(int argc, char *argv[], char *envp[]);
-
上述问题在理解了linux进程创建的基本流程,以及linux进程的内存布局,相信就能做到心中有数
2. linux进程内存布局
-
在创建一个进程时通过fork() 完全将父进程复制一份,然后通过execve() 将要执行的任务加载到进程空间,进程空间的大致布局如下所示。
-
.text .rodata .data .bss stack heap 是可执行文件的代码段和数据段以及堆栈空间,在此便不过多赘述
-
mmap段 则为进程提供了一下灵活的机制,一般用来存放动态链接库,或者使用系统调用mmap() 创建内存映射时便是使用这部分空间,在本文中暂不展开。
-
环境变量&命令行参数 是在进程运行前由execve()复制进来的,实时上除了mmap段动态加载的内容,其余部分都是进程运行前由execve() 复制进来的。
-
通过
cat /proc/self/maps
可以查看当前进程内存布局
3. 进程如何获取命令行参数和环境变量
-
至此,其实已经很清晰了,环境变量和命令行参数都是系统调用execve() 在加载进程时复制到进程空间的,main函数只需到对应的位置去取即可 ,如下是execve() 的原型,
c/* Replace the current process, executing PATH with arguments ARGV and environment ENVP. ARGV and ENVP are terminated by NULL pointers. */ extern int execve (const char *__path, char *const __argv[], char *const __envp[]) __THROW __nonnull ((1, 2));