UNIX环境高级编程-进程的运行时环境和运行时资源

Chapter 7-9

[TOC]

Runtime Environment

当系统启动时,内核代码被加载到内存,初始化后启动第一个用户进程,然后内核的代码就等着用户进程来调度了。

代码在编译之后会生成一个可执行程序。运行程序其实是用户进程(Shell进程)指示内核要启动另一个用户进程,内核便为这个新的进程分配资源,并加载该进程的代码和数据。

一个程序可以被运行多次,除非发生资源抢占可能会发生启动失败。

Runtime Resources

PCB

进程运行时,内核为进程每个进程分配一个PCB(进程控制块),描述进程的详细信息。

PCB在内核中对应的结构体是task_struct

Virtual Memory

每个进程都会分配虚拟地址空间,在 32 位机下,该地址空间为 4G 。

而在 64 位机下,其虚拟地址只使用了 48 位。所以C程序里,打印的地址都是只有 12 位 16 进制 ,最高为FFFF FFFF FFFF。由于当前 AMD 和 Intel 的 x64 CPU仅实现了 16 EB 虚拟地址空间中的 256 TB。换言之,一个 64 位的虚拟地址中,仅有低 48 位被实现(使用)。然而,虚拟地址仍旧是 64 位宽,在寄存器中,或存储在内存中,它们都占用 8 字节。

虚拟地址中的高 16 位(比特位 48~63)需要被设置成与最高的“实现位”(也就是比特位 47)相同的值,这是通过一种类似于二进制补码运算的符号扩展来完成的。而 48 位的虚拟地址转换成物理地址需要 4 级页表。

另外目前 64 位CPU支持的物理内存最大为64T;
而 32 位CPU最大支持的物理内存为64G(开启PAE选项)。

    ffff ffff`ffff ffff    ______________
                          |              |
                          | Kernel Space |
    ffff 8000`0000 0000   |______________|
                          |              |
                          | Unused Space |
    0000 7fff`ffff ffff   |______________|
                          |              |
                          | User   Space |
    0000 0000`0000 0000   |______________|

在进程中,指针变量保存的就是虚拟地址。当应用程序使用虚拟地址访问内存时,CPU中的地址加法器会将其转化成物理地址。

这样做的好处在于:

  • 进程隔离,更好的保护系统安全运行
  • 屏蔽物理差异带来的麻烦,方便操作系统和编译器安排进程地址

CPU

CPU的分配是动态的,不是进程一加载就直接分配的,一般来说每个系统都会有许多进程同时在运行,而CPU只有一个(多核CPU可以认为是多个,但是数量远少于进程数量)。那么,进程就需要排队等待,就好像有100个人,在4个卖饭的窗口买饭一样。

内核将进程PCB放入一个队列,总是让CPU服务来队列中的第一个进程,服务时间可以是10微秒(us),也可以是15毫秒(毫),具体多长时间跟实际的情况有关系,这个时间有个名字叫做时间片。一旦这个进程服务时间到(或时钟中断),这个进程会被丢到队列尾部,进行排队,称为进程调度。

时钟中断由系统定时硬件以周期性的间隔产生,这个间隔由内核根据 HZ 值来设定,HZ 是一个体系依赖的值,在 <linux/param.h>中定义或该文件包含的某个子平台相关文件中。

内核中有一个常量HZ,一般是1002501000

对用户空间,内核 HZ 几乎完全隐藏,用户 HZ 始终扩展为 100

当用户空间程序包含param.h,且每个报告给用户空间的计数器都做了相应转换。对用户来说确切的 HZ 值只能通过 /proc/interrupts 获得:/proc/interrupts 的计数值除以 /proc/uptime 中报告的系统运行时间。