一、进程

1.1 定义

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配的调度的基本单位,是操作系统结构的基础。在早期,进程是程序的基本执行实体,在当代面向线程设计的计算机结构中,进程是线程的容器。
狭义定义:进程是正在进行的程序的实例,是一个正在执行的程序,是可以分配给处理器并由处理器执行的一个实体
广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
进程是由单一的顺序的执行线程、一个当前状态和一组相关的系统资源所描述的活动单元。
**

进程组成:
1.一段可执行的程序
2.程序所需要的相关的数据(变量、工作空间、缓冲区等)
3.程序的执行上下文

执行上下文

执行上下文又称为进程状态,是操作系统用来管理和控制进程所需的内部数据。

进程是资源分配的最小单位,每一个进程中至少有一个线程。



进程的特性:**

  • 动态性:进程的实质是多道程序系统的一次执行过程,进程是动态产生,动态消亡的
  • 并发性:任何进程都可以同其他进程一起并发执行
  • 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单元
  • 异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进

结构特征:进程由程序、数据和进程控制块三部分组成。



状态:**

  • 运行态:该进程正在执行
  • 就绪态:进程已经做好了准备,只要有机会就开始执行
  • 睡眠态(阻塞态):分为深度睡眠和浅度睡眠态

    1. 浅度睡眠状态:进程正在睡眠(被阻塞),等待资源的到来来唤醒,也可以通过其他进程信号或时钟中断唤醒,进入运行队列。Linux 中使用TASK_INTERRUPTIBLE 宏表示此状态。<br /> 深度睡眠状态:其和浅度睡眠基本类似,但不可被其他进程信号或时钟中断唤醒。Linux 中使用TASK_UNINTERRUPTIBLE 宏表示此状态。
  • 停止态:进程暂停执行接收某种处理

  • 僵死态:该进程已经结束,但是其父进程还没有调用wait、waitpid将其回收,不消耗系统的其他资源但是进程号会一直占用,

分类:

  • cpu密集型 高计算、cpu资源比较高
  • IO密集型 IO高的进程

    类型

  • 守护进程:在系统引导过程中启动的进程,与终端无关的进程

  • 前台进程:与终端相关的,需要通过终端进行启动

    1. **优先级**<br /> 0-99:实时优先级,数字越小,优先级越低<br /> 100-139:静态优先级,数字越小,优先级越高<br /> Nice指:-20 ,19

多个不同的进程可以包含相同的程序,一个程序在不同的数据集里就构成不同的进程,能得到不同的结果,但在执行过程中,程序不能发生改变。
程序是指令、数据及其组织形式的描述,进程是程序的实体。

1.2 进程与程序的区别

进程:是程序在处理机上的一次执行过程,是一个动态的概念,进程是有一定生命期,进程是暂时的
程序:是指令和数据的有序集合,是一个静态的概念,程序可以作为一种软件资料长期存在,程序是永久的
1.进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data resign)和堆栈(stack resign)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。
2.进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(即操作系统执行它时),它才能成为一个活动的实体,叫做进程。

1.3 进程调度

要想多个进程交替运行,操作系统必须对这些进程进行调度,这个调度也不是随即进行的,而是需要遵循一定的法则,下面是进程的调度算法。

1.先来先服务算法(FCFS)

先来先服务调度算法,是一种最简单的调度算法,既可用于作业调度,也可用于进程调度
比较有利于长作业(进程),不利于短作业(进程)
适合于CPU繁忙型作业,不利于I/O繁忙型的作业(进程)

2.短作业优先调度算法(SJ/PF)

是指对短作业或短进程优先调度的算法,既可用于作业调度,也可用于进程调度
对长作业不利,不能保证紧迫性作业(进程)不能及时处理,作业的长短时间被估算出来的

3.时间片轮转法(RR)

