操作系统之线程

2023-11-07

一、线程简介

早期的计算机系统只允许一个任务独占系统资源, 一次只能执行一个程序。由于对程序并发执行的需求,引入了多进程。进程的引入可以解决多任务支持的问题,但是也产生了新的问题:每个进程分别分配资源开销比较大,进程频繁切换导致额外系统开销,进程间的通信实现复杂。考虑现实中的场景:
一个word程序如果采用多进程,一个进程负责界面交互,一个进程负责后台运算,会相当低效 (进程通信不好实现 进程频繁切换导致额外的系统开销)。一个同时要处理大量请求的网络数据库如果采用多进程,对每个请求都创建一个进程去响应那服务器的资源很快就耗尽了,而且进程切换消耗很大。
由此就演化出了在一个进程的内存空间上开辟多个"小进程",利用这些小进程来实现多个任务的方法,这些小进程就是所谓的线程。这些线程在进程的内存空间内共享很多进程的资源,所以每个线程分配资源开销不会很大。线程的规模较小,切换开销也不会很大。线程之间共享进程的一部分地址空间,线程之间的通信也不会很麻烦。从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。

线程是进程里面的一个执行序列,每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。同一个进程中的多个线程可以并发执行,一个线程可以创建和撤销另一个线程,但是线程不能够独立执行,必须依存在进程中。每个进程运行时都会创建一个主线程,也叫主控线程,通过主控线程可以继续创建其他子线程。
进程是资源管理的最小单位,线程是程序执行调度的最小单位。
进程的实现只能由操作系统内核来实现,而不存在用户态实现的情况。线程既可以通过内核来实现 也可通过用户态来实现。因为线程的管理者可以是用户也可以是操作系统本身,线程是进程内部的东西,当然存在由进程直接管理线程的可能性,因此线程的实现就应该分为内核态线程实现和用户态线程实现。
多线程之间切换消耗资源少,但是不稳定 一个线程崩溃了会影响整个进程;多进程之间切换消耗资源多,但是稳定 一个进程崩溃不会影响其他进程。

协程是一种比线程更加轻量级的存在,正如一个进程可以拥有多个线程,一个线程也可以拥有多个协程。 协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。可以在线程内并发执行,又不会引起安全问题。

二、线程资源

线程间共享的资源:内核区:文件描述符表,每种信号的处理方式,当前工作目录;用户区:堆区,数据区( bss段,Data段Test 段) 

线程间独占的资源
1.线程ID 
每个线程都有自己的线程ID,这个ID在本进程中是唯一的。进程用此来标识线程。
2.寄存器组的值 
线程间是并发运行的,每个线程有自己不同的运行上下文,当从一个线程切换到另一个线程上时,必须将原有的线程的寄存器集合的状态保存,以便将来该线程重新切换时能恢复。
4.错误返回码 
同一个进程中有很多个线程在同时运行,可能某个线程进行系统调用后设置了errno值,而该线程还没有处理这个错误,另外一个线程就在此时被调度器投入运行,这样错误值就有可能被修改。所以不同的线程应该拥有自己的错误返回码变量。
5.线程的信号屏蔽码 
每个线程感兴趣的信号不同,所以线程的信号屏蔽码应该由线程自己管理。但所有的线程都共享同样的信号处理器。
6.线程的优先级 
线程需要像进程那样能够被调度,那么就必须要有可供调度使用的参数,这个参数就是线程的优先级。
7.线程的栈 
一个栈中只有最下方的帧可被读写,相应的也只有该帧对应的那个函数被激活,处于工作状态。为了实现多线程必须绕开栈的限制。为此在创建新的线程时, 要为这个线程建新的栈,每个栈对应一个线程。当某个栈执行到全部弹出时,对应线程完成任务。多线程的进程在内存中有多个栈,多个栈之间以固定的区域隔开,以备栈的增长。每个线程可调用自己栈下方的帧中的参数和变量。

三、线程切换

