一、进程定义
进程是一个可执行程序的实例;从内核角度看,进程由用户内存空间和一系列内核数据结构组成,其中用户内存空间包含了程序代码及代码所使用的变量,而内核数据结构则用于维护进程状态信息,记录在内核数据结构中的信息包括如下:
- 与进程相关的标志号(IDs)
- 虚拟内存表
- 打开文件的描述符表
- 信号传递及处理的有关信息
- 进程资源使用及限制
- 当前工作目录
- 大量其他信息
运行进程相关的信息均在/proc/PID/xxx中可以查到
二、进程号
系统中每个进程都有一个唯一标识自己的进程号(PID,pid_t类型),可使用getpid()获取调用进程的进程号,每个进程都有一个自己的父进程,可以使用getppid()获取当前调用进程的父进程PID。所有进程一直往上找自己的父进程,都可以找到所有进程的鼻祖init进程(PID=1)。当父进程终止,其子进程就会变成孤儿进程,init进程就会收养该进程。
进程号分配原则
Linux内核显示进程号需要小于32767,新进程创建时,内核会按照顺序将下一个可用的进程号分配给新的进程使用,每当进程号达到32767的限制时,内核将重置进程号计数器,以便从小整数开始分配,一般重置为300,因为底数值的进程号长期被系统进程和守护进程长期占用,在此范围内搜索尚未使用的进程号只会浪费时间。最大进程号可以通过/proc/sys/kernel/pid_max文件来控制。
三、进程内存分段
c语言编程环境提供了3种全局符号:etext、edata和end,可以允许在程序内使用这些符号以获取相应程序的text段、data段和bss段结尾处下一个字节的地址,必须显示:extern char etext, edata, end;
其中,函数局部变量、参数及返回值均属于stack中。
四、进程虚拟内存管理
Linux像大多数现代内核一样,采用虚拟内存管理技术,该技术利用了大多数程序的一个典型特征,即访问局部性,以求高效利用CPU和RAM(物理内存)资源,大多数程序都展现了两种类型的局部性:
- 空间局部性:指程序倾向于访问最近访问过的内存地址附近的内存
- 时间局部性:指程序倾向于在不就的将来再次访问最近刚访问过的内存地址(循环)、
正是由于程序局部性,使得程序仅有部分内存地址空间存在于RAM中,依然能够正常执行。
虚拟内存技术将每个进程使用的内存切分成小型的、固定大小的“页(page)”单元,将RAM划分成一系列与虚拟内存页尺寸相同的“页帧”。任何时刻,每个进程仅有部分页需要驻留在物理内存页帧中,这些驻留的页构成了驻留集,进程未使用的页拷贝保存在交换区内----这是磁盘空间的保留区域,作为计算机RAM的补充,仅在需要的时候才会载入物理内存。若进程访问的页面目前尚不存在于物理内存,将会发生页面错误,内核会挂起进程的执行,同时从磁盘中将该页面载入内存。
为了满足上述的功能,内核需要为每个进程维持一张页表(page table),页表描述了每页在进程虚拟地址空间的位置(可为进程所有的虚虚拟内存页面的集合)。页表中的每个条目要么可以指出一个虚拟页面在RAM中的实际位置,要么表明其当前驻留在磁盘上。
虚拟内存的实现,需要硬件中分页内存管理单元(PMMU)的支持,将每个虚拟内存地址转换成相应的物理内存地址,当特定虚拟内存地址所对应的页没有驻留在RAM中,将以页面错误通知内核。
虚拟内存地址空间与物理地址空间隔离优点
- 进程与进程、进程与内核相互隔离,一个进程不能读取或者访问另外一个进程或者内核的内存。这是因为每个进程的页表条目指向RAM(或交换区)中截然不同的物理页面集合。
- 适当情况下,多个进程能够共享内存。这是由于内核可以使不同进程的页表条目指向相同的RAM页,例如同一程序的多进程共享代码副本;或者调用mmap或者shmget系统调用显示请求与其他进程共享内存区,这有利于进程间通信。
- 因为需要驻留在内存中的仅是程序的一部分,所以程序加载都很快,且允许进程所占用的内存能够超出RAM容量。
- 程序员和编译器、链接器之类的工具无需关注程序在RAM中的物理布局。
五、进程的环境变量
每个进程都有相关联的环境列表(Environment List),环境列表由多个环境字符串组成。环境字符串被定义成name=value格式。name叫做环境变量(Environment Variable)。当一个进程被创建时,该进程继承父进程的环境列表。这个特性可以作为父进程和子进程通信的一种方法。比如,当父进程要创建子进程时,可以先设置某个环境变量,子进程随后在自己的环境列表中读取该环境变量。不过这种通信是单向的(one-way)和一次性的(once-only),子进程在创建后,就完全拥有和父进程独立环境列表了。程序可以通过检查环境变量来改变程序的特性(像使用命令行参数一样)。
环境变量可以通过/proc/PID/environ文件检查指定进程的环境列表,每一个“NAME=value”对都以空字节终止。
在程序中访问环境:
(1)通过全局变量char **environ。environ和argv很相似
(2)也可以通过int main(int argc, char *argv[], char *envp[])中的envp来访问。
(3)使用API函数获取环境变量
参考:
深入理解linux内存管理之 页表管理- https://blog.csdn.net/u011209099/article/details/9248525