详解线程概念

本文详细解析了线程在Linux中的实现机制,强调了线程的轻量级特性,比较了线程与进程的区别,探讨了线程的优点(如资源效率和并行计算)和缺点(如性能损失和编程复杂性),以及线程异常处理和正确使用多线程提升程序效率的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 .线程

是进程内的一个执行分支,线程的执行粒度比进程细。

在进程内部创建一个tack_struct,把其中的一部分执行代码和数据(通过页表映射)单独分给一个线程来维护,这就是一个进程内的一个执行分支。

线程不需要维护所有的代码和数据,所以线程的执行粒度比进程细。

2.Linux的实现方案

线程在进程“内部“执行,线程在进程的地址空间内运行(为什么?)

              任何执行流要执行都要有资源,地址空间是进程的资源窗口

线程的执行粒度比进程更细?

                       线程执行进程的一部分代码和数据。

在CPU中没有进程线程的概念,只有执行流的概念。一个线程或者一个进程就是一个执行流。

3.重新定义一下进程和线程

什么是线程?我们认为,线程是操作系统调度的基本单位。

重新理解进程?内核观点:进程是承担分配系统资源的基本实体。

 那么线程在操作系统中应该如何描述组织呢?

大多数其他操作系统中有线程对应的调度,执行,描述,组织,但是Linux中是没有专门去对线程进行描述组织的,它复用了进程的数据结构和管理算法。

Linux没有真正意义上的线程体现在其没有为线程专门用tcb描述组织起来,但是它符合操作系统对线程的定义。

我们考虑一下,那CPU怎么区分线程和进程呢?

对于CPU而言,我不关心是进程还是线程,只存在执行流的概念。

线程 <= 执行流 <= 进程。

这里执行流的粒度比进程小,所以叫轻量级进程,也就是Linux下所谓的线程,如果一个进程只有一个执行流,那就是一个主线程。

4.重谈地址空间

如何理解资源分配给线程?

我们知道,进程地址空间是一个虚拟空间,每个进程都有一份,我们要找的实际上是对应的物理内存,那怎么通过虚拟地址转换到对应的物理地址呢?这就需要用到页表了

CPU将一个虚拟内存空间中的地址转换为物理地址,需要进行两步:首先将给定一个逻辑地址(其实是段内偏移量,这个一定要理解!!!),CPU要利用其段式内存管理单元,先将为个逻辑地址转换成一个线程地址,再利用其页式内存管理单元,转换为最终物理地址。

那怎么样用页表映射呢? 

直接用一张这个页表映射,那么我们可以粗略计算一下,以32位机器为例。

如果一个地址一一映射,那么一个指针4个字节,俩者一映射就需要8字节,那么完成这4GB内存的映射就需要32GB。不可能这样做的,所以应该怎么设计页表映射呢?

我们以32位虚拟地址为例,把32位虚拟地址化成 32 = 10 + 10 + 12的形式,我们把页表分级,前边十 位的范围【0 - 1023】用来表示对应的一级页表下表,然后一级页表内放的是二级页表的地址,同样,二级页表也是十位,其中存放的是对应物理内存的页框(物理内存或者磁盘通常是把4kb做为一个块,一个页框也就是一个块,这样可以提升效率,比如读取或者加载,可以一次加载或读取一个块,而不用一个字节,一个字节的去读取或加载),剩下的12位呢,我们可以发现,2^12 = 4 kb,所以这12位就作为页框起始地址的偏移量让我们可以找到准确的物理地址。

那我们来算一下空间消耗吧!

一个一级页表 1024 * 4 = 4kb,二级页表 1024 * 4 * 1024 = 4 MB,这样一看,通过页表分级的形式,我们虚拟地址映射所用空间相对来说并不大,而且通常二级页表是不全的,因为一般不存在一个进程需要所有内存(这里是4GB),那可能会说,我一开始不知道我需要访问哪块空间,要是我访问的空间没有映射怎么办?

这人家早就想到解决方案了,先通过二级页表的一个字段判断有没有映射,如果没有,那就缺页中断,不断去填充。

这里说明一下,一个进程可以没有二级页表,但一级页表(页目录)是必须存在的,且CPU中有一个寄存器(CR3)存放它的地址。寄存器(CR2)存放引起缺页中断异常的虚拟地址,这是为了在缺页中断后我能找到上次的地址接着执行。

  所以线程分配资源,本质就是分配地址空间范围。

