【Linux】:线程概念

朋友们、伙计们,我们又见面了,本期来给大家带来线程概念相关代码和知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个 人 主 页 :stackY、

C + + 专 栏 :C++

Linux 专 栏 :Linux

目录

​编辑​

[1. 线程的概念](#1. 线程的概念)

[2. 线程的理解](#2. 线程的理解)

[3. 线程的优缺点](#3. 线程的优缺点)

[4. 重谈地址空间](#4. 重谈地址空间)

[4.1 虚拟地址的转化](#4.1 虚拟地址的转化)

[5.5 进程和线程](#5.5 进程和线程)


1. 线程的概念

关于线程有两种概念:

  • ① 线程是比进程更加轻量化的一种执行流 / 线程是进程内部执行的一种执行流;
  • ② 线程是CPU调度的基本单位 / 进程是承担系统资源的基本实体。
    那么为什么要有线程呢?

我们创建进程就是要让它执行我们的代码,但是进程的创建到运行这个阶段需要做很多事情(从磁盘加载可执行、由虚拟到物理的转化等等),如果我们只是想单纯的运行代码,那么有很多个可执行需要运行的话,通过创建进程的方式来运行就有点太麻烦了,所以需要有一种比进程更加轻量化的去执行我们的代码。
因为地址空间是进程的"资源窗口",所以进程可以这个窗口来进行资源的获取,所以为了减少成本,只创建PCB,然后指向同一个虚拟地址空间,实现共享,简单的理解就是将虚拟地址空间中的代码区、数据区、堆栈区进行划分为多个小块,此时多个PCB指向虚拟地址空间只参与资源的分配任务。

那么既然有线程,OS也需要对这些线程进行管理,那么怎么管理呢?先描述、再组织,如果重新设置一套对于线程的管理方法那么实在太麻烦了,线程和进程很相似,所以直接采用管理进程的那一套方案即可,直接复用。

2. 线程的理解

CPU是调度执行流来运行的,那么在了解完线程之后,CPU获取到一个执行流大小是小于等于进程。这该怎么理解呢?我们如何看待现在的进程呢?内部包含多个执行流的进程!

进程 = 内核数据结构 + 代码和数据,现在所说的进程就是包含进程PCB、虚拟地址空间、页表、代码和数据。

其中不只包含一个执行流,而是有多个执行流(在一个地址空间有多个PCB)。

我们之前看待进程其中里面只有一个执行流,所以CPU在调度的时候,如果调度的进程只有一个执行流,那么就是进程,如果有多个执行流,那么就调度其中一个执行流,就叫做线程,所以CPU在调度执行流的大小总是小于等于进程!

所以线程是CPU调度的基本单位,而进程是承担系统资源的基本实体。
所以创建线程只需要创建PCB,所以线程也叫做轻量级进程。

所以可以得出:

  • 1. 线程的创建相比进程更加简单;
  • 2. 线程在进程的地址空间中运行。
    接下来我们通过代码的方式先直观的看一下线程,其中关于线程控制的代码会专门解释,先观察代码运行结果:
cpp 复制代码
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>

// 新线程
void *ThreadRoutine(void *arg)
{
    const char *threadname = (const char *)arg;
    while (true)
    {
        std::cout << "I am a new thread: " << threadname << ", pid: " << getpid() << std::endl;
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    // 创建线程
    pthread_create(&tid, nullptr, ThreadRoutine, (void *)"thread 1");

    // 主线程
    while (true)
    {
        std::cout << "I am main thread" << ", pid: " << getpid() << std::endl;
        sleep(1);
    }
    return 0;
}

这段代码首先会创建一个新的线程,然后一个执行流去执行新线程的代码,紧接着往下走就是主线程的代码,代码结果就是会有两个循环在一直执行:

使用指令ps -aL可以查看线程:

可以看出线程的调度区分并不是用PID来区分,而是LWP,并且主线程的LWP和PID一样。

3. 线程的优缺点

优点:

  • 创建一个新线程的代价要比创建一个新进程小得多

  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多

  • 线程占用的资源要比进程少很多

  • 能充分利用多处理器的可并行数量

  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务

  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现

  • I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。
    缺点:

  • 健壮性降低,多线程在运行时,各个线程之间不相互独立,一个线程的异常很有可能导致其它线程出现异常,换句话说线程之间是缺乏保护的

  • 缺乏访问控制,在一个线程中调用某些OS函数有可能会对整个进程造成影响

  • 调试难度比较高,多线程程序比单线程程序调试更为复杂

4. 重谈地址空间

我们的可执行程序被编译好之后以ELF的格式存储在磁盘上,当要执行时会先加载的内存,在之前文件系统部分了解到文件系统IO的基本单位大小是4KB,即使我们所要修改的数据只有1比特位,也需要一次性加载4KB,所以为了统一和方便,将物理内存以4KB划分为多个小块,每一个块被叫做一个页框,磁盘文件中的文件以4KB分为多个小块,每一块被叫做一个页帧,这样子每一次加载磁盘文件时,就根据4KB很容易的将数据加载进来。

以32位4GB的机器来计算,页框的数量有10万多个页框,所以OS也需要将这些页框给管理起来,所以就需要先描述再组织,为页框构建描述结构体的对象(包含页框的属性、使用情况、标志位等等),然后用数组将这些结构体管理,对页框的管理就变成了对数组的增删查改。

4.1 虚拟地址的转化

通过上面对物理内存以4KB进行划分,所以虚拟地址到物理地址的转化不只只通过页表来转化,虚拟到物理的转化其实是在CPU内部转化的,在我们的CPU中的eip寄存器保存的是虚拟地址,当CPU拿到一个虚拟地址时会通过MMU从虚拟到物理的转化:

例如:虚拟地址32个比特位

1111 1111 1110 0000 0000 0000 0000 0001

  • 前10个比特位可以找到对应的页目录;
  • 次10个比特位可以找到页表,通过页表可以找到页框;
  • 页框起始地址 + 低12个比特位可以找到页内偏移;

通过整个的虚拟地址就可以访问到物理地址上的数据内容。

可以看到其实划分页表的本质就是划分地址空间。

5.5 进程和线程

进程是资源分配的基本单位
线程是调度的基本单位
线程共享进程数据,但也拥有自己的一部分数据:

  • 线程ID
  • 一组寄存器
  • errno
  • 信号屏蔽字
  • 调度优先级

简单的每个线程都有自己独立的硬件上下文数据与栈结构。

各线程还共享以下进程资源和环境:

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

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!

相关推荐
bluekrystal23 分钟前
DPDK程序编译
linux
威迪斯特1 小时前
linux系统安全:开源的反病毒工具ClamAV的安装配置使用和维护介绍
linux·运维·centos·系统安全·扫描·病毒·木马
小黑哪有坏心思!1 小时前
Linux安装部署数据库:MongoDB
linux·运维·数据库·mongodb·nosql
嶔某1 小时前
Linux:编辑器Vim和Makefile
linux·编辑器·vim
Karoku0661 小时前
【MySQL数据库】MHA高可用
linux·运维·服务器·数据库·mysql
weixin_438197382 小时前
ELK同时采集Nginx、linux内核日志信息
linux·elk
断水客2 小时前
apt-get install cmake, Unable to fetch some archives
linux·运维·服务器
S hh2 小时前
【Linux网络】UdpSocket
linux·运维·服务器·网络·学习
binqian3 小时前
【Linux】mnt命名空间-操作
linux·运维·服务器
❀͜͡傀儡师3 小时前
CentOS 7 下升级 OpenSSL
linux·运维·centos