本文已参与「新人创作礼」活动,一起开启掘金创作之路。
进程是系统中一个正在运行的程序实体,是资源分配的基本单位和独立运行的基本单位。
线程是进程中运行的实际工作单位,是一个进程内可调度的实体,是CPU调度的最小单位。
协程是一种用户态的轻量级线程,拥有自己的寄存器上下文和栈,协程的调度完全由用户控制,调度切换时,将寄存器上下文和栈保存到其他 地方,在切回来时会恢复先前的寄存器上下文和栈,上下文的切换非常快。
区别
进程和线程有什么区别?
- 进程是系统进行资源分配和运行调度的基本单位,线程是系统进行资源分配和运行调度的最小单位。
- 进程是线程的容器,一个进程可以并发多个线程,并发的多个线程共享进程的资源。
- 进程有自己独立的地址空间,线程共享所属进程的地址空间;
- 进程切换的开销远大于线程切换的开销;
协程和线程有什么区别?
-
线程或进程都可以拥有多个协程。
-
线程、进程是同步机制,而协程是异步机制。
-
协程能保留上一次调用时的状态,每次重入都会进入上一次调用的状态。
-
线程是抢占式,而协程是非抢占式的,需要用户自己切换到其他协程,同一时间只有一个协程可以运行。
-
线程是被分割的CPU资源, 协程是组织好的代码流程
-
协程需要线程来承载运行, 线程是协程的资源,协程利用的执行器(Interceptor)使用线程
线程独占哪些资源?
- 线程ID
- 线程自身的栈(堆是共享的)
- 寄存器的值
同一进程中的线程可以共享哪些数据?
- 进程代码段
- 进程的公有数据(全局变量、静态变量...)
- 进程的当前目录
- 进程ID与进程组ID
- 进程打开的文件描述符
- 信号处理器/信号处理函数:对收到的信号的处理方式
通信与同步
临界资源和临界区的概念?
临界资源指不能被多个进程同时访问的共享资源。当有进程在 使用临界资源时,其他进程必须等待占用进程释放该共享资源才可重新竞争共享资源。
临界区是各个进程对临界资源进行操作的程序片段。
同步与互斥的概念?
-
同步:多个进程合作时,执行需要有先后顺序。比如这个进程需要另一个进程提供的消息,获得消息之前这个进程进入阻塞态;
-
互斥:多个进程在同一时刻只有一个进程能进入临界区。
并发、并行、异步的区别?
并发:在一个时间段同时有多个程序运行。微观上只有一个程序在CPU上运行,宏观上的并发是通过不断的切换实现的;
并行(和串行相比):在多CPU系统中,多个程序无论宏观还是微观上都是同时执行的。
异步(和同步相比):同步是顺序执行,异步是在等待某个资源时可以继续执行。
多线程:并发运行的一段代码,是实现异步执行的手段。
如何同步
进程间同步:信号量(互斥量)、管道、消息队列、共享内存
线程间同步:信号量(互斥量)、条件变量、读写锁、自旋锁、临界区
进程通信——管道
IPC定义:是进程间通信或者跨进程通信,指两个进程之间进行数据交换的过程。
管道是一种最基本的IPC机制,作用于关系密切的进程之间,完成数据传递。调用pipe系统函数即可 创建一个管道。有如下特质:
- 其本质是一个伪文件(实为内核缓冲区)
- 由两个文件描述符引用,一个表示读端,一个表示写端。
- 规定数据从管道的写端流入管道,从读端流出。
管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区实现。
管道的局限性:
- 数据自己读不能自己写。
- 数据一旦被读走,便不在管道中存在,不可反复读取。
- 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。
- 只能在有公共祖先的进程间使用管道。
进程同步问题——管程
进程的同步是目的,而进程间通信是实现进程同步的手段。
管程是共享资源的数据结构,将共享变量以及对共享变量的操作进行封装,形成具有一定接口的功能模块,只能通过管程提供的某个过程才能访问管程中的资源。进程只能互斥地使用管程,使用完之后必须释放管程并唤醒入口等待队列中的进程。
HOARE管程:
当一个进程试图进入管程时,会在入口等待队列中等待。
wait操作:执行wait操作的进程进入条件变量链末尾,唤醒入口等待队列或紧急等待队列中的进程;
signal操作:执行signal操作的进程进入紧急等待队列,唤醒条件变量链中的进程,若条件变量链为空,则继续执行。
MESA管程:将HOARE中的signal换成了notify(或者broadcast)通知所有满足条件的进程,只进行通知而不是立马交换管程的使用权,在合适的时候,条件队列首位的进程可以进入,进入之前必须用while检查条件是否合适。优点:没有额外的进程切换。
生产者-消费者问题
问题描述:使用一个缓冲区来存放数据,通过互斥量控制访问,只有缓冲区没有满时生产者才可以写入数据;只有缓冲区不为空时消费者才可以读出数据
// 伪代码描述 // 定义信号量 full记录缓冲区物品数量 empty代表缓冲区空位数量 mutex为互斥量 semaphore full = 0, empty = n, mutex = 1; // 生产者进程 void producer(){ do{ P(empty);//检查是否有空位,没有则等待 P(mutex);//检查互斥量是否可用,不可用则等待 // 生产者进行生产 V(mutex);//释放互斥量 V(full);//通知消费者有物品 } while(1); } void consumer(){ do{ P(full);//检查是否有物品,没有则等待 P(mutex);//检查互斥量是否可用,不可用则等待 // 消费者进行消费 V(mutex);//释放互斥量 V(empty);//通知生产者有空位 } while(1); }