(四) 通用试题——进程线程相关
进程线程相关
1. 说一说你对进程和线程的认识?
- 进程隔离内存,线程共享内存。调度是以线程为单位。
现代操作系统上能同时执行多个任务。为了能方便的管理这些任务,引入了进程的概念。 - 每个进程拥有独立的内存,这样写代码不需要关心内存保护。
另一个是进程的运行是被操作系统调度的,写代码的时候不需要担心什么时候让出控制权。这一套机制让代码写起来更方便高效。因为写代码的时候不用关心其他进程是怎么运行的。随着cpu技术的发展,出现了多核。那么为了充分利用这些多核心计算资源,就需要用多个进程。
但是因为进程之间内存是隔离的,那么多进程之间进行数据通信和同步就非常复杂。为了能方便的利用多核心 cpu,引入了线程的概念。 - 一个进程可以拥有多个线程,内存的隔离单元还是进程,但是操作系统的调度单元变成了线程。
- 线程间通信就非常方便,因为共享内存,可以直接调用函数或共享全局变量。但是事物总是有利有弊的,随意共享全局变量会导致数据不一致的问题。(如同一账户的存钱取钱问题)那就需要引入锁来进行数据同步/原子性操作。
后续可继续按解决问题这个思路继续往下说线程、线程池、协程、io 多路复用等。
扩展: 多线程技术是实现并发访问的方案之一,但是随着网络的发展,多线程又出现了一些缺陷。
比如多线程中,如果并发量较大,每个请求分配一个线程,面对大量的线程操作系统根本调度不过来,
其次本身线程的切换又涉及到上下文保存与重建工作,也有一定的开销,
此外频繁的线程创建、初始化、销毁也会有性能损耗。
针对第三点有线程池技术可以解决,但是前两点仍然是多线程的缺陷。
后来诞生了IO多路复用技术来解决并发访问问题。
2. IO多路复用是如何解决并发访问问题的?
我先说明一下什么是IO,我们的应用程序是运行在内存中的,内存又分为用户态和内核态,内核态是由操作系统管理的,一次IO就是数据从硬件设备如网卡、磁盘到程序内存的传输过程,包括读和写,但通常我们的应用程序是没有面向硬件的接口的,所以中间又会有操作系统内核进行缓冲。所以一个IO过程可以分为两个阶段,第一阶段是IO的就绪过程,比如一次网络通信,数据从发送方物理设备根据相应的网络协议,传输到接收端物理设备也就是网卡,然后操作系统将数据从硬件读取到内核buffer中;
第二阶段是真正的读写过程,由我们的程序线程执行,将数据从内核buffer读取到程序内存中,这一步是CPU操作,是非常快的,而第一步就绪过程则可能涉及到网络通信,或磁盘读写等,相比于CPU操作是非常慢的,也就是说第一阶段就绪阶段是耗时的瓶颈。
那么如何监控一个IO流呢,我们可以一个IO流分配一个线程,在IO的两个阶段我的工作线程都处于阻塞状态,线程在第一个IO就绪阶段不执行任何操作,直到IO就绪后进行数据的读写;我们前面说过第一阶段是非常长的,长时间的阻塞无疑是一种浪费,那么又有了非阻塞型方案,我的工作线程在IO就绪过程中仍然可以执行一些其他的CPU操作,但为了保证IO处理的及时性,工作线程需要频繁的查看IO是否就绪,虽然这是我们已经利用了第一阶段执行了一些CPU任务,但在真正处理IO之前必然会有很多次无效的轮询操作,仍然有性能浪费。
后来诞生了IO多路复用技术,将多个IO流的监听工作委托给操作系统内核进行,我们的工作线程只需将读或写操作注册为回调函数绑定到对应的IO流即可。内核监听到某个IO就绪后,将这个IO流提呈给操作线程,触发相应读与写的回调函数,执行IO读写即可。此时我们的工作线程不需要再关心IO的第一阶段,只需要专注于IO的读写阶段的CPU操作即可,效率有很大提升。
...