什么是缺页中断呢?

这部分先留着。。。。。。。。。。

我们知道,一个地址是一个字节,那么对一个int类型变量&拿到几个地址呢?

任何一个变量(结构体,内置类型,自定义类型,数组)&都只拿到一个地址,那么定义的不同类型的变量是为什么呢?

CPU内有许多寄存器,我们定义不同的类型是给CPU的寄存器去看的,CPU读取指令是一条一条去读的(也就是一个地址一个地址去读),那到底读多少,这个CPU在读取之前就要知道的,所以类型就作为了一个限制,规定一次读多少。

 谈一下我们的‘ * ’ 操作。

我们对不同类型的变量解引用,就要读取不同的大小空间,但&是取的首地址,还有我们要区分大小端。

这是因为每一位地址里边的值是一定的,但是读取多少位地址,起始地址从哪里开始,这都会影响最终结果。

起始地址+类型 = 起始地址 + 偏移量。      这是X86的特点。

6.Linux线程周边概念

线程比进程更加轻量化(为什么?)

创建和释放更加轻量化(线程生死)

切换更加轻量化(线程运行)

这两者就是线程的生命周期

进程的创建需要:PCB,页表,地址空间,文件描述符表,信号的三张表……

线程只需要创建对应的tcb。

同样,进程释放申请的资源都要释放,线程只需要释放他的tcb。

切换:线程只需要切换他的tcb,页表,地址空间都不需要切换(这些都是进程的资源,被线程共享,也就是说,线程创建好了就能看到进程的这些资源)。

有人会说,这些切换也不需要多少时间呀,怎么就说线程的切换更高呢?

CPU中除了寄存器,还有有一个缓存进程数据的硬件(cache),线程的执行其实本质就是进程的执行,因为线程其实就是进程内部的一个执行分支(一个部门,其中一个人在做一个工作,其实就是你这个部门在做工作,他只是做其中的一部分)。

 我这个机器是2G2核的,机器CPU配置高,cache也大。他会提前加载进程的数据,这样是为了加快效率。

 那样就来了,线程切换只需要切换上下文,而这个cache不需要切换,进程切换就需要直接丢弃这个cache,切换不只有寄存器的切换,所以我们说线程切换要轻量化。

当然,进程也是需要去切换的,时间片一到,就需要切换,线程的数量不会影响进程的时间片,进程通常会把时间片合理分配给其内部线程。

7. 线程优缺点

7.1线程的优点

创建一个新线程的代价要比创建一个新进程小得多
不需要创建页表建立映射,不需要创建地址空间
与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
不需要切换页表,地址空间,cache的内容
线程占用的资源要比进程少很多(方方面面都有,线程可以访问进程的大部分资源不需要去单独维护
能充分利用多处理器的可并行数量 (这个进程也可以做到)
在等待慢速 I/O 操作结束的同时,程序可执行其他的计算任务(IO:网络数据接收发送,文件数据读取写入)进程也可以做,只是线程做更容易。
计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
(如果是一个但CPU,那么只要一个线程的效率最高,这样连切换的时间都不要了,计算密集型,通常几个CPU就创建几个)
I/O 密集型应用,为了提高性能,将 I/O 操作重叠。线程可以同时等待不同的 I/O 操作。(可以多创建一点,IO时间可以重叠)

7.2线程的缺点

性能损失
一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型
线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的
同步和调度开销,而可用的资源不变。
健壮性降低
一个线程出现问题,整个进程都会挂掉,比如除0错误,会发信号,整个进程都会执行对应方法,信号是独立于进程之外的执行流,它的信号是发送给进程而不是线程,所以整个进程会挂掉,一旦进程挂掉,那么线程赖以生存的资源就没有了,那么线程也就挂了。一个进程除0,不会所有进程都挂掉,进程独立性。
编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了
不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。
缺乏访问控制
就是说大部分资源是共享的
进程是访问控制的基本粒度,在一个线程中调用某些 OS 函数会对整个进程造成影响。
编程难度提高
编写与调试一个多线程程序比单线程程序困难得多

7.3线程异常

单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃
线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该
进程内的所有线程也就随即退出

7.4线程用途

合理的使用多线程,能提高 CPU 密集型程序的执行效率
合理的使用多线程,能提高 IO 密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是多线程运行的一种表现)(边看文章边点个赞也是以种多线程哦,在时间上可以重叠呢)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值