当前位置: 首页 > news >正文

内核线程、轻量级进程、用户线程三种线程概念解惑(线程≠轻量级进程)【转】...

转自:http://blog.csdn.net/gatieme/article/details/51481863

  1. 线程与进程概念
  2. 线程概念的产生
    1. 传统单线程进程的缺点
    2. 多线程的优缺点
    3. 线程的设计过程演变
      1. SMP机器上多线程的并行性
      2. 线程模型核心级线程和用户级线程
      3. 目前的实现策略
  3. 三种线程概念内核线程轻量级进程用户线程
    1. 内核线程
    2. 轻量级进程
    3. 用户线程
    4. 加强版的用户线程用户线程LWP
    5. Linux使用的线程库
 

转载

关于进程、线程和轻量级进程的一些笔记

[维基百科-轻量级进程]https://en.wikipedia.org/wiki/Light-weight_process#See_also

线程与进程概念


在现代操作系统中,进程支持多线程。

  • 进程是资源管理的最小单元;

  • 线程是程序执行的最小单元。

即线程作为调度和分配的基本单位,进程作为资源分配的基本单位

一个进程的组成实体可以分为两大部分:线程集和资源集。进程中的线程是动态的对象;代表了进程指令的执行。资源,包括地址空间、打开的文件、用户信息等等,由进程内的线程共享。

线程概念的产生


传统单线程进程的缺点


  1. 现实中有很多需要并发处理的任务,如数据库的服务器端、网络服务器、大容量计算等。

  2. 传统的UNIX进程是单线程的,单线程意味着程序必须是顺序执行,不能并发;既在一个时刻只能运行在一个处理器上,因此不能充分利用多处理器框架的计算机。

  3. 如果采用多进程的方法,则有如下问题:

    • fork一个子进程的消耗是很大的,fork是一个昂贵的系统调用,即使使用现代的写时复制(copy-on-write)技术。
    • 各个进程拥有自己独立的地址空间,进程间的协作需要复杂的IPC技术,如消息传递和共享内存等。

多线程的优缺点


多线程的优点和缺点实际上是对立统一的。

支持多线程的程序(进程)可以取得真正的并行(parallelism),且由于共享进程的代码和全局数据,故线程间的通信是方便的。它的缺点也是由于线程共享进程的地址空间,因此可能会导致竞争,因此对某一块有多个线程要访问的数据需要一些同步技术。

线程的设计过程演变


在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持SMP以及减小(进程/线程)上下文切换开销。

SMP机器上多线程的并行性


无论按照怎样的分法,一个进程至少需要一个线程作为它的指令执行体,进程管理着资源(比如cpu、内存、文件等等),而将线程分配到某个cpu上执 行。

一个进程当然可以拥有多个线程,此时,如果进程运行在SMP机器上,它就可以同时使用多个cpu来执行各个线程,达到最大程度的并行,以提高效率;同 时,即使是在单cpu的机器上,采用多线程模型来设计程序,正如当年采用多进程模型代替单进程模型一样,使设计更简洁、功能更完备,程序的执行效率也更 高,例如采用多个线程响应多个输入,而此时多线程模型所实现的功能实际上也可以用多进程模型来实现,而与后者相比,线程的上下文切换开销就比进程要小多 了,从语义上来说,同时响应多个输入这样的功能,实际上就是共享了除cpu以外的所有资源的。

线程模型–核心级线程和用户级线程


针对线程模型的两大意义,分别开发出了核心级线程用户级线程两种线程模型,分类的标准主要是线程的调度者在核内还是在核外。前者更利于并发使用多处理器的资源,而后者则更多考虑的是上下文切换开销。

关于线程的实现模型,可以参见博主的另外一篇博客线程的3种实现方式–内核级线程, 用户级线程和混合型线程

目前的实现策略


在目前的商用系统中,通常都将两者结合起来使用,既提供核心线程以满足smp系统的需要,也支持用线 程库的方式在用户态实现另一套线程机制,此时一个核心线程同时成为多个用户态线程的调度者。

