本文从linux中的进程、线程实现原理开始,扩展到linux线程模型,最后简单解释线程切换的成本。

刚开始学习,不一定对,好心人们快来指正我啊啊啊!!!

linux中的进程与线程

首先明确进程与进程的基本概念:

  • 进程是资源分配的基本单位
  • 线程是CPU调度的基本单位
  • 一个进程下可能有多个线程
  • 线程共享进程的资源

基本原理

linux用户态的进程、线程基本满足上述概念,但内核态不区分进程和线程。可以认为,内核中统一执行的是进程,但有些是“普通进程”(对应进程process),有些是“轻量级进程”(对应线程pthread或npthread),都使用task_struct结构体保存保存。

使用fork创建进程,使用pthread_create创建线程。两个系统调用最终都都调用了do_dork,而do_dork完成了task_struct结构体的复制,并将新的进程加入内核调度。

进程是资源分配的基本单位、线程共享进程的资源

普通进程需要深拷贝虚拟内存、文件描述符、信号处理等;而轻量级进程之所以“轻量”,是因为其只需要浅拷贝虚拟内存等大部分信息,多个轻量级进程共享一个进程的资源。

线程是CPU调度的基本单位、一个进程下可能有多个线程

linux加入了线程组的概念,让原有“进程”对应线程,“线程组”对应进程,实现“一个进程下可能有多个线程”:

  • 操作系统中存在多个进程组
  • 一个进程组下有多个进程(1:n)
  • 一个进程对应一个线程组(1:1)
  • 一个线程组下有多个线程(1:n)

task_struct中,使用pgid标的进程组,tgid标的线程组,pid标的进程或线程。假设目前有一个进程组,则上述概念对应如下:

  • 进程组中有一个主进程(父进程),pid等于进程组的pgid;进程组下的其他进程都是父进程的子进程,pid不等于pgid
  • 每个进程对应一个线程组,pid等于tgid。
  • 线程组中有一个“主线程”(勉强称为“主线程”,位的是与主进程对应;语义上绝不能称为“父线程”),pid等于该线程组的tgid;线程组下的其他线程都是与主线程平级,pid不等于tgid

因此,调用getpgid返回pgid,调用getpid应返回tgid,调用gettid应返回pid。使用的时候不要糊涂。

进程下除主线程外的其他线程是CPU调度的基本单位,这很好理解。而所谓主线程与所属进程实际上是同一个task_struct,也能被CPU调度,因此主线程也是CPU调度的基本单位。

tgid相同的所有线程组成了概念上的“进程”,只有主线程在创建时会实际分配资源,其他线程通过浅拷贝共享主线程的资源。结合前面介绍的普通线程与轻量级进程,实现“进程是资源分配的基本单位”。

举个栗子

pgid tgid pid
111 111 111
112 112 112
112 112 113
113 113 113
113 113 114
113 115 115
113 115 116
113 115 117
  • 存在3个进程组111、112、113

    • 进程组111下有1个父进程111,单独分配资源

      • 进程111下有1个线程111,共享进程111的资源
    • 进程组112下有1个父进程112,单独分配资源
      • 进程112下有2个线程112、113,共享进程112的资源
    • 进程组113下有1个父进程113,1个子进程115,各自单独分配资源
      • 进程113下有2个线程113、114,共享进程113的资源
      • 进程115下有3个线程115、116、117,共享进程115的资源

小结

现在再来理解linux中的进程与线程就容易多了:

  • 进程是一个逻辑上的概念,用于管理资源,对应task_struct中的资源
  • 每个进程至少有一个线程,用于具体的执行,对应task_struct中的任务调度信息
  • task_struct中的pid区分线程,tgid区分进程,pgid区分进程组

linux线程模型

一对一

LinuxThreads与NPTL均采用一对一的线程模型,一个用户线程对应一个内核线程。内核负责每个线程的调度,可以调度到其他处理器上面。Linux 2.6默认使用NPTL线程库,一对一的线程模型

优点:

  • 实现简单。

缺点:

  • 对用户线程的大部分操作都会映射到内核线程上,引起用户态和内核态的频繁切换。
  • 内核为每个线程都映射调度实体,如果系统出现大量线程,会对系统性能有影响。