每个进程在就绪队列中的等待时间和享受服务的时间成比例
在时间片轮转法中,需要将CPU的处理时间分成固定大小的时间片,eg:几十毫秒至几百毫秒。
如果一个进程在被调度选中之间用完了系统规定的时间片,但又未完成要求的任务,则它自行释放自己所占有的CPU而排到就绪队列的末尾,同时,进程调度程序又去调度当前就绪队列中的第一个进程。
轮转法只能用来调度分配一些可以抢占的资源。这些可以抢占的资源可以随时被剥夺,而且可以将它们再分配给别的进程。CPU是可抢占资源的一种,但是打印机等资源是不可被抢占的。作业调度是对除了CPU之外的所有系统硬件资源的分配,其中包含有不可抢占的资源,所以作业调度不使用轮转法。
时间选片很重要。 首先,时间片长度的选择会之间影响到系统的开销和响应时间。

  • 如果时间长度过短,则调度程序抢占处理机的次数增加,这将使进程上下文切换次数也大大增加,从而加重系统开销。
  • 如果时间长度选择过长;eg:一个时间片能保证就绪队列中所需执行时间最长的进程能执行完毕,则轮转法变成了先来先服务法。

时间片长度的选择是根据系统对响应时间的要求和就绪队列中所允许最大的进程数来确定的。
在轮转法中,加入到就绪队列的进程有三种情况:

  1. 分给它的时间片用完,但进程还未完成,回到就绪队列的末尾等待下次调度去继续执行
  2. 分给该进程的时间片还未用完,只是因为I/O或者由于进程的互斥与同步关系被阻塞。当阻塞被解除之后再回到队列
  3. 新创建的进程进入就绪队列

    4. 多级反馈队列

    设置多个就绪队列,并为各个队列赋予不同的优先级。
  • 第一个队列的优先级最高,第二个次之,其余各队列的优先权逐个降低。在优先权越高的队列中,为每一个进程所规定的执行时间片就越小。

eg:第二个队列的时间片要比第一个队列的时间片长一倍。。。。。。第i+1个队列的时间片要比第i个队列的时间片长一倍。

  • 当 一个新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如果它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚未完成,调度进程便将改进程转到第二队列的末尾,再同样按FCFS原则等待调度执行,如果它再第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列。。。。。。如此下去,当一个长作业(进程)从第一队列依次降到第n队列后,再第n队列便采取按时间片轮转的方式进行。
  • 仅当第一队列空闲时,调度程序才调度第二程序队列中的进程运行,仅当第1~(i-1)队列均空时,才会调度第i队列中的程序运行。如果处理机正在第i队列中为某进程服务时,又有新进程进入优先权较高的队列(第i~(i-1)中的任意一个队列),则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回到第i队列的末尾,把处理机分配给新到的高优先权进程。

1.4 进程的并发与并行

并行:并行是指两者同时执行,资源公用。
并发:并发是指资源有限的情况下,两者交替轮流使用资源。

区别:
并行:微观上,也就是在一个精确的时间片刻,有不同的程序在执行,这就要求必须有多个处理器。
并发:宏观上,在一个时间段上可以看出是同时执行的;eg:一个服务器同时处理多个session。

1.5同步异步阻塞与非阻塞

1. 状态介绍

进程、线程、协程 - 图1
在程序运行的过程中,由于被操作系统的调度算法控制,程序会进入几个状态:就绪、运行和阻塞。
(1)就绪状态
当进程已分配到出 CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态成为就绪状态。
(2)运行状态
当进程已获得处理机,其程序正在处理机上执行,此时的进程状态成为执行状态。
(3)阻塞状态
正在执行的进程,由于等待某个事件的发送而无法执行时,便放弃处理机而处于阻塞状态。
引起进程阻塞的时间可有多种;eg:等待I/O完成、申请缓冲区不能满足,等待信号等。

进程、线程、协程 - 图2

2.同步与异步 同步与互斥

同步就是一个任务的完成需要依赖另一个任务时,只有等待依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列。要么成功,都成功,失败都失败,两个任务的状态可以保持一致。
异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务是否真正完成,依赖它的任务无法确定,所以是不可靠的任务序列。

同步:在不同进程中的若干个程序片段,他们的运行必须严格按照规定的某种先后次序来运行,先后顺序取决于要完成的特定的任务。从资源的访问定义,互斥某一资源同时只允许一个访问者来对其进行访问,具有唯一性和排他性。但是互斥无法控制访问者对于资源的访问顺序,即访问是无序的
互斥:在不同进程中的若干个程序片段,当某个进程运行其中一个程序片段时,其他进程就不能运行它们之中的任一个程序片段,只能等待该进程运行完这个程序片段后才可以运行。从资源的访问定义,同步大多是在互斥基础上的,通过其他访问机制实现访问者对资源的有序访问。