正如很多技术一样,”混合”通常都能带来更高的效率,但同时也 带来更大的实现难度,出于”简单”的设计思路,Linux从一开始就没有实现混合模型的计划,但它在实现上采用了另一种思路的”混合”。

在线程机制的具体实现上,可以在操作系统内核上实现线程,也可以在核外实现,后者显然要求核内至少实现了进程,而前者则一般要求在核内同时也支持进 程。核心级线程模型显然要求前者的支持,而用户级线程模型则不一定基于后者实现。这种差异,正如前所述,是两种分类方式的标准不同带来的。

当核内既支持进程也支持线程时,就可以实现线程-进程的”多对多”模型,即一个进程的某个线程由核内调度,而同时它也可以作为用户级线程池的调度 者,选择合适的用户级线程在其空间中运行。这就是前面提到的”混合”线程模型,既可满足多处理机系统的需要,也可以最大限度的减小调度开销。

绝大多数商业 操作系统(如Digital Unix、Solaris、Irix)都采用的这种能够完全实现POSIX1003.1c标准的线程模型。在核外实现的线程又可以分为”一对一”、”多对 一”两种模型,前者用一个核心进程(也许是轻量进程)对应一个线程,将线程调度等同于进程调度,交给核心完成,而后者则完全在核外实现多线程,调度也在用 户态完成。后者就是前面提到的单纯的用户级线程模型的实现方式,显然,这种核外的线程调度器实际上只需要完成线程运行栈的切换,调度开销非常小,但同时因 为核心信号(无论是同步的还是异步的)都是以进程为单位的,因而无法定位到线程,所以这种实现方式不能用于多处理器系统,而这个需求正变得越来越大,因 此,在现实中,纯用户级线程的实现,除算法研究目的以外,几乎已经消失了。

linux内核只提供了轻量进程的支持,限制了更高效的线程模型的实现,但Linux着重优化了进程的调度开销,一定程度上也弥补了这一缺陷。目前 最流行的线程机制LinuxThreads所采用的就是线程-进程”一对一”模型,调度交给核心,而在用户级实现一个包括信号处理在内的线程管理机制。

三种线程概念——内核线程、轻量级进程、用户线程


内核线程


内核线程就是内核的分身,一个分身可以处理一件特定事情。这在处理异步事件如异步IO时特别有用。内核线程的使用是廉价的,唯一使用的资源就是内核栈和上下文切换时保存寄存器的空间。支持多线程的内核叫做多线程内核(Multi-Threads kernel )。

内核线程只运行在内核态,不受用户态上下文的拖累。

  • 处理器竞争:可以在全系统范围内竞争处理器资源;

  • 使用资源:唯一使用的资源是内核栈和上下文切换时保持寄存器的空间

  • 调度:调度的开销可能和进程自身差不多昂贵

  • 同步效率:资源的同步和数据共享比整个进程的数据同步和共享要低一些。

轻量级进程


轻量级进程(LWP)是建立在内核之上并由内核支持的用户线程,它是内核线程的高度抽象,每一个轻量级进程都与一个特定的内核线程关联。内核线程只能由内核管理并像普通进程一样被调度。

a LWP runs in user space on top of a single kernel thread and shares its address space and system resources with other LWPs within the same process

轻量级进程由clone()系统调用创建,参数是CLONE_VM,即与父进程是共享进程地址空间和系统资源。

与普通进程区别:LWP只有一个最小的执行上下文和调度程序所需的统计信息。

  • 处理器竞争:因与特定内核线程关联,因此可以在全系统范围内竞争处理器资源

  • 使用资源:与父进程共享进程地址空间

  • 调度:像普通进程一样调度

轻量级线程(LWP)是一种由内核支持的用户线程。它是基于内核线程的高级抽象,因此只有先支持内核线程,才能有LWP。每一个进程有一个或多个LWPs,每个LWP由一个内核线程支持。这种模型实际上就是恐龙书上所提到的一对一线程模型。在这种实现的操作系统中,LWP就是用户线程。

