目录
- 🌈前言
- 🌸1、Linux线程概念
- 🍡1.1、概念
- 🍢1.2、线程的优点
- 🍧1.3、线程的缺点
- 🍨1.4、线程的异常和用途
- 🌺2、Linux下进程 vs 线程
🌈前言
这篇文章给大家带来线程的学习!!!
🌸1、Linux线程概念
🍡1.1、概念
前言:
-
我们都知道:fork之后,父子代码是共享的,可以通过if else进行分流执行不同的代码块
-
不同的执行流,可以做到进行对特定的资源进行划分
-
父子进程都有自己的地址空间和页表,因为它们之间具有独立性,互不干扰
图解:
-
线程概念:
-
- 在一个程序里的一个执行路线就叫做:线程(thread),更准确的定义是:线程是“一个进程内部的控制序列”
-
- 进程至少都有一个执行线程,进程与线程的比例是 1 比 N
-
- 线程在进程内部运行,本质是在进程地址空间内运行
-
- 在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化
-
- 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流
-
Linux下的线程
-
- 在Linux中进程和线程没有概念上的区分,它们都叫做:执行流。Linux线程(TCB)是用PCB模拟实现的,它复用了PCB的代码
-
- Linux下的TCB就是PCB,因为他们的逻辑结构是一样的
-
Other OS下的线程
-
- 在Other OS中,它们认为进程和线程在执行流层面是不一样了,为此又实现了一个TCB结构体,真线程的维护成本更高
-
- Other OS中的TCB与PCB的关系也百变得错综复杂,难以理清,维护成本高
-
重新理解进程
-
- 前面学习的进程 = 内核数据结构 + 进程对应的代码和数据
-
- 现在学习的进程 = 内核视角:承担分配系统资源的基本实体 (进程的基座属性),即:向系统申请资源的基本单位
-
- Linux没有真正的实现线程,而是用PCB模拟实现,所有没有提供线程接口
-
- 我们之前所学的进程是单执行流进程(内部只有一个task_struct),而线程是多执行流进程(内部包含多个task_struct)
-
- 线程(执行流)是CPU调取的基本单位
下图中:用蓝色框起来的PCB中的多个task_struct、地址空间、页表、物理内存的数据,这一集合叫做:进程
线程也叫轻量级进程,为什么呢?
-
因为在CPU的视角中:进程与线程的比例是 1 比 N
-
task_struct(单执行流进程) <= 传统的PCB(包含1个或多个执行流的进程)
-
进程地址空间的资源会被创建的多个线程所瓜分并且映射到物理内存中,当CPU调度时,只需要执行进程的一部分代码即可
🍢1.2、线程的优点
-
线程优点
-
- 创建一个新线程的代价,要比创建一个进程小得多,因为创建线程只需要将进程地址空间的一部分资源分配给它即可
-
上下文切换:内核在CPU上对进程或者线程进行切换,上下文切换过程中的信息被保存在PCB当中
-
- 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多。因为进程切换时,需要进行上下文和页表的切换,而线程不用进行页表的切换
-
- 线程占用的资源要比进程少很多,因为进程的地址空间的一部分资源会分配给线程
-
- 能充分利用多处理器的可并行数量(能够同时执行多个线程的代码)
-
I/O操作:比如向显示器读写数据、向硬盘读写数据,向网卡读写数据等等…
-
- 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务。因为在申请I/O资源时,可能被其他进程占用,进程需要到对应的资源等待队列中等待
-
计算密集应用:比如有一个软件,你在软件中看电视,并且下载东西,它们同步运行
-
- 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
-
I/O密集型应用:比如OS中的文件编码解码操作,会一直占用CPU的资源
-
- I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作
-
- 进程是串行的,如果发生阻塞就会一直等待资源就绪,多线程是并行的,发生阻塞会进行重叠等待资源就绪
🍧1.3、线程的缺点
-
性能损失
-
- 一个很少被外部事件阻塞的计算密集型线程往往无法与其它线程共享同一个处理器
-
- 如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变
健壮性降低
-
- 编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的
-
- 换句话说,就是:线程之间是缺乏保护的,它们的隔离性差
缺乏访问控制
-
- 进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响
-
- 比如:创建多个线程,线程都往显示器打印数据,可以看出数据都是乱的,跟fork一样
编程难度提高
-
- 编写与调试一个多线程程序比单线程程序困难得多
🍨1.4、线程的异常和用途
-
异常问题
-
- 单个线程如果出现除零错误,野指针问题导致线程崩溃,进程也会随着崩溃
-
- 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,导致进程终止,该进程内的所有线程也就随即退出
线程的用途
-
- 合理的使用多线程,能提高CPU密集型程序的执行效率
-
- 合理的使用多线程,能提高IO密集型程序的用户体验(如:生活中我们一边写代码一边下载开发工具,就是多线程运行的一种表现)
🌺2、Linux下进程 vs 线程
-
进程与线程的区别
-
- 进程是OS资源分配的基本单位
-
- 线程是CPU调度的基本单位
-
- 线程共享进程数据,但也拥有自己的一部分(私有)数据:线程ID、一组寄存器、栈、errno(错误码)、信号屏蔽字、CPU调度优先级
-
- 进程的多个线程共享同一地址空间,因此Text Segment、Data Segment都是共享的
-
- 如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到
紫色框里面和箭头指的区域,其他线程都是可见的!!!
除此之外,各线程还共享以下进程资源和环境:
-
文件描述符表(fd_array)
-
每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
-
进程当前所处的工作目录(CWD)
-
用户id(UID) 和 组id(PID)-- 在OS中启动一个进程/线程,要知道是谁启动的
进程和线程的关系图: