程序运行环境

需掌握CPU运行模式以及程序的链接和装入方式,可能会在选择题中考察。

CPU运行模式

CPU的两种运行模式:内核模式(又称为特权模式、系统模式或超级用户模式)和用户模式是操作系统设计中用于隔离系统关键任务与普通任务的机制,以提高系统的安全性和稳定性。

  1. 内核模式(Kernel Mode):
    • 当CPU运行在内核模式时,它可以执行所有的指令集和直接访问所有的硬件资源。
    • 操作系统的核心部分(即内核)通常会在这种模式下运行。这允许内核进行如管理内存、任务调度、直接硬件访问等关键操作。
    • 如果程序(通常是设备驱动)在内核模式下出现错误或异常,往往会导致整个系统崩溃或出现蓝屏等严重故障。
    • 由于其对硬件的直接访问能力,恶意软件如果能在内核模式下运行,可能会对系统造成严重的损害。因此,确保只有受信任的代码能在内核模式下执行是非常重要的。
  2. 用户模式(User Mode):
    • 当CPU运行在用户模式时,执行的代码受到严格限制,不能直接访问硬件或执行某些特权指令。
    • 大多数应用程序都是在用户模式下运行的。这样做的好处是,即使应用程序出现问题或崩溃,也不会直接影响到整个系统的稳定性。
    • 要从用户模式访问硬件或执行其他特权操作,应用程序必须通过系统调用(system call)来请求操作系统提供的服务。这样的设计提供了一层保护,确保了用户程序不能直接干扰系统的关键部分。

系统调用

系统调用(system call)是运行在用户模式的应用程序与操作系统内核之间的接口。当应用程序需要执行一些它在用户模式下不能直接完成的任务(如文件操作、网络通信、创建进程等)时,它可以通过系统调用来请求操作系统内核在内核模式下为其执行这些操作。

特点

  • 特权级的转换:应用程序通常在用户模式下运行,而操作系统内核在内核模式下运行。系统调用提供了从用户模式到内核模式的一种安全的转换机制,这样内核可以代表应用程序执行特权操作。
  • 系统调用的类型:常见的系统调用类型包括进程管理(如创建、终止进程)、文件操作(如打开、读取、写入、关闭文件)、网络通信、设备控制、内存管理等。
  • 性能开销:执行系统调用涉及上下文切换,从用户模式到内核模式,然后再返回。这会带来一定的性能开销。因此,频繁地进行系统调用可能会影响应用程序的性能。

程序的链接

程序的链接是将编译后的代码模块(通常是目标文件)和其他所需的库组合在一起,生成一个可以执行的程序或库的过程。链接过程由链接器(linker)完成。

静态链接和动态链接

根据所使用的库的链接方式,链接可以分为静态链接和动态链接。

  • 静态链接:
    • 当使用静态链接时,外部代码和库在链接阶段被整合到最终的可执行文件中。这意味着,如果程序使用了某个库的函数,那么这些函数的代码会被复制到最终的二进制文件中。
    • 结果是一个较大的可执行文件,因为它包含了所有必要的代码以独立运行。
  • 动态链接:
    • 使用动态链接时,外部库不会被直接嵌入到最终的可执行文件中。相反,程序包含了对动态链接库(如Linux中的.so文件或Windows中的.dll文件)的引用。当程序启动时,这些库会被加载到内存中供程序使用。
    • 动态链接的库通常称为动态链接库(Dynamic Link Libraries,DLL)或共享对象(Shared Object
特点/链接方式静态链接动态链接
文件大小通常较大,因为库代码被整合到可执行文件中通常较小,只包含对库的引用
运行依赖不需要外部库文件需要相应版本的动态链接库文件
存储效率较低,每个程序都有库的一个副本较高,多个程序共享同一个库文件
更新便利性较差,更新库需要重新链接和分发程序较好,只需更新库文件
启动性能通常更快,无需加载外部库可能稍慢,需要加载外部库
跨版本兼容性较好,因为程序包含了特定版本的库代码可能出现问题,特别是当库接口发生变化时

程序的装入

程序的装入是指将程序或进程的代码和数据从磁盘加载到主存(RAM)中的过程,使其准备好被CPU执行。装入过程在程序执行周期中是必不可少的一部分,通常由操作系统中的装入器(loader)完成。

绝对装入

适用于单道程序环境。在编译时,若知道程序驻留在内存的某个位置,则编译程序将产生绝对地址的目标地址。绝对装入程序按照装入模块的地址,将程序和数据装入内存。由于程序中的逻辑地址与实际地址完全相同,因此不需要对程序和数据的地址进行修改。

另外,程序中所用的绝对地址,可在编译或汇编时给出,也可由程序员直接赋予。而通常情况下在程序中采用的是符号地址,编译或汇编时在转换为绝对地址。

可重定位装入

在多道程序环境下,多个目标模块的起始地址通常都从0开始,程序中的其他地址都是相对于起始地址的,此时应采用可重定位装入方式。根据内存的当前情况,将装入模块装入内存的适当位置。在装入时对目标程序中指令和数据地址的修改过程称为重定位,又因为地位变换通常是在进程装入时一次完成的,故称为 静态重定位

当一个作业装入内存时,必须给它分配要求的全部内存空间,若没有足够的内存,则无法装入。此外,作业一旦装入内存,整个运行期间就不能在内存中移动,也不能再申请内存空间。

LOAD 1, 6
Add      1,8
Store  1,10
A               
B               
LOAD 1,106
Add   1,108
Store 1,110
A               
B               
0
2
4
6
8
100
102
104
106
108
10
110
地址空间
存储空间

动态运行时装入

也称为 动态重定位。装入程序把装入模块装入内存后,并不立即把装入模块的相对地址转换为绝对地址,而是把这种地址转换推迟到程序真正要执行时进行。因此,装入内存后的所有地址都是相对地址。这种地址需要一个重定位寄存器的支持。

LOAD      500
12345
500
1000
+
LOAD      1.500
12345
0
100
500
1000
1500
地址空间
存储空间

动态重定位的优点在于可以将程序分配到不连续的存储区;在程序运行之前可以只装入部分代码即可运行,然后在程序运行期间,根据需要动态申请分配内存。