76b181968f50340ee480027e8d93a193
进程和线程的区别与联系深入理解

进程和线程的区别与联系深入理解

我们都知道进程是系统进行资源分配和调度的一个独立单位。线程是进程的一个实体,是CPU调度和分派的基本单位。线程自己基本上不拥有系统资源,但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。由于线程比进程更小,基本上不拥有系统资源,线程上下文切换比进程上下文切换要快得多,故对它的调度所付出的开销就会小得多,从而显著提高系统资源的利用率和吞吐量。

但是对于为什么线程上下文切换比进程要快以及进程线程上下文切换到底做了哪些事情大多数人并不是特别清楚。

线程共享哪些进程的资源?

1.进程代码段

2.进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)

上面的 2 个总结就是线程共享进程的虚拟地址映射

3.进程打开的文件描述符、信号的处理器、进程的当前目录和进程用户ID与进程组ID

线程独立的资源

1.线程ID

每个线程都有自己的线程ID,这个ID在本进程中是唯一的。进程用此来标识线程。

2.寄存器组的值

由于线程间是并发运行的,每个线程有自己不同的运行线索,当从一个线程切换到另一个线程上 时,必须将原有的线程的寄存器集合的状态保存,以便将来该线程在被重新切换到时能得以恢复。

3.线程的堆栈

堆栈是保证线程独立运行所必须的。线程函数可以调用函数,而被调用函数中又是可以层层嵌套的,所以线程必须拥有自己的函数堆栈, 使得函数调用可以正常执行,不受其他线程的影响。线程的栈分配在进程的堆上

4.错误返回码

由于同一个进程中有很多个线程在同时运行,可能某个线程进行系统调用后设置了errno值,而在该 线程还没有处理这个错误,另外一个线程就在此时被调度器投入运行,这样错误值就有可能被修改。所以,不同的线程应该拥有自己的错误返回码变量。

5.线程的信号屏蔽码

由于每个线程所感兴趣的信号不同,所以线程的信号屏蔽码应该由线程自己管理。但所有的线程都 共享同样的信号处理器。

6.线程的优先级

由于线程需要像进程那样能够被调度,那么就必须要有可供调度使用的参数,这个参数就是线程的优先级。

进程切换的开销到底有哪些?

那么上下文切换的时候,CPU的开销都具体有哪些呢?开销分成两种,一种是直接开销、一种是间接开销

直接开销就是在切换时,cpu必须做的事情,包括:

  • 切换页表全局目录

  • 切换内核态堆栈

  • 切换硬件上下文(进程恢复前,必须装入寄存器的数据统称为硬件上下文)

  • - ip(instruction pointer):指向当前执行指令的下一条指令

    • bp(base pointer): 用于存放执行中的函数对应的栈帧的栈底地址
    • sp(stack poinger): 用于存放执行中的函数对应的栈帧的栈顶地址
    • cr3:页目录基址寄存器,保存页目录表的物理地址
  • 刷新TLB

  • 系统调度器的代码执行

间接开销主要指的是虽然切换到一个新进程后,由于各种缓存并不热,速度运行会慢一些。如果进程始终都在一个CPU上调度还好一些,如果跨CPU的话,之前热起来的TLB、L1、L2、L3因为运行的进程已经变了,所以以局部性原理cache起来的代码、数据也都没有用了,导致新进程穿透到内存的IO会变多。

线程切换的开销和进程相比少在哪里?

在Linux下其实本并没有线程,只是为了迎合开发者口味,搞了个轻量级进程出来就叫做了线程。轻量级进程和进程一样,都有自己独立的task_struct进程描述符,也都有自己独立的pid。从操作系统视角看,调度上和进程没有什么区别,都是在等待队列的双向链表里选择一个task_struct切到运行态而已。只不过轻量级进程和普通进程的区别是可以共享同一内存地址空间、代码段、全局变量、同一打开文件集合而已。

线程切换和进程切换之间的主要区别在于,在线程切换期间,虚拟内存空间保持不变,而在进程切换期间则不然。两种类型都涉及将控制权交给操作系统内核以执行上下文切换。 切换进出OS内核的过程以及切换寄存器的成本是执行上下文切换的最大固定成本。

更模糊的成本是上下文切换与处理器缓存机制混淆。 基本上,当您进行上下文切换时,处理器在其缓存中“记住”的所有内存地址实际上都变得无用。 这里的一个很大的区别是,当您更改虚拟内存空间时,处理器的转换后备缓冲区(TLB)或等效内容会被刷新,从而使内存访问在一段时间内变得更加昂贵。 在线程切换期间不会发生这种情况

两种情况下的主要成本都与缓存污染有关。在大多数情况下,传出线程使用的工作集将与传入线程使用的工作集显着不同。结果,传入的线程将以高速缓存未命中率开始其生命,从而从高速缓存中清除旧的和无用的数据并从存储器加载新数据。对于TLB(在CPU上的Translation Look Aside Buffer)也是如此。在重置虚拟地址空间(线程在不同进程中运行)的情况下,惩罚甚至更糟,因为虚拟地址空间的重置导致刷新整个TLB,即使新线程实际上只需要加载少量新条目。结果,新线程将开始其时间量,有很多TLB未命中和频繁的页面遍历。线程切换的直接成本也是不可忽略的(从大约250到大约1500-2000周期)并且取决于CPU复杂性,它们实际使用的线程和寄存器组的状态。

区别在于调用处理新旧任务的内存描述符的switch_mm() 在线程的情况下,虚拟内存地址空间不变(线程共享虚拟内存),因此必须做很少的事情,因此成本更低。

线程进程之间上下文切换开销的差异在哪?

top Created with Sketch.