3.阻塞与非阻塞

主要是程序(线程)等待消息通知时的状态角度来说的
阻塞:进程给CPU传达一个任务之后,一直等待CPU处理完成,然后才执行后面的操作
非阻塞:进程给CPU传达任务后,继续处理后续的操作,隔断时间再来询问之前的操作是否完成。这样的过程其实也叫轮询

二、线程

进程所创建的,是进程内的一个执行单元,也是进程内的可调度实体。
线程包括处理器上下文环境(包含程序计数器和栈指针)和栈中自己的数据区域(为允许子进程分支)。
线程是可中断的,处理器可以从一个线程转到另一个线程。
每个进程中至少有一个线程
是cpu分配和调度的最小单位
执行处理机调度的基本单位

三、协程

是用户态轻量级线程,协程是由用户程序自己控制调度的
优点:
1.是进程和线程的升级版,进程和线程都面临着内核态和用户态的切换而消耗许多切换时间,协程就是用户自己控制切换的时机,不再需要陷入系统的内核态
2.执行效率高。是子程序切换,不是线程切换,有程序自身控制。与多线程相比,线程数量越多,相同数量的协程体现出来的优势越明显
3.不需要多线程的锁机制,只有一个线程,不存在同时写变量的冲突,在协程中控制共享资源不需要加锁,只需要判断数据的状态,执行效率远高于线程
4.轻量级线程

四、进程与线程的通信

进程

1.管道 用于具有亲缘关系的父子进程间的通信,半双工模式下的通信方式,数据只能单向流动
2.有名管道 不仅可以进行具有亲缘关系间的通信,它还允许无亲缘关系进程间通信,半双工的通信方式
3.信息量 信息量是一个计数器,可以用来控制多个进程对共享资源的访问。常用作一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。主要作为进程间通信以同一进程内不同线程之间的同步手段
4.消息队列 消息队列是消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点
5.信号 用于通知接收进程某个事件已经发生
6.共享内存:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信
7.套接字 用于不同进程之间的通信

线程

1.锁机制
互斥锁:提供了以排他方式防止数据结构被并发修改的方法
条件变量:以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用
读写锁:允许多个线程同时读共享数据,而对写操作是互斥的
2.信号量机制
包括无名线程信号量和命名线程信号量
3.信号机制
类似进行的信号处理
线程之间通信的目的主要是用于线程同步,所以线程之间没有像进程通信中的用于数据交换的痛惜机制

五、网络I/O模型

1.文件描述符

文件描述符( File descriptor )是 进程文件描述符表的一个索引,是内核为了高效管理已被打开的文件创建的索引,用于指向被打开的文件,所有执行IO操作的系统调用都要通过文件描述符。
文件描述符是一个简繁的非负的整数,用以表明每个被进程打开的文件。
程序刚刚启动时,一个打开的文件时0,第二个是1,第三个,以此类推。
用户通过操作系统处理信息的过程中,使用的交互设备文件(键盘,鼠标,显示器)
标准输入输出说明:
stdin,标准输入,默认设备是键盘,文件编号为0
stdout,标准输出,默认设备是显示器,文件编号为1,也可以重定向到文件
stderr,标准错误,默认设备是显示器,文件编号为2,也可以重定向到文件
不同的进程中会出现相同的文件描述符,它们可能指向同一个文件,也可能指向不同的文件
两个不同的文件描述符,若指向同一个打开文件句柄,将共享同一文件偏移量
文件描述符标志(即,close-on-exec)为进程和文件描述符所私有。对这一标志的修改将 不会影响同一进程或不同进程中的其他文件描述符

2.五大网络I/O模型

阻塞、非阻塞、多路IO复用,都是同步IO,异步必定是非阻塞的,所以不存在异步阻塞和异步非阻塞的说法。真正的异步IO需要CPU的深度参与。
https://www.cnblogs.com/wlwl/p/10291397.html
https://blog.csdn.net/muyuyuzhong/article/details/83538860

