计算机是完成特定任务的工具,我们把特定的任务称为task。最早的计算机是单task系统,需要手动装载,启动task的程序,所有的资源由该task独占,一个task完成后才能启动下一个task。操作系统的一个重要功能是管理task,为task分配系统资源。进程是操作系统分配资源和调度task的基本单位。
进程是一个具有一定独特功能的程序在一个数据集合上的一次动态执行。进程由代码块(静态,二进制),数据集,进程控制块(动态)三部分组成。进程概念的引入是为了便于理解操作系统对task管理。进程是操作系统为task分配系统资源的基本单位,也是task调度的基本单位。
线程是CPU计算流的抽象,一般认为它由程序计数器(PC),栈和寄存器组构成。从操作系统的角度来看,线程使资源和计算流分离,多个线程可以共享系统资源,实现了系统资源(内存,文件,信号)的高效利用。同时,这种共享也实现了计算流之间更高效的交互,实现更高的并行和并发度。
POSIX的使用
demo
|
|
互斥锁
互斥锁是为了保证在同一时刻只有一个线程操作(读写)共享资源。POSIX API有以下几个:
|
|
条件变量
条件变量用于同步,即在达到某种条件时线程才继续运行,否则挂起等待,直到满足条件后被唤醒。线程一般通过检查某个共享变量的值来确定是否要挂起等待,所以条件变量一般与互斥锁结合实现同步机制。条件变量相关的 POSIX API如下:
|
|
这里需要主要的是,一个条件变量同一时刻只能与一个互斥量配套使用,而一个互斥量可以同时与多个条件变量对应。
线程模型
逻辑上,一个进程对应一组线程,这是一个典型的一对多的模型。但是从实现上,又有几种不同的模型,常见的有一对一,一对多,多对多几种。
在介绍线程模型前,必须清楚两个概念:用户级线程(ULT)和内核级线程(KLT)。ULT,顾名思义,运行于用户态的线程,对操作系统的内核是不可见的,用户自行管理线程的调度。内核级线程,运行于内核态的线程,内核可见,内核参与线程调度。
一对一
一个KLT对应一个ULT,早期的POSIX实现版本Linux Threads就是根据这个模型实现的。它利用Linux的轻量级进程(LWP,通过clone()系统调用)实现不同进程间的资源共享。有专门的管理线程负责线程调度。
多对一
一个KLT对应多个ULT。缺点是:如果一个线程在系统调用上阻塞,整个进程阻塞。
多对多
多个KLT对应多个ULT,一对一和一对多的结合,实现更高的并行性。IBM的NGPT是基于这一模型实现的。