(1)一般的进程切换分为两步 :1)切换页目录使用新的地址空间;2)切换内核栈和硬件上下文。对于Linux来讲,地址空间是线程和进程的最大区别,如果是线程切换的话,不需要切换页目录使用新的地址空间。但是切换内核栈和硬件上下文则是线程切换和进程切换都需要做的。
(2)切换进程上下文:
进程上下文可以分为三个部分: 
用户级上下文:  正文、数据、用户堆栈以及共享存储区;
寄存器上下文:  通用寄存器、程序寄存器(IP)、处理器状态寄存器(EFLAGS)、栈指针(ESP);
系统级上下文:  进程控制块task_struct、内存管理信息(mm_struct、vm_area_struct、pgd、pte)、内核栈。
系统中的每一个进程都有自己的上下文。一个正在使用处理器运行的进程称为当前进程。当前进程因时间片用完或者因等待某个事件而阻塞时,进程调度需要把处理器的使用权从当前进程交给另一个进程,这个过程叫做进程切换。此时,被调用进程成为当前进程。在进程切换时系统要把当前进程的上下文保存在指定的内存区域(该进程的任务状态段TSS中),然后把下一个使用处理器运行的进程的上下文设置成当前进程的上下文。当一个进程经过调度再次使用CPU运行时,系统要恢复该进程保存的上下文。进程的切换也就是上下文切换。
(3)线程切换:
Linux下的线程实质上是轻量级进程。线程生成时会生成对应的进程控制结构,只是该结构与父线程的进程控制结构共享了同一个进程内存空间。同时新线程的进程控制结构将从父线程(进程)处复制得到同样的进程信息,如打开文件列表和信号阻塞掩码等。创建线程比创建新进程成本低,因为新创建的线程使用的是当前进程的地址空间。相对在进程之间切换,在线程之间切换所需的时间更少,因为后者不包括地址空间的切换。
线程上下文切换的原理与此类似,只是线程在同一地址空间中,不需要MMU等切换,只需要切换必要的CPU寄存器,因此,线程切换比进程切换快的多。

四、线程的用户级和内核级

进程的实现只能由操作系统内核来实现,而不存在用户态实现的情况。但是线程的管理者可以是用户也可以是操作系统本身,因此线程的实现分为内核态线程实现和用户态线程实现。

线程是进程的不同执行序列,也就是说线程是独立运行的基本单位,也是CPU调度的基本单位。那么操作系统是如何实现管理线程的?

首先操作系统像管理进程一样维护线程的所有资源,将线程控制块存放在操作系统的内核空间中,此时操作系统就同时掌管进程控制块和线程控制块。操作系统管理线程的好处是:

1.用户编程简单;
2.如果一个线程执行阻塞操作,操作系统可以从容的调度另外一个线程的执行。

内核线程的实现缺点是:

1.效率低,因为线程在内核态实现,每次线程切换都需要陷入到内核由操作系统来调度,而由用户态切换到内核态要花很多时间。另外内核态实现会占用内核稀有的资源,因为操作系统要维护线程列表,操作系统所占内核空间一旦装载后就无法动态改变,并且线程的数量远远大于进程的数量,随着线程数的增加内核将耗尽;

2.内核态的实现需要修改操作系统。

用户态是如何实现管理线程的?用户态管理线程就是用户自己做线程的切换,自己管理线程的信息,操作系统无需知道线程的存在。在用户态下进行线程的管理需要用户创建一个调度线程。一个线程在执行完一段时间后主动把资源释放给其他线程使用,而在内核态下则无需如此,因为操作系统可通过周期性的时钟中断把控制权夺过来

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

操作系统之线程 的相关文章

