进程
进程概念
在没有操作系统之前,计算机只能运行一个程序,全部的资源都属于当前运行的程序。配置了操作系统后,引入了多道程序设计的概念,进程可以合理的隔离资源和运行环境、提升资源利用率。
- 进程是系统进行资源分配和调度的基本单位;
- 进程作为程序独立运行的载体保障程序正常执行;
- 进程的存在使得操作系统资源的利用率大幅提升;
进程实体
主存中的进程是一个连续的存储空间,称为进程控制块(PCB)。进程控制块是用于描述和控制进程运行的通用数据结构。进程控制块中存有进程状态、优先级、程序计数器、内存指针、上下文数据、IO状态信息、记账信息等多个进程当前状态和控制进程的信息:
- 标识符:唯一标记一个进程,用于区别其他进程;
- 状态:标记进程的运行状态
- 程序计数器:指向进程即将被执行的下一条指令的地址;
- 内存指针:程序代码、进程数据相关的指针;
- 上下文数据:存储进程执行时处理器中的数据;
- IO状态信息:被进程IO操作所占用的文件列表;
- 记账信息:存储进程使用处理器的时间、时钟数总和等信息;
除了上面的内容,进程还有一些其他信息。所有信息可以分为四大类:进程标识符、处理机状态、进程调度信息、进程控制信息。进程控制块使得进程是能够独立运行的基本单位,操作系统通过进程控制块信息来调度和控制进程。进程控制块是常驻内存的,存放在系统专门开辟的PCB区域内。
进程与线程
进程(Process)和线程(Thread)是一对多的关系,一个进程内可以有一个或多个线程。
进程是系统进行资源分配和调度的基本单位,而线程是操作系统进行运行调度的最小单位。线程包含在进程之中,是进程中实际运行工作的单位。一个进程可以并发多个线程,每个线程执行不同的任务。
进程拥有资源,而线程不拥有资源,进程内部的线程共享进程资源。
进程 | 线程 | |
---|---|---|
资源 | 资源分配的基本单位 | 不拥有资源 |
调度 | 独立调度的基本单位 | 独立调度的最小单位 |
系统开销 | 进程系统开销大 | 线程系统开销小 |
通信 | 进程IPC | 读写同一进程数据通信 |
五状态模型
- 就绪状态:
当进程被分配到除CPU以外的所有必要资源后,就处于就绪状态。只要再获得CPU的使用权,就可以立即运行。在一个系统中多个处于就绪状态的进程通常排成一个队列,称为就绪队列。
- 执行状态:
进程获得CPU,其程序开始执行,这个状态为执行状态。在单处理机中,某个时刻只能有一个进程是处于执行状态。
- 阻塞状态:
进程因为某种原因,比如其他设备未就绪而无法执行,从而放弃CPU的状态称为阻塞状态。和就绪队列一样,操作系统也有阻塞队列,存放所有阻塞的进程。
- 创建状态:
进程创建分为两步,第一步分配PCB,第二步插入就绪队列。创建进程时拥有PCB但其他资源尚未就绪的状态称为创建状态。无论是系统创建的进程还是用户创建的进程都是一样的,操作系统提供fork接口创建进程。
- 终止状态:
进程终止也分为两步:首先进行系统清理,然后进行PCB归还。进程结束由系统清理或者归还PCB的状态称为终止状态。
进程同步
生产者-消费者模型
有一群生产者进程在生产产品,并将这些产品提供给消费者进程进行消费。生产者进程和消费者进程可以并发执行,在两者之间设置了一个具有n个可缓冲区的缓冲池,生产者进程需要将所生产的产品放到其中一个缓冲区中,消费者进程可以从缓冲区中取走产品进行消费。
生产者消费者模型在宏观上是没有问题的,当生产一个产品时缓冲区+1,当消费一个产品时缓冲区-1。但是计算机系统中是有问题的。计算机中的缓冲区位于高速缓存Cache上,生产者或消费者如果要操作缓冲区需要三个步骤:
- 把缓冲区中的信息取出来放入寄存器:register = count
- 如果生产register+1;如果消费register-1
- 把寄存器信息放回缓冲区里:count = register
这三个步骤但从生产者或消费者程序看没有问题,但两者并发执行时就会出差错:
哲学家进餐问题
有五个哲学家,他们的生活方式是交替的进行思考和进餐。哲学家们共同使用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五支筷子。平时哲学家们只进行思考,饥饿时则试图取靠近他们的左、右两支筷子,只有两支筷子都被拿到时才能进餐,进餐完毕后分别放下左、右的筷子继续思考。
通常的情况下,假设一个哲学家饿了,首先拿起左边的筷子,如果发现右边的筷子被拿走,则需要等待右边的筷子释放,释放后才能拿起筷子吃饭。但有一种极端情况:五个哲学家同时拿起左边的筷子,此时发现他们右边的筷子都被拿走,所以五个哲学家都需要等待右边筷子释放,最终的结果就是五个哲学家被饿死。
进程同步
上面两个问题是操作系统中的经典问题,问题的根源在于:彼此之间没有通信。如果生产者通知消费者已经完成了一件生产,或者哲学家向旁边的哲学家说我要进餐了,就可以避免这些问题。
进程的同步用于解决这种对竞争资源在多进程间进行使用次序的协调,使得并发的多个进程之间可以有效使用资源和相互合作。
进程同步原则
临界资源指的是一些虽然作为共享资源却无法同时被多个线程共同访问的共享资源。当有进程在使用临界资源时,其他进程必须依据操作系统的同步机制等待占用进程释放该共享资源才可重新竞争使用共享资源。为了对临界资源进行约束,提出了进程同步的四个原则:
- 空闲让进:资源无占用时允许进程使用;
- 忙则等待:资源有占用,其他请求进程需要等待;
- 有限等待:保证有限等待时间能够使用资源;
- 让权等待:进程等待时,需要让出CPU;
线程同步
和进程同步一样,一个进程内有多个线程并发使用,也会产生生产者-消费者问题和哲学家进餐问题,所以进程内的多线程也需要同步。
Linux的进程管理
进程类型
Linux下进程分为三种类型:前台进程、后台进程、守护进程。
- 前台进程:
Linux下有一个重要概念,就是终端Shell,我们使用命令行来使用Linux时就是通过终端Shell来实现。前台进程就是具有终端Shell,可以和用户交互的进程。前台进程占用终端Shell但不一定有输出。
- 后台进程:
和前台进程相对,没有占用终端的就是后台进程。后台进程基本上不和用户交互,优先级比前台进程低。Linux下将需要执行的命令以“&”符号结束来启动后台进程。
- 守护进程:
守护(daemon)进程是特殊的后台进程。很多守护进程在系统引导的时候启动,一直运行到系统关闭。Linux下进程名字以d结尾的进程一般都是守护进程,比如定时任务守护进程crond、http服务守护进程httpd、ssh登录的守护进程sshd、数据库守护进程mysqld。
进程标记
- 进程ID:
进程的唯一标识符,每个进程拥有不同的ID。进程ID表现为一个非负整数,最大值由操作系统限定。
操作系统提供fork接口创建进程,进程可以多层创建,这些进程是父子进程的关系。进程父子关系可以用pstree命令查看。
需要记住几个特殊的进程:ID为0的进程为idle进程,是系统创建的第一个进程;ID为1的进程为init进程,是0号进程的子进程,完成系统的初始化。init进程也是所有用户进程的祖先进程;
- 进程标记:
和Windows系统类似,Linux下的进程也有不同的进程状态,我们可以通过man ps命令来查看进程状态的所有标记,Linux进程状态很多,常见的有以下几种:
状态符号 | 状态说明 |
---|---|
R | (TASK_RUNNING),进程正处于运行状态 |
S | (TASK_INTERRUPTIBLE),进程正处于睡眠状态 |
D | (TASK_UNINTERRUPTIBLE),进程正处于IO等待的睡眠状态 |
T | (TASK_STOPPED),进程正处于暂停状态 |
Z | (TASK_DEAD or EXIT_ZOMBIE),进程正处于退出状态,或僵尸进程 |
Linux操作进程命令
1 | + ps命令:用于显示进程信息的常用命令,常用选项如下: |
1 | + top命令:系统监视工具,查看进程状态,常用选项如下: |
1 | kill命令:给进程发送信号时使用 |
进程同步方法
- 消息队列
- 共享存储
- 信号量
线程同步的方法:
- 互斥量
- 读写锁
- 自旋锁
- 条件变量