【Linux】线程的概念

一、线程的概念

1.1 什么是线程

  • 在一个程序里的一个执行路线就叫做线程,更准确的定义是:线程是“一个进程内部的控制序列”
  • 一切进程至少都有一个执行线程
  • 线程在进程内部运行,本质是在进程地址空间内运行
  • 在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化
  • 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每一个执行流,就形成了线程执行流
  • 线程是进程内部的一个执行分支,线程是CPU调度的基本单位
  • 加载到内存中的程序叫做进程,进程 = 内核数据结构 + 进程代码和数据

       我们在正文代码区中,全部都是串行调用的,在地址空间和地址空间上的虚拟地址本质上是一种资源,当我们在创建进程的时候,需要创建虚拟地址空间,页表,进程PCB等,需要时间和空间,比较麻烦。

               在Windows中,操作系统会创建线程控制块,因为线程的数量很多,所以需要先描述,在组织。有一个缺点是非常复杂。所以Linux的设计者认为进程和线程都是执行流,具有极度的相似性,没有必要单独设计出数据结构和算法,直接服用代码即可,使用进程来模拟线程。

        

1.2 线程的优点和缺点

1.2.1 线程的优点

  • 创建一个新线程的代价要比创建一个新进程小得多
  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少的很多
  • 线程占用的资源要比进程少很多
  • 能充分利用多处理器的可并行数量,但是线程的数量最好不要超过CPU的核数
  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
  • I/O密集型应用,为了提高性能,将I/O操作重叠,线程可以同时等待不同的I/O操作

1.2.2 线程的缺点

  • 性能损失:一个很少被外部事件阻塞的计算密集型线程往往无法与其他线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,但是可用的资源是不变的
  • 健壮性降低:编写多线程需要更全面更深入的考虑,在一个多线程程序中,因时间分配上的细微偏差或者因为共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的
  • 缺乏访问控制:进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响
  • 编程难度的提高:编写和调试一个多线程程序比单线程程序困难的多

1.3 线程的异常

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

1.4 线程的用途

  • 合理的使用多线程,能提高CPU密集型程序的执行效率
  • 合理的使用多线程,能提高IO密集型程序的用户体验

二、Linux进程 VS 线程

       在以前的进程的理解中,一个进程内部只有一个线程的进程,但是在线程中,一个进程中至少有一个线程,线程是执行流。

       那么进程是什么呢?进程的内核角度上,进程是承担分配系统资源的基本实体。我们不要站在调度的角度区理解进程,而是应该站在资源的角度理解进程。

关于调度,我们来谈一谈进程调度和线程调度都是什么?

       在进程中,我们来进行CPU调度是需要重新加载进程的上下文数据和一个数据结构,但是在线程中,CPU调度只需要重新加载进程的PCB即可,那么进程调度也就比线程调度需要多加载几个数据结构,速度也没有慢到哪里。

那么究竟是什么让线程调度优于进程调度呢??

       CPU中有一个硬件叫做cache,也叫做缓存,有一个东西叫做时间相关性和空间相关性,当CPU去执行代码时,会将代码拷贝到缓存中,并且会将该行代码下面的部分代码也拷贝到缓存中,所以,当我们进行进程调度的时候,我们会发现原先拷贝到缓存中的代码不能使用,因此需要我们重新去拷贝代码;但是在进行线程调度的时候,我们不需要进行将缓存中的代码进行替换,因为线程是共享代码区的。

进程和线程的概念以及线程的共享区和私有区

  • 进程是资源分配的基本单位
  • 线程是调度的基本单位

线程共享进程数据,但是也拥有自己的一部分数据:

  • 线程ID
  • 一组寄存器
  • errno
  • 信号屏蔽字
  • 调度优先级
  • 进程的多个线程共享

        同一个地址空间,因此Text Segment,Data Text Segment 都是共享的,如果定义一个函数,在各个线程中都可以调用,如果定义个全局变量,在各个线程中都可以访问到,各个线程还共享以下进程资源和环境:

  • 文件描述符
  • 每一种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
  • 当前工作目录
  • 用户ID和组ID

进程和线程的关系如下图:

三、再谈地址空间

       我们一般来说,多个线程是共享的,多个线程就是多个执行流,这个多个执行流可以将共享的代码进行划分,那么如何进行划分多个执行流呢??

       在之前,我们谈及了页表,那么我们现在还计算一下页表占用的空间大小,在2^32位的地址空间中,由于每一个地址空间需要进行虚拟地址到物理地址空间的转换,所以一共需要2^32个地址转换,在页表中,一行上具有虚拟地址空间(4字节),物理地址空间(4字节),一些属性,就按一行10个字节来计算,但属性肯定不会这么少,那么一共需要:2^32 * 10 字节的空间,换算一下是40GB的地址空间,内存才4GB,一个页表居然需要40GB的地址空间,肯定不现实啊!所以实际上的页表不是这么存储的。

       在谈论页表之前,我们需要先来看一看内存在操作系统中的结构是什么?

       内存也有结构吗?内存在操作系统中不是直接的一整个大的空间,而是由一份一份小的空间来进行操作的,在文件系统中,在磁盘存储中,文件是有属性的,以4KB为基本数据单元,而在内存中的基本数据单位也是4KB,那么一个4GB的内存有1048576个4KB个数据单元,所以操作系统需要先描述,在组织,有属于他的结构体:struct page,有一个属性是flag,我们可以将这个内存是不是内核的,还是用户的,是不是已经被使用了等等的属性用宏去定义,在flag中声明。

       那么页表在操作系统应该是怎样的结构呢?我们可以将虚拟地址进行划分为三个部分,一共是32比特,一部分是10比特,一部分是10比特,一部分是12比特,划分结果为下图所示:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加油,旭杏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值