随机推荐

  • springsecurity 获取token流程分析

    一 oauth token password模式获取token源码主要操作 1 定义请求参数 增加请求头 Authorization 否则 在请求参数使用 client id 注意 此头为base64拼接格式为 client id clie
  • 光耦隔离继电器驱动

    一 前言 因为吃亏了 而且不小 发个贴记录下 以后长记性 问题是由于不做光耦隔离 电路不稳定 做了光耦隔离 上电和断电瞬间被隔离器件上电抖动一下 明显不可以 错误的示范我就不发图了 二 环境 12V电源 stm32 驱动舵机 功率不大也不小
  • 一篇文章,从源码深入详解ThreadLocal内存泄漏问题

    原创文章 经验总结 从校招到A厂一路阳光一路沧桑 详情请戳www coderccc com 1 造成内存泄漏的原因 threadLocal是为了解决对象不能被多线程共享访问的问题 通过threadLocal set方法将对象实例保存在每个线
  • 在ISO/OSI参考模型中,网络层的主要功能是()----百度2016研发工程师笔试题(六)

    在ISO OSI参考模型中 网络层的主要功能是 正确答案 A 你的答案 A 正确 路由选择 拥塞控制与网络互连 提供可靠的端一端服务 透明地传送报文 数据格式变换 数据加密与解密 数据压缩与恢复 在通信实体之间传送以帧为单位的数据 添加笔记
  • Dynamics 365 安装后续

    之前说到Dynamics365安装时遇到的错误 这里将讲述最后一个错误的解决办法 这里指的是蓝色条选中的错误 根据错误的提示 我们需要去对Reporting Services进行安装和配置 首先说Reporting Services的安装
  • 树莓派Linux内核源码

    前期工作 配置好交叉编译工具链 交叉编译工具链详细教程 树莓派Linux内核源码下载 查看树莓派操作版本命令 uname r 源码下载 一 嵌入式裸机和带操作系统启动过程了解 C51 stm32启动过程 c直接操控底层寄存器实现相关业务 业
  • python代码编写规范

    最近两年的工作都是和运维相关 有时运维人员也会写一些python程序 但基本上都没有遵循相应的代码规范 一向粗暴 能用就行 既不考虑可读性也不考虑可维护性 作为一个开发人员有时候看他们写代码就很不舒服 今天就谈谈python写代码时的一些规
  • beforefieldinit释义

    首先让我们认识什么是 当字段被标记为beforefieldinit类型时 该字段初始化可以发生在任何时候任何字段被引用之前 这句话听起了有点别扭 接下来让我们通过具体的例子介绍
  • 黑苹果oc清除nvram_黑苹果完善之路-英特尔蓝牙「开启/关闭」功能完善教程

    小编开篇提醒 玩转黑苹果有风险 建议随时备份好自己的重要数据 很多机友安装黑苹果之后 打开蓝牙选项却发现 关闭蓝牙 选项是灰色的 无法进行选择 但是一些具有强迫症的机友内心可能会接受不了 所以就此出一期关于英格尔蓝牙驱动相关的详细教程 博通
  • 10 种机器学习算法的要点(附 Python 和 R 代码)

    http blog jobbole com 92021 本文由 伯乐在线 Agatha 翻译 唐尤华 校稿 未经许可 禁止转载 英文出处 SUNIL RAY 欢迎加入 翻译组 前言 谷歌董事长施密特曾说过 虽然谷歌的无人驾驶汽车和机器人受到
  • 什么是 IaaS? 基础结构即服务

    基础结构即服务 IaaS 是通过 Internet 配置和管理的即时计算基础结构 它是四类云服务中的一种 其余三种为软件即服务 SaaS 平台即服务 PaaS 和无服务器 IaaS 可根据需求快速纵向扩缩 你只需按实际使用量付费 它让你无需
  • 甘特图生产排程(APS)定制开发

    快速开发完成APS的数据可视化 订单展示 资源调度 智能排程等几乎全部功能模块 自动智能排程功能 提供专业需求分析师及开发团队 按需开发 全自动智能排程 这一APS的主要功能 能够实现自定义排程规则 一键式产生计划等高级功能 无需人工干预既
  • python虚拟环境理解 conda创建虚拟环境 pycharm配置运行环境

    python虚拟环境理解 conda创建虚拟环境 pycharm配置运行环境 一 什么是python的虚拟环境 python虚拟环境是为了让不同的项目能够在不同的python环境中运行 比如项目A需要python2 numpy3 1 项目B
  • Openwrt学习笔记(四)——系统开机启动

    1 内核启动 bootloader将kernel从flash中拷贝到RAM以后 bootloader将退出舞台 并将这个舞台交给了kernel 中间有些交接的细节过程 这里不赘述 我们直接从kernel的启动开始分析 不同平台的kernel
  • 算法专题之矩阵

    前言 矩阵相关的算法在互联网世界有着广泛的应用 比如图片的像素修改 上一小结介绍过 获取地图路径方案等 在数学中 一个矩阵说穿了就是一个二维数组 矩阵相关的基础算法也都是基于二维数组的基础上完成各类数据操作 本小节列举了前端面试中高频出现的
  • Error:fatal error C1010: unexpected end of file while looking for precompiled head

    场景 在VC6 0进行编写C 代码时 创建了一个 简单的程序 s 然后编译就爆出这个错误 场景复现 创建流程 点击左上角的 文件 然后点击 新建 在左上方选择工程 然后下方选择 Win32 Console Application 在右侧填写
  • 接口参数返回值 根据不同的值去改变样式

    改变接口返回值的样式 div class btn scope row notificationStatus div css部分 noticion active border 1px
  • ReactJS报错 React createElement error type is invalid — expected a string ...

    2019独角兽企业重金招聘Python工程师标准 gt gt gt Warning React createElement type is invalid expected a string for built in components
  • Lua中使用毫秒精度时间的方法

    这篇文章主要介绍了lua中使用毫秒精度时间的方法 本文讲解使用luasocket库实现毫秒精度时间 需要的朋友可以参考下 lua自带的时间函数只能到秒的精度 为了统计到毫秒精度的时间 可以使用luasocket 下载地址http files
  • 操作系统之线程

    一 线程简介 早期的计算机系统只允许一个任务独占系统资源 一次只能执行一个程序 由于对程序并发执行的需求 引入了多进程 进程的引入可以解决多任务支持的问题 但是也产生了新的问题 每个进程分别分配资源开销比较大 进程频繁切换导致额外系统开销