跳到主要内容

程序的运行

本节例

本节我们用下面这个程序来举例:

main.cpp
#include <iostream>
#include "name.h"

int main(){
std::cout << name() << std::endl;
return 0;
}
name.h
#include <string>
string name(){
return "wangenius";
}

程序加载

编译

  1. 预编译: 将宏替换, 注释删除, 内联函数替换等等.
  2. 编译:生成可执行代码,产生若干目标文件代码块Object Module. 具体查看编译原理部分.
程序编译的4个步骤
  • 预处理阶段 gcc -E test.c -o test.i 预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中。
  • 编译阶段 gcc -S test.c 编译完成之后就停下来,结果保存在test.s中。
  • 汇编阶段 gcc -c test.c 汇编完成之后就停下来,结果保存在test.o中。
  • 链接阶段 生成可执行文件,链接器将安排目标代码的各个部分,使某些部分的功能能够成功地调用其他部分的功能。它还将添加包含程序所使用的库函数指令的片段。

编译就是将我们编写的源代码“翻译”成计算机可以识别的二进制格式,它们以目标文件的形式存在

链接就是一个“打包”的过程,它将所有的目标文件以及系统组件组合成一个可执行文件。

链接

C语言代码经过编译以后,会变成了二进制形式的目标文件(Object File)--- 对于 Visual C++,目标文件的后缀是.obj,对于 GCC,目标文件的后缀是.o。但此时的代码还不能运行起来。因为它还需要和系统提供的组件(比如标准库)结合起来,这些组件都是程序运行所必须的。例如我们要在屏幕中输出字符,这必须调用系统提供的库才能够实现。

这就是链接,经过链接才会生成 可执行程序 (如 win 平台上的exe)目标文件链接形成完整的装入模块Load Module(可执行文件)

  1. 静态链接:运行前连接成一个完整的exe文件
  2. 装入时动态链接:边装入边链接
  3. 运行时动态链接:对某些目标模块链接,需要才链接,便于修改、更新、共享。与虚拟内存相关。

装入

装入:将装入模块装入内存 ,地址重定位的过程。

  1. 绝对装入:compile时绝对定位,不适用于批处理操作系统
  2. 静态重定位:load时进行定位,在装入时一次性完成地址变换,必须分配要求的全部内存空间,不能移动,适用于分区分配。不需要MMU
  3. 动态重定位:execute时重定位,内存中的所有地址都是相对地址,可以将程序分配到不连续的存储区中,适用于分页,分段分配内存。需要附加硬件重定位寄存器的支持。

进程

进程是对操作系统正在运行的程序的一种抽象。是进程实体的动态(最基本的特征)运行过程,进程是能独立运行、独立获得资源(进程是资源分配,拥有资源的基本单位)、独立接受调度(通常使用线程完成)的基本单位.

进程控制块

进程存在的唯一标志.系统通过PCB对进程进行控制

  1. 标识符(PID,UID...)
  2. 分配资源信息(内存,IO,文件)
  3. 控制和管理信息(当前状态、优先级、代码运行入口地址、外存地址、信号量使用、CPU使用时间,磁盘使用情况,网络流量使用情况等)
  4. 处理机相关信息(CPU上下文、中断现场,通用寄存器值、地址寄存器值、控制寄存器值、标志寄存器值等)
idle

空闲进程: 优先级最低,不需要CPU之外的资源,不会被阻塞

并发性

任何时刻,单处理器只能执行一个进程的代码。

线程

减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。基本的CPU执行单元,是程序执行流的最小单元。

  1. 线程是基本的CPU执行单元,是程序执行流的最小单元,由线程ID,程序计数器PC,寄存器集合以及堆栈组成。
  2. 提升了并发度。对于单线程进程,只能运行在一个处理机上,对于多线程进程可以将进程中的多个线程分配到多个处理机上执行;
  3. 调度不需要进行CPU上下文切换,时空开销小,引入线程的操作系统中,线程是独立调度的基本单位。
  4. 进程并发需要切换进程的运行环境,系统开销很大,线程间并发,如果是同一进程内的线程切换,则不需要切换进程环境,系统开销小。
  5. 线程无法拥有资源,进程是拥有资源的基本单位,同一个进程的所有线程共享该进程的所有资源。

TCB:标识符、寄存器组(PC,PSW,Xs)、运行状态、优先级、线程专有存储区、堆栈指针(用于过程调用时保存局部变量和返回地址)

线程之间的区别

自己做的一个原模型太过复杂, 仅供参考.

软件线程

内核级线程

操作系统内核空间管理线程。可以发挥多处理机内核的优势,同时调度同一进程中的多个线程并行运行。一个内核级线程由于IO操作阻塞时,不会影响其他线程的运行。处理器分配时间片的对象是线程,所以有多个线程的进程将会获得更多的处理器时间。线程切换开销小,但是系统开销大,一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到内核态,线程管理成本高,开销大

用户级线程

效率高,用户级代码级线程,由用户实现,与OS无关,线程阻塞则进程阻塞,不能发挥多处理机的优势。内核也并不知道它的存在,所以所有对用户级线程的管理和调度都是在用户空间来进行的。不仅无需通过中断进入操作系统的内核,而且切换的规程也远比进程调度和切换的规则来得简单,用户级线程的切换速度特别快。但是一个用户级线程若执行了阻塞系统调用就会导致该线程所属的进程阻塞。此时如果采用的是内核级线程,则调度室以线程为单位。当一个线程调用一个系统调用时,内核把系统调用只看做是该线程的行为,因而封锁该线程,于是可以再调度该进程中的其他线程执行。

KLT和ULT的区别

主要区别是是否阻塞问题. 用户级线程直接由代码逻辑生成, 顺序执行下, 会发生阻塞. 而内核级线程是进程向操作系统申请分配的线程(如果有多处理机,可以同时调度占用). 因此不存在阻塞的问题.

组合模型

  1. 多对一模型(不使用内核线程): 多个用户线程对应一个内核线程. 线程管理在用户空间进行,因而效率比较高
  2. 一对一模型(不使用用户线程): 并发能力强,每创建一个线程,相应地就需要创建一个内核线程,管理开销大
  3. 多对多模型(既使用内核线程又使用用户线程): 以上两个模型的优点,用户线程大于内核线程

硬件线程

KLT和ULT属于软件线程,是在os层级以上的。 硬件线程则是在一个处理机上有两个或多个完整的寄存器集,允许它代表两个或多个进程(内核级线程)执行指令。

Loading Comments...