为什么要引入进程?
多道程序环境下,程序之间因共享资源而相互制约着运行,因此体现出间断性的特征。
传统的程序是一组指令的集合,体现不出其在内存中的运行情况(间断性导致的何时停顿、何时运行)。
因此引入了进程的概念。
进程是怎么解决问题的?
进程把能识别程序运行态的一些变量放在PCB中,通过这些变量能够清晰的了解进程的状况,并在适当时刻进行进程的调度与切换。
进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。
这里的资源指的是处理机、存储器和其他设备服务于某个进程的“时间”。
进程标识符:进程唯一的标识号
用户标识符:进程归属用户,主要为共享和保护服务
资源分配清单:说明有关内存地址空间或虚拟地址空间的状况、打开的文件列表和使用的I/O设备信息
处理机相关信息(处理机上下文):主要保存各寄存器的值
程序在装入内存后一般分为三个段:
- 正文段(代码和赋值数据段):包含全局变量,常量值(123,"Hello")等。
- 数据栈段:包含局部变量和传递的参数等。
- 数据堆段:malloc()动态分配的存储区等。
进程的特征:
- 动态性:进程是程序的一次执行,是动态地产生、变换和消亡的。动态性是进程最基本的特征。
- 并发性:多个进程实体同存于内存之中。
- 独立性:只有建立了PCB,才能作为一个进程实体独立运行、独立地获得资源和独立地接受调度单位。
- 异步性:由于进程的相互制约,使得进程按各自独立的、不可预知的速度运行。异步性会导致执行结果的不可再现性,为此在OS中需要配置进程同步机制。
进程状态
创建态:进程正在被创建——首先申请一个空白PCB,在其中填写用于控制和管理进程的信息;然后为该进程分配运行时必需的资源;最后把PCB插入就绪队列。
系统通常根据阻塞原因将处于阻塞态的进程排成不同的阻塞队列。
进程组织
进程控制
对进程的管理和控制是通过执行各种原语来实现的。
PCB是有限的。
如果为新进程分配所需资源失败,则并不是创建失败,而是处于创建态,等待资源分配。
进程通信
进程间通信 (Inter-Process Communication,IPC)
进程空间一般是独立的,进程运行期间一般不能访问其他进程的空间,想让两个进程共享一段空间,必须通过系统调用来实现。
线程是自然共享进程空间的。
消息传递是应用最广泛的进程间通信机制。微内核与服务器之间的通信就采用了消息传递机制;该机制也很好地支持多处理机系统、分布式系统和计算机网络。
优点:隐藏了通讯实现细节,使通信过程对用户透明,简化了通讯程序的设计。
信箱并不独属于某个进程,是共享的。
线程
引入线程的目的是为了减少程序在并发执行时所付出的时空开销,提高并发率。
线程即“轻量级进程”
线程与进程相比
- 线程的切换开销远小于进程
进程的切换除了线程切换的开销外,还包括TLB缓存失效以及部分L1 L2 cache失效的代价。
- 引入线程具有更好的并发性
对于传统的进程,不管有多少处理机,进程只能运行在一个处理机上。
对于多线程进程,可以将进程中的多个线程分配到多个处理机上运行。
除此之外,因为1.的原因,平均进程切换的代价降低了。
- 线程不拥有系统资源
进程是系统资源分配的基本单位,线程不拥有系统资源(除了TCB、寄存器值以及堆栈),但是线程们共享其进程的资源。
- 同进程的线程之间易通信与同步
同一进程的所有进程都完全共享进程的地址空间和全局变量。各个线程都可以访问进程地址空间的每个单元,所以一个线程可以读、写或甚至清除另一个线程的堆栈。
线程由如下三种基本状态:
- 运行态
- 就绪态
- 阻塞态
与进程一样,各线程之间也存在共享资源和相互合作的制约关系,致使线程在运行时也具有间断性。
线程的组织与控制
线程控制块(TCB,Thread Control Block)
- 线程标识符
- 寄存器值(PC、PSW、通用寄存器)
- 线程运行状态
- 优先级
- 线程专有存储器:线程切换时用于保护现场等
- 堆栈指针:用于过程调用时保存局部变量及返回地址等
线程创建
程序启动时,通常仅有一个称为“初始化线程”的线程正在执行,其主要功能是用于创建新线程。
创建线程需要调用操作系统提供的函数(或系统调用,看是用户级线程还是内核级线程而定),并提供相应的参数(如指向线程主程序的入口指针、堆栈的大小、线程优先级等)。
函数(或系统调用)执行完后,将返回一个线程标识符。
线程终止
线程运行完或出现异常而要被强制终止时,由“终止线程”用过调用函数(或系统调用)来执行线程终止。
有些线程(尤其是系统线程)一旦被建立,便一直运行而不会被终止。
通常,线程被终止后并不立即释放它所占有的资源,只有当进程中的其他线程执行了分离函数后,被终止线程才与资源分离,此时的资源才能被其他线程利用。
线程的实现方式
用户级线程
线程的调度算法可以是进程专用的,不同的进程可根据自身需要,对自己的线程选择不同的调度算法。
用户级线程的实现与系统平台无关。系统底层的调度仍是以进程单位的。
内核级线程
内核支持线程具有很小的数据结构和堆栈,线程切换的比较快,开销小(但由于涉及到内核态切换,所以相对于用户级开销还是比较大的,不过比进程切换开销小)。
线程的管理与调度都是由内核进行管理。
组合方式
有的系统,内核支持内核级线程的建立、调度与管理,同时允许用户程序建立、调度和管理用户级线程。
用户级线程通过时分多路复用内核级线程,使得一些内核级线程可以对应多个用户级线程。
根据内核级线程与用户级线程对应关系的不同,有三种不同的组合方式(多线程模型)。
多线程模型
“当一个用户级线程被阻塞后,整个进程都会被阻塞”,这句话是不准确的。
具体情况取决于内核对线程的支持情况。
例如Linux为Native Posix Thread Lib提供了内核方面的支持,用户级线程和内核级线程是1:1的,所以当某个线程阻塞时,当然可以切换到对应进程的其他线程工作,进程不会阻塞。
但在有些操作系统中,并没有提供内核级线程的实现,线程的调度其实就是进程的调度,线程没有办法提升到内核态,也无法利用多核特性。当线程阻塞后,对应进程也会阻塞。
所以这句话不应该作为多对一模型的缺点放在这里(可能是书中的错误),除非一个进程只能创建一个内核级线程(但这样内核级线程又有什么意义呢)。
或者此处想表达的意思是,一个用户级线程阻塞后,其他映射到相同内核级线程的用户级线程也阻塞(一个阻塞,全部阻塞)。但和进程阻塞没什么关系就是了。
又或者他默认该进程的所有线程都映射到这一个内核级线程上。
所以:多对一模型 其实就是 用户级线程
一对一模型 其实就是 内核级线程