多对一

顾名思义,多对一线程模型中,多个用户线程对应到同一个内核线程上,线程的创建、调度、同步的所有细节全部由进程的用户空间线程库来处理。

优点:

  • 用户线程的很多操作对内核来说都是透明的,不需要用户态和内核态的频繁切换。使线程的创建、调度、同步等非常快。

缺点:

  • 由于多个用户线程对应到同一个内核线程,如果其中一个用户线程阻塞,那么该其他用户线程也无法执行。
  • 内核并不知道用户态有哪些线程,无法像内核线程一样实现较完整的调度、优先级等

多对多

多对一线程模型是非常轻量的,问题在于多个用户线程对应到固定的一个内核线程。多对多线程模型解决了这一问题:m个用户线程对应到n个内核线程上,通常m>n。由IBM主导的NGPT采用了多对多的线程模型,不过现在已废弃。

优点:

  • 兼具多对一模型的轻量
  • 由于对应了多个内核线程,则一个用户线程阻塞时,其他用户线程仍然可以执行
  • 由于对应了多个内核线程,则可以实现较完整的调度、优先级等

缺点:

  • 实现复杂

线程切换

linux采用一对一的线程模型,用户线程切换与内核线程切换之间的差别非常小。同时,如果忽略用户主动放弃用户线程的执行权(yield)带来的开销,则只需要考虑内核线程切换的开销。

注意,这里仅仅是为了帮助理解做出的简化。实际上,用户线程库在用户线程的调度、同步等过程中做了很多工作,这部分开销不能忽略

如JVM对Thread#yield()的解释:如果底层OS不支持yield的语义,则JVM让用户线程自旋至时间片结束,线程被动切换,以达到相似的效果

什么引起线程切换

  • 时间片轮转
  • 线程阻塞
  • 线程主动放弃时间片

线程切换的开销

直接开销

直接开销是线程切换本身引起的,无可避免,必然发生。

用户态与内核态的切换

