第六章 进程
- 进程的定义
从内核角度看,进程由用户内存空间(user-space memory)和一系列内核数据结构组成,其中用户内存空间包含了程序代码及代码所使用的变量,而内核数据结构则用于维护进程状态信息。记录在内核数据结构中的信息包括许多与进程相关的标志号(IDs),虚拟内存表,打开文件的描述符表,信号传递及处理的有关信息,进程资源使用及限制,当前工作目录和大量的其他信息。
- 使用ps, top等命令可查看进程信息
- 进程内存布局
每个进程所分配的内存有很多部分组成,通常称之为“段(segment)”
- 文本段:进程运行的程序机器语言指令,因为多个程序可同时运行同一程序,所以将文本段设为可共享,这样,一份程序代码的拷贝可以映射到所有这些进程的虚拟地址空间
- 初始化数据段:显示初始化的全局变量和静态变量,
- 未初始化数据段:未进行显示初始化的全局变量,静态变量。此段常被称为BSS段“block started by symbol”。
- 栈:动态增长和收缩的段
- 堆:是可在运行时(为变量)动态进行内存分配的一块区域。4
使用size可查看可执行文件的内存信息
- 典型的进程内存结构
- 虚拟内存管理技术
- 典型特征:访问局部性(locality of reference),以求高效使用CPU和RAM(物理内存)资源。
- 空间局部性(Spatial locality)
- 时间局部性(Temporal locality)
- 驻留集(resident set)
- 交换区 (swap area)
- 页面大小
- 进程
- 虚拟内存的实现硬件中分页内存管理单元(PMMU)的支持4
虚拟内存管理使进程的虚拟地址空间与RAM物理地址空间隔离开来,有很多优点
- 进程与进程,进程与内核相互隔离
- 适当情况下,两个或者更多进程能够共享内存。
- 便于实现内存保护机制
- 程序员和编译器,链接器之类的工具无需关注程序在RAM中的物理布局
- 因为需要驻留在内存中的仅是程序的一部分,所以程序的加载和运行都很快
- 栈和栈帧
- 专用寄存器:栈指针(stack pointer)同于跟踪栈顶
- 用户栈
- 内核栈
- 命令行参数
- argc
- argv
- /proc/PID/cmdline
- environ
- sysconf
ARG_MAX的开销
- 终止空字符
- 字节对齐
- argv
- envirom指针数组
- 环境列表
- 新进程在创建之时,会继承其父进程的环境副本
- /proc/PID/environ:文件检查任一进程的环境列表
- extern char **environ
- int main(int argc, char *argv[], char *environ[])
- getenv能从环境中检索单个环境变量值
- 执行非局部跳转:setjump()和longjump()
- 使用库函数setjump()和longjump()可执行非局部跳转
- goto语句:潘多拉魔盒
- 通过查看setjump()返回的整数值,可以区分setjump调用是初始返回还是第二次“返回”。
- 程序计数寄存器
- 栈指针寄存器
SUSv3和C99规定,对setjmp()的调用只能在如下语境中使用
- 构成选择或迭代语句中(if,switch, while等)整个控制表达式
- 作为一元操作符!(not)的操作对象,其最终表达式构成了选择或迭代语句的整个控制表达式
- 作为比较操作(==, !=,<)的一部分。
- 作为独立的函调用
优化编译器的问题:
优化编译器会重组程序的指令执行顺序,并在CPU寄存器中,而非RAM中存储某些变量。这种优化一般依赖于反映了程序词法结构的运行时(run-time)控制流程。
此外,某些应用程序二进制接口(ABI)实现的语义要求longjump()函数恢复先前setjmp()调用所保存的CPU寄存器副本。这意味着longjmp()操作会致使进过优化的变量被赋以错误值。