由于每个LWP都与一个特定的内核线程关联,因此每个LWP都是一个独立的线程调度单元。即使有一个LWP在系统调用中阻塞,也不会影响整个进程的执行。

轻量级进程具有局限性。

  • 首先,大多数LWP的操作,如建立、析构以及同步,都需要进行系统调用。系统调用的代价相对较高:需要在user mode和kernel mode中切换。

  • 其次,每个LWP都需要有一个内核线程支持,因此LWP要消耗内核资源(内核线程的栈空间)。因此一个系统不能支持大量的LWP。

这里写图片描述

注:

  1. LWP的术语是借自于SVR4/MP和Solaris 2.x。
  2. 有些系统将LWP称为虚拟处理器。
  3. 将之称为轻量级进程的原因可能是:在内核线程的支持下,LWP是独立的调度单元,就像普通的进程一样。所以LWP的最大特点还是每个LWP都有一个内核线程支持。

用户线程


用户线程是完全建立在用户空间的线程库,用户线程的创建、调度、同步和销毁全又库函数在用户空间完成,不需要内核的帮助。因此这种线程是极其低消耗和高效的。

  • 处理器竞争:单纯的用户线程是建立在用户空间,其对内核是透明的,因此其所属进程单独参与处理器的竞争,而进程的所有线程参与竞争该进程的资源。

  • 使用资源:与所属进程共享进程地址空间和系统资源。

  • 调度:由在用户空间实现的线程库,在所属进程内进行调度

LWP虽然本质上属于用户线程,但LWP线程库是建立在内核之上的,LWP的许多操作都要进行系统调用,因此效率不高。而这里的用户线程指的是完全建立在用户空间的线程库,用户线程的建立,同步,销毁,调度完全在用户空间完成,不需要内核的帮助。因此这种线程的操作是极其快速的且低消耗的。

这里写图片描述

上图是最初的一个用户线程模型,从中可以看出,进程中包含线程,用户线程在用户空间中实现,内核并没有直接对用户线程进程调度,内核的调度对象和传统进程一样,还是进程本身,内核并不知道用户线程的存在。

用户线程之间的调度由在用户空间实现的线程库实现。

这种模型对应着恐龙书中提到的多对一线程模型,其缺点是一个用户线程如果阻塞在系统调用中,则整个进程都将会阻塞。

加强版的用户线程——用户线程+LWP


这种模型对应着恐龙书中多对多模型。

用户线程库还是完全建立在用户空间中,因此用户线程的操作还是很廉价,因此可以建立任意多需要的用户线程。

操作系统提供了LWP作为用户线程和内核线程之间的桥梁。LWP还是和前面提到的一样,具有内核线程支持,是内核的调度单元,并且用户线程的系统调用要通过LWP,因此进程中某个用户线程的阻塞不会影响整个进程的执行。

用户线程库将建立的用户线程关联到LWP上,LWP与用户线程的数量不一定一致。当内核调度到某个LWP上时,此时与该LWP关联的用户线程就被执行。

这里写图片描述

Linux使用的线程库


LinuxThreads是用户空间的线程库,所采用的是线程-进程1对1模型(即一个用户线程对应一个轻量级进程,而一个轻量级进程对应一个特定的内核线程),将线程的调度等同于进程的调度,调度交由内核完成,而线程的创建、同步、销毁由核外线程库完成(LinuxThtreads已绑定到 GLIBC中发行)。

在LinuxThreads中,由专门的一个管理线程处理所有的线程管理工作。当进程第一次调用pthread_create()创建线程时就会先 创建(clone())并启动管理线程。后续进程pthread_create()创建线程时,都是管理线程作为pthread_create()的调用者的子线程,通过调用clone()来创建用户线程,并记录轻量级进程号和线程id的映射关系,因此,用户线程其实是管理线程的子线程。