阻塞IO(同步IO)

发起请求就一直等待,直到数据返回。

非阻塞IO(同步IO)

不管有没有数据都返回,没有就隔一段时间再来请求,如此循环。

IO复用(同步IO)

I/O指的是网络I/O,多路指多个tcp连接(socket或者channel)复用指复用一个或者几个线程。
一个或者一组线程来处理多个连接。

信号驱动IO(同步I/O)

事先发送一个请求,当有数据后返回一个标识回调,这个时候可以去请求数据。

异步IO

发出请求就返回,剩下的事情会异步自动完成,不需要做任何处理。

五种IO的模型:阻塞IO、非阻塞IO、多路复用IO、信号驱动IO和异步IO;前四种都是同步IO,在内核数据copy到用户空间时都是阻塞的。
阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞,如果会那就是传统的阻塞IO,如果不会那就是非阻塞IO。
同步IO和异步IO的区别就在于第二个步骤是否阻塞,如果实际的IO读写阻塞请求进程,那么就是同步IO;如果不阻塞,而是操作系统帮你做完IO操作再将结果返回给你,那么就是异步IO

3.select、poll、epoll之间的区别

三者都是IO多路复用的机制
IO多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪,能够通知程序进行相应的读写操作。
select、poll、epoll本质上都是同步I/O,都需要在读写事件就绪之后自己负责进行读写,在读写过程中是阻塞的。而异步I/O无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间
select
客户端操作服务器时就会产生这三种文件描述符(简称fd):writefds(写)、readfds(读)、和exceptfds(异常)。select会阻塞住监视3类文件描述符,等有数据、可读、可写、出异常 或超时、就会返回;返回后通过遍历fdset整个数组来找到就绪的描述符fd,然后进行对应的IO操作
select 的核心功能是调用tcp文件系统的poll函数,不停的查询,如果没有想要的数据,主动执行一次调度(防止一直占用cpu),直到有一个连接有想要的消息为止。从这里可以看出select的执行方式基本就是不同的调用poll,直到有需要的消息为止

  • 缺点

每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
由于是采用轮询方式全盘扫描,会随着文件描述符FD数量增多而性能下降
select支持的文件描述符数量太小了,默认是1024

  • 优点

select的可移植性更好,在某些Unix系统上不支持poll()
select对于超时值提供了更好的精度:微秒,而poll是毫秒
poll
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd

  • 缺点

大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义;
与select一样,poll返回后,需要轮询pollfd来获取就绪的描述符

  • 优点

poll() 不要求开发者计算最大文件描述符加一的大小。
poll() 在应付大数目的文件描述符的时候速度更快,相比于select。
它没有最大连接数的限制,原因是它是基于链表来存储的
epoll
epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时, 返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一 个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射技术,这 样便彻底省掉了这些文件描述符在系统调用时复制的开销

  • 优点

支持一个进程打开大数目的socket描述符,epoll没有对FD的限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048
IO效率不随FD数目增加而线性下降
使用mmap加速内核与用户空间的消息传递
三者对比
1、select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。
2、select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。
image.png

文件句柄

对象创建之后,请求创建的应用程序得到该对象的句柄,句柄实质上就是指向被引用对象的指针。
句柄可以被一个进程中的多个线程进行复用或者被复制到其他进程中去
对象创建之后,对象管理器会返回这个对象的句柄,句柄是访问该对象的唯一途径。

COW

copy on write 写时复制技术
元数据(源数据指针表)拷贝,在创建快照的时候,并不会发生物理的数据拷贝动作,仅是拷贝了原始数据所在的元数据块的物理位置元数据。
在创建快照之后,快照软件会监控跟踪原始数据的变化(即对源数据的写操作),一旦源数据块中的原始数据被改写,则会将源数据块上的数据拷贝到新数据块中,然后将新数据写入到源数据块中覆盖原始数。

ROW

redirect on write 写时重定向
与cow相似,区别在于row对于原始数据卷的首次写操作,会将新数据重定向到预留的快照卷中,而cow会使用新数据将原始数据覆盖。
cow的快照卷存放的是原始数据,而ROW的快照卷存放的是新数据

进程、线程、协程 - 图4