线程与进程(待补充)
进程
进程定义 :进程是一个正在执行的程序。进程是程序在一个数据集合上的运行过程,它是系统进行资源分配和调度的一个独立单位。
进程和程序相关联,但是是两个截然不同的概念。比如正在打开的高德地图就是一个进程,但高德地图不是进程。
进程与程序的区别?
- 程序是代码编译后的静态文件
- 进程是正在运行的动态实例
进程有五个基本特征:
- 动态性。有生命期。
- 并发性。多个进程实体同存于内存,能并发执行(注意和并行的区别)。
- 独立性。具备申请系统资源的独立单位。
- 异步性。进程以各自独立、不可预知的速度向前推进。
- 结构特性。为描述进程的运动变化过程,每个进程都由程序段、数据段和一个进程控制块(PCB)三部分组成
进程的状态
进程活动状态 :
- 运行状态。该时刻进程占用 CPU;
- 就绪状态。进程已处于准备运行状态,由于其他进程处于运行状态而暂时停止运行,即进程获得了除了处理器之外的一切所需资源,一旦得到处理器资源(处理器分配的时间片)即可运行
- 阻塞状态。又称为等待状态,进程正在等待某一事件而暂停运行如等待某资源为可用或等待 IO 操作完成,(如等待输入/输出操作的完成)而暂时停止运行,这时,即使给它CPU控制权,它也无法运行;
进程基本状态 :
- 创建状态(new):进程正在被创建,尚未到就绪状态。;
- 结束状态(Exit):进程正在从系统中消失。可能是进程正常结束或其他原因中断退出运行。
进程挂起状态:
在虚拟内存管理的操作系统中,通常会把阻塞状态的进程的物理内存空间换出到硬盘,等需要再次运行的时候,再从硬盘换入到物理内存。描述进程没有占用实际的物理内存空间的情况,就是挂起状态。
- 阻塞挂起状态:进程在外存(硬盘)并等待某个事件的出现;
- 就绪挂起状态:进程在外存(硬盘),但只要进入内存,即刻立刻运行;
进程状态转移:
下面这张图是不加挂起状态的状态转移:
下面这张图是加挂起状态的状态转移:
进程控制
每个进程都会有一个**进程控制块(Process control block,PCB)**,他们之间是一一对应的关系,是操作系统为了管理进程设置的一个专门的结构体。
PCB包括哪些信息?
- 进程号pid : 是当前OS中每个进程唯一的标识符。进程号可重复使用,即一个进程结束后,该进程的进程号会被OS回收并再发放给另一个要运行的进程。
- 进程状态 :如上所示,包括new、ready、running、waiting 或 blocked 等;
- 其他信息:如上下文数据、IO信息、记账信息等。
在OS中,PCB们会通过链表的方式进行组织,把具有相同状态的进程链在一起,组成各种队列。如就绪队列、阻塞队列等。
那么既然PCB是为了管理进程而生,看看他怎么管理进程。
- 创建进程:申请PCB —> 向PCB填写信息(唯一标识等) —–> 为进程分配资源(内存资源等) ——> PCB 插入到就绪队列,等待运行
- 终止进程:查找PCB ——> 终止进程的执行 ——-> 将其子进程移交给一号进程 ——-> OS收回其占用的资源 ——-> 删除PCB
- 阻塞进程 :查找PCB ——> 保护此进程现场 ——> 将其状态转为阻塞 ——–> 将该 PCB 插入到阻塞队列中
- 唤醒进程 :(进程由「运行」转变为「阻塞」状态是由于进程必须等待某一事件的完成,所以处于阻塞状态的进程是绝对不可能叫醒自己的。)在阻塞队列查找PCB ——> 将其从阻塞队列中移出,并置其状态为就绪状态 ——–> 把该 PCB 插入到就绪队列中
进程上下文切换
一个进程切换到另一个进程运行,称为进程的上下文切换。
什么时候上下文切换?
一般来说,发生进程调度的时候会引发进程切换,而进程被调度有如下几个时机:
- 某个进程时间片耗尽,会被系统挂起,切换到其他等待 CPU 的进程。(时间片是系统分配给进程的,为了保证所有的进程可以被公平的调度而设立。某个进程的时间片耗尽了,进程就从运行状态变为就绪状态,系统从就绪队列选择另外一个进程运行;)
- 进程所需系统资源不足,需要等到资源满足时才可运行,此时会被挂起,其他进程会被调度。
- 进程通过 sleep 方法主动挂起,其他进程就有机会被调度。
- 有更高优先级的进程,当前进程会被挂起,高优先级进程会被调度。
- 硬件中断时,CPU 上的进程会被中断挂起,转而执行内核中的中断服务程序
怎么上下文切换?
进程的上下文切换属于CPU上下文切换。(CPU上下文切换还包括线程上下文切换、中断上下文切换)。
CPU上下文:在CPU运行任务前,需要CPU寄存器和程序计数等器等环境,这些环境就是CPU上下文。
CPU上下文切换 :
- 前一个任务的 CPU 上下文(CPU 寄存器和程序计数器)保存起来
- 加载新任务的上下文到这些寄存器和程序计数器
- 再跳转到程序计数器所指的新位置,运行新任务。
进程上下文切换:
由于进程是由内核管理和调度,所以进程的上下文切换不仅包含了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的资源。所以进程上下文切换开销大。大致过程为 :
- 把交换的信息保存在前一个进程的 PCB
- 从下个进程的 PCB 取出上下文,然后恢复到 CPU 中
- 运行下一个进程
进程通信
进程中通信方式总览 :
管道(匿名管道)。
用于具有亲缘关系的父子进程间或者兄弟进程之间的通信。
缺点是 :只支持单向数据流 、只能用于具有亲缘关系的进程之间
有名管道。
为了克服匿名管道由于没有名字,只能用于亲缘关系的进程间通信这个缺点,提出了有名管道。
有名管道严格遵循 先进先出(First In First Out) 。
有名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。
缺点是:有名管道在打开时需要确实对方的存在,否则将阻塞。即以读方式打开某管道,在此之前必须一个进程以写方式打开管道,否则阻塞。
信号。
一种比较复杂的通信方式,用于通知接收进程某个事件已经发生;
信号量。
信号量是一个计数器,用于多进程对共享数据的访问,信号量的意图在于进程间同步。
这种通信方式主要用于解决与同步相关的问题并避免竞争条件。
消息队列。
消息队列是消息的链表。
与有名管道不同的是消息队列存放在内核中,只有在内核重启(即,操作系统重启)或者显式地删除一个消息队列时,该消息队列才会被真正的删除。
可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.
内存共享。
使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。
可以说这是最有用的进程间通信方式。
套接字。
套接字是支持 TCP/IP 的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。
主要用于在客户端和服务器之间通过网络进行通信。
进程调度算法
在进程上下文切换中,谈到了进程上下文切换的原因是出现进程调度。
进程调度的几个场景在上面也列出来了,下面来看进程调度算法。
先到先服务调度算法(FCFS,First Come, First Served)。
其实就是每次从就绪队列选择最先进入队列的进程,然后一直运行,直到进程退出或被阻塞,才会继续从队列中选择第一个进程接着运行。
但是当一个长作业先运行了,那么后面的短作业等待的时间就会很长,不利于短作业。
**短作业优先的调度算法(SJF,Shortest Job First)**。
从就绪队列中选出一个估计运行时间最短的进程为之分配资源,使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用 CPU 时再重新调度。
但是对长作业非常不利。
时间片轮转调度算法(RR,Round-Robin)
每个进程被分配一个时间段,称为时间片(Quantum),即允许该进程在该时间段中运行。
如果时间片用完,进程还在运行,那么将会把此进程从 CPU 释放出来,并把 CPU 分配另外一个进程;
如果该进程在时间片结束前阻塞或结束,则 CPU 立即进行切换;
优先级调度算法(Priority)
就是希望调度程序能从就绪队列中选择最高优先级的进程进行运行。
优先级有可以根据内存要求,时间要求或任何其他资源要求来确定。
多级反馈队列调度算法(MFQ,Multi-level Feedback Queue)
「时间片轮转算法」和「最高优先级算法」的综合和发展。
「多级」表示有多个队列,每个队列优先级从高到低,同时优先级越高时间片越短。
「反馈」表示如果有新的进程加入优先级高的队列时,立刻停止当前正在运行的进程,转而去运行优先级高的队列;
多级反馈队列调度算法既能使高优先级的作业得到响应又能使短作业(进程)迅速完成。,因而它是目前被公认的一种较好的进程调度算法,UNIX 操作系统采取的便是这种调度算法。
线程
线程也被称为轻量级进程,多个线程可以在同一个进程中同时执行,并且共享进程的资源比如内存空间、文件句柄、网络连接等。打开的淘宝是一个进程,那么还有专门的线程来接受商家信息、观看直播等。
线程和进程
线程是进程划分成的更小的运行单位,一个进程中可以有多个线程。
多个线程共享进程的堆和方法区 (JDK1.8 之后的元空间)\资源
每个线程有自己的\程序计数器、虚拟机栈 和 本地方法栈。
线程与进程最大的区别在于:线程是调度的基本单位,而进程则是资源拥有的基本单位。
线程 | 进程 | |
---|---|---|
单位 | CPU 调度的单位 | 资源(包括内存、打开的文件等)分配的单位 |
资源 | 只独享必不可少的资源,如寄存器和栈 | 拥有一个完整的资源平台 |
状态 | 有就绪、阻塞、执行三种基本状态 | 有就绪、阻塞、执行三种基本状态 |
为什么使用线程?
这其实就在说进程做不到的事情了。
场景举例 : 有一个视频播放器软件,那么该软件功能的核心模块有三个:
从视频文件当中读取数据;对读取的数据进行解压缩;把解压缩后的视频数据播放出来;
那么使用单进程的话 : 就是对这三个核心模块依次执行。带来的问题就是,三个函数不是并发执行,影响效率。而且如果有一个函数执行的比较慢导致卡住了,会出现视频还没播放而声音已经放出来了的情况,导致音画不同步。
而使用多进程的话 :
- 如上面进程控制所述,维护进程的系统开销比较大(分配资源,PCB。。。)
- 如进程切换所述,进程切换是一个开销很大的操作
- 如进程通信所述,进程之间的通信也很费劲。
所以总的来说 ,线程比进程的优势有:
- 从计算机底层来说: 线程可以比作是轻量级的进程,是程序执行的最小单位,线程间的切换和调度的成本远远小于进程。另外,多核 CPU 时代意味着多个线程可以同时运行,这减少了线程上下文切换的开销。
- 从当代互联网发展趋势来说: 现在的系统动不动就要求百万级甚至千万级的并发量,而多线程并发编程正是开发高并发系统的基础,利用好多线程机制可以大大提高系统整体的并发能力以及性能。
线程上下文切换
两个线程是否属于一个进程,在切换时会带来不同的效果。
- 当两个线程不属于同一个进程,则切换的过程就跟进程上下文切换一样;
- 当两个线程是属于同一个进程,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据(这就是为什么线程的上下文切换相比进程开销要小很多。)
线程同步
线程同步是两个或多个共享关键资源的线程的并发执行。应该同步线程以避免关键的资源使用冲突。
…待更新