LinuxThreads只支持调度范围为PTHREAD_SCOPE_SYSTEM的调度,默认的调度策略是SCHED_OTHER。 
用户线程调度策略也可修改成SCHED_FIFO或SCHED_RR方式,这两种方式支持优先级为0-99,而SCHED_OTHER只支持0。

  • SCHED_OTHER 分时调度策略,

  • SCHED_FIFO 实时调度策略,先到先服务

  • SCHED_RR 实时调度策略,时间片轮转

SCHED_OTHER是普通进程的,后两个是实时进程的(一般的进程都是普通进程,系统中出现实时进程的机会很少)。SCHED_FIFO、 SCHED_RR优先级高于所有SCHED_OTHER的进程,所以只要他们能够运行,在他们运行完之前,所有SCHED_OTHER的进程的都没有得到 执行的机会

小结:

很多文献中都认为轻量级进程就是线程,实际上这种说法并不完全正确,从前面的分析中可以看到,只有在用户线程完全由轻量级进程构成时,才可以说轻量级进程就是线程。

【作者】 张昺华
【出处】 http://www.cnblogs.com/sky-heaven/
【博客园】 http://www.cnblogs.com/sky-heaven/
【新浪博客】 http://blog.sina.com.cn/u/2049150530
【知乎】 http://www.zhihu.com/people/zhang-bing-hua
【我的作品---旋转倒立摆】 http://v.youku.com/v_show/id_XODM5NDAzNjQw.html?spm=a2hzp.8253869.0.0&from=y1.7-2
【我的作品---自平衡自动循迹车】 http://v.youku.com/v_show/id_XODM5MzYyNTIw.html?spm=a2hzp.8253869.0.0&from=y1.7-2
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

相关文章:

  • 试用友盟SDK实现Android分享微信朋友圈
  • 程序员书单【持续更新】
  • 烂代码传奇
  • Docker的安装和测试
  • React-Native - 收藏集 - 掘金
  • java基础-数组的折半查找原理
  • Ubuntu设置屏幕分辨率
  • Python 入门学习 -----变量及基础类型(元组,列表,字典,集合)
  • PowerDesigner16的使用
  • linux 下的动态库制作 以及在python 中如何调用 c 函数库
  • Asp.Net 用户验证(自定义IPrincipal和IIdentity)
  • mybatis标签之——trim
  • 【laravel5.4】 Composer移除依赖
  • IP-MAC绑定后,能禁止私接路由和随身WIFI吗?
  • c:关于 #include
  • 2018以太坊智能合约编程语言solidity的最佳IDEs
  • 2019.2.20 c++ 知识梳理
  • Android框架之Volley
  • angular学习第一篇-----环境搭建
  • canvas绘制圆角头像
  • Git同步原始仓库到Fork仓库中
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • Javascript Math对象和Date对象常用方法详解
  • java取消线程实例
  • Java知识点总结(JDBC-连接步骤及CRUD)
  • Laravel 菜鸟晋级之路
  • Redis中的lru算法实现
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • vue 配置sass、scss全局变量
  • 回顾 Swift 多平台移植进度 #2
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 来,膜拜下android roadmap,强大的执行力
  • 模型微调
  • 前言-如何学习区块链
  • 使用 5W1H 写出高可读的 Git Commit Message
  • 原生js练习题---第五课
  • 在weex里面使用chart图表
  • 怎么将电脑中的声音录制成WAV格式
  • Java总结 - String - 这篇请使劲喷我
  • 积累各种好的链接
  • ​linux启动进程的方式
  • ​云纳万物 · 数皆有言|2021 七牛云战略发布会启幕,邀您赴约
  • #stm32整理(一)flash读写
  • $con= MySQL有关填空题_2015年计算机二级考试《MySQL》提高练习题(10)
  • (2)nginx 安装、启停
  • (delphi11最新学习资料) Object Pascal 学习笔记---第2章第五节(日期和时间)
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (第27天)Oracle 数据泵转换分区表
  • (二)linux使用docker容器运行mysql
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (附源码)ssm跨平台教学系统 毕业设计 280843
  • (九)c52学习之旅-定时器