线程切换只能在内核态完成,如果当前用户处于用户态,则必然引起用户态与内核态的切换。(“用户态与内核态的切换”具体带来什么成本???

上下文切换

前面说线程(或者叫做进程都随意)信息需要用一个task_struct保存,线程切换时,必然需要将旧线程的task_struct从内核切出,将新线程的切入,带来上下文切换。除此之外,还需要切换寄存器、程序计数器、线程栈(包括操作栈、数据栈)等。

线程调度算法

线程调度算法需要管理线程的状态、等待条件等,如果根据优先级调度,则还需要维护优先级队列。如果线程切换比较频繁,该成本不容小觑。

间接开销

间接开销是直接开销的副作用,取决于系统实现和用户代码实现。

缓存缺失

切换进程,需要执行新逻辑。如果二者的访问的地址空间不相近,则会引起缓存缺失,具体影响范围取决于系统实现和用户代码实现。如果系统的缓存较大,则能减小缓存缺失的影响;如果用户线程访问数据的地址空间接近,则本身的缓存缺失率也比较低。

对页表等快慢表式结构同理。


参考:

  • Linux 线程实现机制分析
  • 历史上线程的3种实现模型
  • 从Java视角理解系统结构(一)CPU上下文切换
  • 从Java视角理解系统结构(二)CPU缓存
  • 从Java视角理解系统结构(三)伪共享

本文链接:浅谈linux线程模型和线程切换
作者:猴子007
出处:monkeysayhi.github.io
本文基于 知识共享署名-相同方式共享 4.0 国际许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名及链接。

浅谈linux线程模型和线程切换相关推荐

  1. 浅谈 Linux 高负载的系统化分析

    简介: 浅谈 Linux 高负载的系统化分析,阿里云系统组工程师杨勇通过对线上各种问题的系统化分析. 讲解 Linux Load 高如何排查的话题属于老生常谈了,但多数文章只是聚焦了几个点,缺少整体排 ...

  2. Linux先发送条件变量,浅谈Linux条件变量的使用

    Linux线程同步之间存在多种机制,条件变量是一种类似操作系统里提到的生产者-消费者算法的同步机制,允许线程以无竞争的方式等待特定条件的发生. 示例伪代码: void* Thread1(void){ ...

  3. Linux系统常用函数,浅谈linux下的一些常用函数的总结(必看篇)

    1.exit()函数 exit(int n)  其实就是直接退出程序, 因为默认的标准程序入口为int main(int argc, char** argv),返回值是int型的. 一般在shell下 ...

  4. linux的多任务 多进程,浅谈linux模拟多线程崩溃和多进程崩溃

    结论是: 多线程下如果其中一个线程崩溃了会导致其他线程(整个进程)都崩溃: 多进程下如果其中一个进程崩溃了对其余进程没有影响: 多线程 #include #include #include #incl ...

  5. Linux先发送条件变量,linux 条件变量 浅谈Linux条件变量的使用

    想了解浅谈Linux条件变量的使用的相关内容吗,在本文为您仔细讲解linux 条件变量的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:linux,条件变量,下面大家一起来学习吧. Linu ...

  6. linux cp mv区别,浅谈Linux下mv和cp命令的区别

    1.功能上的区别 mv:用户可以使用该命令为文件或目录重命名或将文件由一个目录移入另一个目录中. cp: 该命令的功能是将给出的文件或目录拷贝到另一文件或目录中. 2.从inode角度来区分 mv:会 ...

  7. 浅谈Java内存模型、并发、多线程

    浅谈Java内存模型.并发.多线程 Java内存模型(Java Memory Model)是围绕着在并发编程中如何处理原子性,可见性,有序性三个特性而建立的模型. 下面我简单描述一下这三个特性: 原子 ...

  8. linux 易语言窗口程序_浅谈Linux入门的基本知识

    浅谈Linux入门的基本知识 图形模式与文字模式的切换方式Linux预设提供了六个命令窗口终端机让我们来登录. 默认我们登录的就是第一个窗口,也就是tty1,这个六个窗口分别为tty1.tty2 - ...

  9. 浅谈 Linux 系统中的 SNMP Trap 【转】

    文章来源:浅谈 Linux 系统中的 SNMP Trap 简介 本文讲解 SNMP Trap,在介绍 Trap 概念之前,首先认识一下 SNMP 吧. 简单网络管理协议(Simple Network ...

最新文章

  1. 在ASP.Net中如何彻底杀死Excel进程
  2. c语言未命名exe,用dev-c++编译出现问题,求大神解答啊
  3. dubbo k8s 服务发现_服务化改造实践(二)| Dubbo + Kubernetes
  4. 【COCOS2DX隐藏IOS7状态栏】通过添加PLIST KEY隐藏IOS7状态栏
  5. 面试官:你对Redis缓存了解吗?面对这11道面试题是否有很多问号?
  6. 04-01 常见接口协议
  7. 20181211HW
  8. 梁胜:做开源项目的贡献者没有意义 | 人物志
  9. echarts环形图
  10. 数据结构十大排序算法(python)
  11. 安装配置管理 之 JRE 安装和配置,以适合JAVA程序运行所具备的环境
  12. 一次非常成功的项目经验分享和糟糕项目的对比
  13. android输入法剪贴板,手机写作利器:输入法剪贴板
  14. STM32第九课(Input Capture, HAL)
  15. BitTorrent Sync简介
  16. 安装loadrunner时出现”命令行选项语法错误键入命令 \?获得帮助“的解决方法
  17. Re:从0开始的微服务架构:(二)如何快速体验微服务架构?
  18. springboot搭建项目环境以及整合其他技术
  19. Controller层使用@value注解获取不到 yml文件中的properties属性值
  20. 网易云音乐不能加载音乐 解决办法

热门文章

  1. 蓝桥杯 ADV-194 算法提高 盾神与积木游戏 java版
  2. L3-010. 是否完全二叉搜索树-PAT团体程序设计天梯赛GPLT
  3. oracle的nvl和nvl2是什么函数,两者区别
  4. 浅谈分布式存储系统的数据分布算法
  5. 缓存失效和命名是计算机科学两大难题,命名也是一种艺术
  6. 3.07 检测两个表中是否有相同的数据
  7. hdu5651 xiaoxin juju needs help(逆元)
  8. LNMP - nginx代理详解
  9. 基于jQuery鼠标悬停上下滑动导航条
  10. 删除linux系统中的eth0.bak与多余的网卡