@TOC 进程的环境,主要是描述是对程序执行时,main函数如何被调用;命令行参数以及环境变量如何传递给程序;进程的存储空间布局形式;以及进程终止方式;进程的资源限制等问题。
一个典型的C程序总是从main开始执行 int main(argc,char* argv[]) 内核执行C程序时,其通过父进程采取exec函数,在调用main之前先调用一个启动例程。启动例程将 命令行参数和环境变量从内核中获取,传递给进程。
一个典型的C程序启动与终止:
可以看到,总是从一个C启动例程开始,由它启动main函数,而在main函数中又可能调用用户函数过程。那么一个进程其终止的可能路线包括如下情况:
- 从main中返回
- 调用exit
- 调用_exit
- 从启动例程中返回
- 调用abort()
- 接收到一个信号 终止处理程序由exit调用,可以通过atexit将终止函数进行注册,exit将依次调用终止处理程序,最后exit将调用标准IO的清理程序,并从_exit返回。 _exit函数将直接进入内核,而exit会执行相应的清理工作(典型地:调用fclose关闭打开的文件描述符,并将标准IO的输出缓冲区刷新到文件中)。
命令行参数与环境表
当执行一个程序时,调用exec的进程将命令行参数传递给新程序。而每个程序会接收一张环境表。和参数表一样,环境表是一个字符指针数组,其中每一个指针包含一个C字符串的地址。而全局变量envion则指向该指针数组的地址。
extern
环境变量通常由一张环境变量表进行描述,每一个表项为指向一个环境变量字符串(name=value)的指针;
进程的地址空间
通常而言,一个进程的地址空间布局包含以下几个部分:
一个典型的进程虚拟地址空间总是从逻辑地址0x08048000开始,栈总是从0xC0000000往下 由于历史遗留问题,代码段并不是逻辑地址0开始,而下面保留者128M的逻辑地址空间保留给未来可能使用。
共享库的映射
在栈和堆之间的虚拟地址空间还存在共享库的映射区域,每个进程使用共享库时并不是在此处存储一份实际的共享库数据,而是基于虚拟内存的映射技术,将所有使用该共享库的进程映射到同一份共享库的副本。
共享库基于动态链接技术,大大减少可执行目标文件的大小。并且当共享库更新时,只要保持接口不变,应用程序无需做更改。
#-static指明使用静态链接
linux>gcc test.cpp -o test_without_static
编译产生的可执行目标文件的大小如下:
动态内存分配
ISO C中采取3个函数用于动态内存分配:
|系统调用库函数 |含义 | |--|--| |malloc | 分配指定大小的空间,初始值不确定。 | |calloc | 分配指定大小的空间,初始化为0。 | | realloc | 增加或者减少已分配区的长度,增加长度时,可能意味着原先分配内容移动到一个更大区域。 |
realloc函数使得能够增减已分配的存储区的长度。对于增加存储区的长度,若原先存储区所在区域之后有足够的空间,那么realloc修改存储区长度等信息,返回原先地址;若原先存储区之后空间不足,那么realloc分配一片更大的存储区,并将原先内容复制到新区域,同时返回新地址。
另外,大多数实际的分配空间比函数请求的空间大一些(需要一些元数据描述存储区)。通常在返回地址的前面部分安插一定的区域存放存储区的长度信息。这也解释了为什么free()只需要传递首指针即可,其会从前面部分读取到长度。
C程序的进程环境即UNIX系统进程控制的先决条件。参数表和环境变量并不由内核解释,相反,由需要的应用程序进行使用。shell是一个典型的应用进程,其使用一定的环境变量以执行相应的过程。进程环境是对一个程序在某个数据集上执行过程所处环境的描述,它们是一个程序执行的先决条件。