进程、线程、协程相关知识积累

1.进程、线程、协程的区别

(1)进程是程序一次动态执行的过程,是程序运行的基本单位。操作系统会以进程为单位,分配系统资源(CPU时间片、内存等资源),进程是资源分配的最小单位。

进程占据独立的内存,所以上下文进程间的切换开销比较大。进程之间可以通过管道、信号量、消息队列、、套接字、共享内存进行通信。
(2)线程从属于进程,是程序的实际执行者。一个进程至少包含一个主线程,也可以有更多的子线程。
多个线程共享所属进程的资源(在jvm中包括堆、方法区),同时线程也拥有自己的专属资源(虚拟机栈、本地方法栈、程序计数器)。
线程间通信主要通过共享内存,上下文切换成本比进程低。
(3)协程既不是进程也不是线程,协程仅仅是一个特殊的函数,一个线程可以包括多个协程。
协程的上下文切换不是由操作系统管理的,而是由程序控制的,所以不需要像线程进行多次的用户态、内核态切换。

2.进程切换步骤

(1)切换页目录以使用新的地址空间
(2)切换内核栈和硬件上下文

所以在切换进程时,一定会有两个问题:

新的进程需要新的内存空间,将寄存器中的内容切换出来是最显著的性能消耗。
上下文的切换会扰乱处理器的缓存机制。一旦切换上下文,处理器中所有已经缓存的内存在址在一瞬间全部作废,
已缓存的页表被全部刷新,导致内存访问在一段时间内相当低效,程序运行也会变慢甚至出现卡顿。

3.什么是系统态、用户态?(线程什么时候会切换到内核态)

由于用户态不能直接使用系统资源,如CPU、内存、I/O等,内核提供了一组通用的访问接口,这些接口叫做系统调用,系统调用可以使程序从用户态到内核态进行切换。

除了系统调用这种自发的切换,异常和外围设备中断也会发生状态改变。异常会使当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态。

当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。

4.线程模型(1:1 1:N N:M)

1:1 线程模型
1:1 模型,即每一个用户线程都对应一个内核线程,每个线程的创建、调度、销毁都需要内核的支持,每次线程的创建、切换都会设计用户状态/内核状态的切换,性能开销比较大,并且单个进程能够创建的LWP的数量是有限的,但能够充分里用多核的优势

N:1 线程模型
N:1模型,即所有的用户线程都会对应到一个内核线程中,该模型可以在用户空间完成线程的创建、调度、销毁,不需要内核的支持,同样也就不涉及用户状态/内核状态的切换,线程的操作较快且消耗较低,并且线程数量不受操作系统的限制,但不能发挥多核的优势,只能在一个核中分时复用,并且由于内核不能感知用户态的线程,在某一线程被阻塞时,会导致整个所属进程阻塞

N:M 线程模型
N:M 模型是基于以上两种模型的一种混合实现,多个用户线程对应于多个内核线程,即解决了1:1模型中性能开销及线程数量的问题,也解决了N:1模型中阻塞问题,同时也能充分利用CPU的多核优势,这也是大部分协程实现的基础

Java在1.2之前基于用户线程实现(N:1线程模型),在1.2之后windows及linux平台下采用1:1线程模型(这也是java线程切换需要切换系统态的原因),在solaris平台使用1:1或N:M线程模型实现(可配置)

5.用户态切换到系统态为什么费时间?

(1)下面是从用户态切换到内核态的主要步骤:

用户程序发起系统调用
用户程序使用系统调用库中提供的函数,如read、write等,向操作系统发起请求。

中断触发
当用户程序调用系统调用库中的函数时,CPU会发出一个软中断(软件中断),即通过在中断向量表中查找相应的系统调用处理程序地址并跳转至其执行。该中断会把当前进程挂起,将控制权转交给内核。

执行内核代码
一旦CPU进入内核空间,就可以执行内核代码了。此时,CPU可以访问所有的系统资源,包括设备驱动程序、文件系统、网络协议栈等。

执行系统调用
内核开始执行用户程序请求的系统调用,并按照用户程序提供的参数进行操作。例如,如果用户程序请求读取文件,内核会根据用户程序提供的参数,在文件系统中查找文件并读取数据。

返回用户程序
当内核完成请求的操作后,它将结果返回给用户程序,并将控制权返回给用户程序。此时,CPU重新切换到用户态。
 

你可能感兴趣的:(面试)