1.进程号
pid_t getpid();获取进程的进程号。
pid_t getppid()获取父进程的进程号。
Linux内核限制进程号需要小于等于32767。新进程创建时,内核会按顺序将下一个可用的进程号分配给其使用。每当进程号到达32767的限制时,内核将重置进程号计数器,以便从小整数重新开始分配。该分配方式具体如下:
一旦进程号到达32767,内核会将进程号计数器重置为300,而不是1。之所以如此,是因为低数值的进程号为系统进程和守护进程长期占用,在此范围内搜索尚未使用的进程号只会是浪费时间。
在Linux 2.4版本及更早版本中,进程号的上限是32767,由内核常量PID_MAX所定义。在Linux 2.6版本中,情况有所改变。尽管进程号的默认上限仍是32767,但是可以通过Linux系统特有的/proc/sys/kernel/pid_max文件来进行调整(其值=最大进程+1)。在32位平台中,pid_max文件的最大值为32767,但是在64位平台中,该文件的最大值可以高达2^22次方(约400万),系统可容纳的进程数量会非常庞大。
2.进程内存布局
文本段,只读,防篡改,包含程序机器语言指令。
未初始化数据段(bss,block started by symbol),包含未进行初始化的全局变量和静态变量,程序启动前系统会将它们初始化为0。系统认为不需要给未初始化变量分配空间,直到运行时才为它们分配存储空间。
初始化数据段,包含显式初始化的全局变量和静态变量。
栈,动态增缩的段,由栈帧组成,存储局部变量、实参、返回值。
堆,运行时动态进行内存分配的区域。
3 应用程序二进制接口ABI
规定了二进制可执行文件在运行时应如何与某些服务(如内核或函数库提供的服务)交换信息。ABI规定使用哪些寄存器和栈地址来交换信息以及交换值的含义,一旦针对某一个特定ABI进行了编译,其二进制可执行文件能在ABI相同的任何系统上运行,而API仅能通过编译源代码来保证应用程序的可移植性。
4 虚拟内存管理
将每个程序使用的内存分割成小型的固定大小的页,x86-32中页面大小为4096字节,相应将RAM划分为与虚存页尺寸相同的页帧。任一时刻,每个程序仅有部分页必需驻留在物理内存页帧上,这些页构成驻留集。程序未使用的页帧保存在交换区,这是磁盘空间的保留区域,作为RAM的补充,尽在需要时才会载入物理内存,若进程欲访问的页面未驻留在物理内存上,将导致页面错误,内核即刻挂起进程,同时从磁盘中将该页载入内存。
内核为每个进程维护一张页表,描述每页在进程虚拟地址空间中的位置,页表每一个条目指出一个虚拟页面在物理内存RAM中的位置或者表明其当前驻留在磁盘上。
虚拟内存管理的优点:
将进程的虚拟内存空间和RAM物理地址空间隔离。
将进程的虚拟内存空间和RAM物理地址空间隔离。
a进程与进程,进程与内核相互隔离,一个进程不能随意读取或修改另一个进程或内核的内存,因为每个进程的页表条目指向不同的RAM或交换区中不同的物理页面集合。
b适当情况下,进程间可以共享内存,只需把不同进程的页表条目指向相同的RAM页。
c便于实现内存保护机制,只需对进程页表条目进行标记就可以表示相关页面的内容可读、可写。
d程序员、编译器、连接器之类的工具无需关注程序在RAM中的物理布局。
e需要驻留内存的仅是程序的一部分,所以程序加载运行都很快,而且一个进程所占内存能超过RAM容量。
f每个进程使用的RAM少了,RAM就可以同时容纳更多进程,任一时刻都可以执行一个进程,提高cpu的利用率。
5 栈和栈帧
栈的增长方向向下,不过栈的增长方向是一个实现细节,有的系统是向上。
每个栈帧包括以下信息:
函数实参和局部变量。
函数调用的连接信息:cpu寄存器如程序计数器指向下一条将要实行的及其语言指令。
因为函数可以嵌套调用,所以栈中可能含有多个栈帧。
6 环境列表
每个进程都有一个与其相关的环境列表的字符串数组,简称环境,每个字符串都以name=value形式定义。
新进程创建时会继承父进程的环境副本,此后双方对各自互不可见。
环境变量常见用途:shell通过自身环境中放置变量,就能把它传递给创建的新进程。
可以通过设置环境变量来改变一些库函数的行为,无需改变程序代码。
设置一个环境变量export SHELL=/bin/bash
撤销一个环境变量用unset
程序中可用extern char** environ;获取环境变量。
也可以用char* getenv(const char* name);获取对应名称的环境变量。
添加一个环境int putenv(char* string);
清空环境int clearenv(void);
7 执行非局部跳转
c语言中goto不能从当前函数跳转到另一函数,如从函数调用中跳转到函数调用者之一。
setjmp()和longjmp()实现了这一功能。
#include<setjmp.h>
int setjmp(jmp_buf env);首次调用返回0,后续的返回值是longjmp()中的val参数,由此分辨起跳位置的不同。
void longjmp(jmp_buf env,int val);val不能为0,longjmp会把0替换成1.
尽可能避免使用goto、setjmp、longjmp,难以阅读。