进程
进程的状态
- 创建状态:进程正在被创建时的状态;
- 运行状态:该时刻进程占用CPU;
- 就绪状态:可运行,由于其他进程处于运行状态而暂时停止运行(也就是获取除了CPU以外的所有资源);
- 阻塞状态:该进程正在等待某一事件(IO)发生而暂时停止运行,这时即使给它CPU控制权,它也无法运行;
- 挂起状态:描述进程没有占用实际的物理内存空间的情况,通常会把阻塞状态的进程的物理内存空间换出到硬盘中,等再次运行的时候再从硬盘换入到物理内存中;
- 结束状态:进程正在从系统中消失的状态;
进程的数据结构(北森一面被问)
进程控制块(PCB):是进程存在的唯一标识;
包含:
- 进程描述信息;
- 进程控制和管理信息(进程当前的状态如:new、ready、running、blocked;进程的优先级:进程抢占CPU的优先级);
- 资源分配清单(虚拟内存地址等信息);
- CPU相关信息(CPU中各种寄存器的值,当进程被切换时,CPU的状态信息都会被保存在相应的PCB中,以便进程重新执行时,能从断点处继续执行)。
组织:
- 通过链表的方式组织,把具有相同状态的进程链在一起,组成各种队列;
- 例如:将所有处于就绪状态的进程链在一起称为就绪队列;将所有因等待某事件而处于等待状态的进程链在一起就组成各种阻塞对列。
进程间的通信
每个进程的用户地址空间都是独立的,一般而言是不能相互访问的,但是内核空间是每个进程都是共享的,所以进程之间要通信必须通过内核。
管道(Pipe)
实质:内存里面的一串缓存,也就是只存在于内存,不存在于文件系统;所以管道传输数据是无格式流且大小受限;
分类:
- 匿名管道( ‘|’ ):通信范围是存在父子关系的进程,通讯方式是单向的,因为管道没有实体,也就是没有管道文件,只能通过fork来复制父进程FD文件描述符,来达到通信的目的;
- 命令管道:通信范围是可以在不相关的进程间也能相互通信,因为提前常见了一个类型为管道的设备文件,在进程里只要使用这个设备文件,就可以相互通信。
流程:进程写入数据都是缓存在内核中,另一个进程读取数据时候也是从内核中获取,同时通信数据遵循先进先出的原则;
缺点:通信方式效率低,不适合进程间频繁的交换数据;
消息队列
实质:保存在内核中的消息链表;
数据结构:消息体(数据块),消息体是用户自定义的数据结构,消息的发送方和接受方要约定好消息体的数据类型,也就是消息体都有固定大小的存储块;
缺点:
- 不适合较大数据的传输,因为消息体都有一个最大长度的限制,同时所有队列包含的全部消息体的总长度也是有上限的;
- 在通信的过程中,存在用户态到内核态之间的数据拷贝开销,因为进程写入数据或者读取到(从)内核中的消息队列,都会发生用户态与内核态的数据拷贝。
共享内存
- 概念:每个进程都有自己的独立的虚拟内存空间,让不同进程的虚拟内存空间映射到相同的物理内存中。
- 优点:不涉及用户态和内核态之间的数据拷贝;
- 缺点:可能产生多进程竞争共享资源,造成数据错乱;
- 解决:信号量,实质是一个整型计数器,主要用于实现进程之间的互斥与同步;
信号量
P操作:将信号量减去1;
- 如果信号量 < 0,表示资源被占用,进程被阻塞;
- 如果信号量 >= 0,表示资源可使用,进程可正常继续执行。
V操作:将信号量加上1;
- 如果信号量 <= 0,表示当前有阻塞中的进程,需要将该进程唤醒;
- 如果信号量 > 0,表示当前没有阻塞的进程。
情况:
互斥量(Mutex):使两个进程互斥访问共享内存,可以初始化信号量(Semaphore)为1;
- 进程A在访问共享内存前,先执行P操作,将信号量变为0,表示资源可使用,于是进程A继续访问共享内存;
- 此时进程B也访问共享内存,执行P操作,将信号量变为-1,表示资源被占用,进程B被阻塞;
- 进程A使用完毕,执行V操作,将信号量变为0,表示当前有阻塞进程,将进程B唤醒;
- 之后B执行访问共享内存,结束后执行V操作,将信号量变为1。
保证进程A在进程B执行前执行,可以初始化信号量为0;
- 进程B先访问共享内存,执行P操作,将信号量变为-1,表示资源被占用,进程B被阻塞;
- 进程A生产完数据,执行V操作,将信号量变为0,表示有阻塞的进程,将进程B唤醒;
- 进程B执行。
信号
进程间通信机制中唯一的异步通信机制,信号可以在应用程序与内核之间直接交互,内核也可以利用信号来通知用户空间的进程发生了哪些系统事件。
进程响应信号的方式:
- 执行默认操作
- 捕捉信号
- 忽略信号
socket通信
跨网络与不同主机上的进程之间的通信
三者之间的关系
进程、线程、协程
- 操作系统会以进程为单位,分配系统资源,进程是分配资源的最小单位;
- 线程:CPU调度执行的最小单位;
- 协程:一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈;协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基础没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
进程与线程的比较
- 线程共享进程的共享内存(虚拟内存共享,也就是说同一个进程的线程都具有同一个页表,切换的时候不需要切换页表)和文件资源;
- 线程只独享寄存器和栈;
- 线程同样具有就绪、阻塞、执行三种基本状态,同样具有状态之间的转换关系。
线程与协程的比较
- 线程需要进行上下文切换(需要内核切换)、需要加锁;
- 协程:
- 一个方法可以有多个切入点,不只有开始和结束,而是中间可以切入;
- 用户态:是指我们已经知道了开启的两个线程是消耗IO资源的,这个时候会主动释放掉线程,不占用CPU资源,通过yield()转化为就绪状态,再通过监听factorbox(也就是多加一层,通过监听factorbox实现解耦)是否有内容来执行线程,也就节省了等待IO操作的CPU资源;
- 不会一直占用CPU等待所有线程都执行完毕,而是事先让出CPU资源,通过监听的方式再将线程运行。也就节省了CPU资源。