【Linux】线程概念

       🔥🔥 欢迎来到小林的博客!!
      🛰️博客主页:✈️林 子
      🛰️博客专栏:✈️ Linux
      🛰️社区 :✈️ 进步学堂
      🛰️欢迎关注:👍点赞🙌收藏✍️留言

目录

  • 线程与进程
    • 重新认识进程
  • 创建线程
  • 线程中独立的资源
  • 为什么线程切换的成本更低?

线程与进程

我们都知道,进程是操作系分配资源的基本实体。 每当操作系统创建一个新的进程时,都会为这个进程分配资源,例如 : 进程地址空间,页表…等。

在这里插入图片描述

那如何理解线程呢?

在Linux系统下,并没有真正意义上的线程。因为在Linux系统下,线程没有属于自己的数据结构。而windows操作系统是为线程设定了指定的数据结构。而在Linux系统下,线程复用了进程的PCB。也就是说,描述线程和进程的结构体都是task_struct。 而这些PCB都共享同一块进程地址空间,共享同一块页表…以及其他的资源。

在这里插入图片描述

而这个时候,进程就不仅仅是一个PCB了,而是多个PCB + 当前进程的资源 = 进程。而每一个PCB都是一个执行流,无论是线程还是进程,CPU都不关心。因为CPU只负责调度PCB。而通过一定的技术手段,可以将进程的"资源"以一定的方式分配给不同的task_struct。

所以可以得出结论:

进程是承担操作系统分配资源的基本实体。

线程是在进程的内部执行。因为它们共享同一块进程地址空间以及其他资源。

线程是CPU调度的基本单元

重新认识进程

在之前的认知中,我们都认为一个进程就是一个PCB + 程序的代码和数据。 但是现在我们要重新认识进程了。当进程内部只有一个执行流的时候, 进程 = PCB + 程序的代码和数据。 当进程内部有多个执行流的时候 ,那么 进程 = 多个PCB + 程序的代码和数据。

在CPU的视角中,CPU其实根本不关心当前调用的是进程还是线程,因为它只认PCB,也就是task_struct。所以在linux系统下, PCB <= 其他OS内的PCB。因为当Linux下的进程包含多个执行流的时候,那么多个PCB其实共享了大部分资源,那么此时的PCB就会小于其他OS内的PCB。因为其他的OS,进程和线程都有属于各自的数据结构。

在Linux下,Linux是用进程来模拟线程的!

这也就意味着Linux并不能直接给我们提供线程相关的接口,只能提供轻量级进程接口!不过好在有一位Linux系统工程师在用户层实现了一套多线程方案,以库的方式提供给了用户进行使用,那就是 pthread线程库,也叫原生线程库。

创建线程

在初步了解线程之后,那么我们可以来创建一个线程,见见线程是什么样子的。

我们先认识一下创建线程的函数。

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
第一个参数为线程的tid
第二个参数为线程的属性
第三个参数是一个函数指针,为线程的执行函数
第四个参数为执行函数的参数

测试代码:

#include<iostream> 
#include<pthread.h> 
#include<unistd.h> void* ThreadRun(void* name)
{while(1){std::cout << "this is " << (char*)name << ", pid = " << getpid() << std::endl;sleep(1);}
}
int main()
{pthread_t tids[5]; char name[64];for(int i = 0 ; i < 5 ; i++){snprintf(name,sizeof name,"%s:%d","Thread ",i);pthread_create(tids+i,nullptr,ThreadRun,(void*)name);sleep(1);}while(1){std::cout << "this is main thread , pid = " << getpid() << std::endl;sleep(3);}
}

记得在编译的时候加上一个-lpthread选项,否则无法编译通过,因为 -lphtread原生线程库并不属于C/C++库。

在这里插入图片描述

然后运行后我们发现。5个线程+一个主线程,它们打印出来的进程pid都是一样的。

在这里插入图片描述

然后我们再用ps ajx | head -1 && ps ajx | grep “你的可执行程序名称” 来查看当前运行的进程

在这里插入图片描述

我们发现只有一个进程,这是因为线程是进程内部执行的!所以我们无法看到线程,如果想看线程,我们可以用ps -aL | head -1 && ps -aL | grep "要查看的进程名称" 即可查看当前进程下的线程。

在这里插入图片描述

我们可以看到这个进程中有6个线程,一个主线程。剩下的5个创建的线程。 我们可以发现它们的PID都是一样的。但是LWP是不一样的! 所以,CPU调度看的是LWP还是PID 呢? 答案肯定是LWP,因为线程是CPU调度的基本单元。如果是根据PID进行调度,那么这么多线程的PID都一样,就会产生歧义。所以CPU调度实际是根据LWP字段调度的。

验证线程之间共享地址空间

很简单,我们只需要创建一个全集变量,并在主线程对该变量进行修改,然后让所有线程打印该变量。其他线程的值也发生了改变,那就说明线程之间共享了地址空间。

#include<iostream> 
#include<pthread.h> 
#include<unistd.h> int x = 0;void* ThreadRun(void* name)
{while(1){std::cout << "this is " << (char*)name << ", pid = " << getpid() << "  x = " << x  << std::endl;sleep(1);}
}int main()
{pthread_t tid; pthread_create(&tid,nullptr,ThreadRun,(void*)"new thread");while(1){x++;std::cout << "this is main thread , pid = " << getpid() <<"  x = " << x << std::endl;sleep(1);}
}

运行结果:

在这里插入图片描述

我们发现,全局变量x被所有线程所共享。

线程中独立的资源

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

  • 线程id
  • 一组寄存器(相当于上下文)
  • 栈(每个线程有独立的栈结构,让线程与线程之间独立)
  • errno
  • 信号屏蔽字
  • 调度优先级

为什么线程切换的成本更低?

1.因为进程地址空间和页表不需要切换

但是地址空间和页表切换并没有太大的消耗。线程切换成本更低的本质原因是因为CPU内部有L1~L3 cache。

我们都知道,CPU处理指令是一条一条处理的。但如果每次CPU都去内存读一条指令,那么速度是非常非常慢的。所以CPU内部有个缓冲区。会先把内存中的指令放进CPU内部缓冲区。也就是预读代码,这样CPU就不用频繁的去内存中读取指令。而是直接在内部缓冲区里读,这样子速度是非常快的。而线程切换,cache不会失效。但如果是进程切换,那么cache就会立马失效,只能重新缓冲。所以这才是线程切换更快的本质原因,因为线程切换,CPU内部的缓冲区不用重新缓存。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/147215.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

[VIM]VIM初步学习-3

3-1 编写 vim 配置&#xff0c;我的 vim 我做主_哔哩哔哩_bilibili

C# DeOldify 黑白照片 老照片上色

效果 项目 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System…

NPDP产品经理知识(市场调研-文化,团队,领导力)

--- VOC --- 市场调研的关键步骤 1.> 定义问题 2.>定义结果的准确度 3.>收集数据 4.>分析和解读数据 5.>得出结论 6.>实施 --- 二级市场研究/一级市场研究 --- 定性 > 焦点小组 > 深度访谈 > 人种学(On-Site In-Home) > 客户…

Rust 使用Cargo

Rust 使用技巧 Rust 使用crates 假设你正在编写一个 Rust 程序&#xff0c;要使用一个名为 rand 的第三方库来生成随机数。首先&#xff0c;你需要在 Cargo.toml 文件中添加以下依赖项&#xff1a; toml [dependencies] rand "0.7.3" 然后运行 cargo build&…

C++ 学习系列 -- std::stack 与 std::queue

一 std::stack 与 std::queue 分别是什么&#xff1f; 两者均是 c 中的序列化容器&#xff0c;区别在于&#xff1a; std::stack 元素是先进后出 std::queue 元素是先进先出 二 std::stack 与 std::queue 原理 1 std:statck 2. std::queue 两者底层容器可以是 list 也可以…

数据结构与算法基础(青岛大学-王卓)(8)

哎呀呀&#xff0c;sorry艾瑞波地&#xff0c;这次真的断更一个月了&#xff0c;又发生了很多很多事情&#xff0c;秋风开始瑟瑟了&#xff0c;老父亲身体查出肿瘤了&#xff0c;有病请及时就医&#xff0c;愿每一个人都有一个健康的身体&#xff0c;God bless U and FAMILY. 直…

GraphQL全面深度讲解

目录 一、GraphQL 是什么 二、GraphQL 规范 数据模型 字段 参数 三、运行示例 四、优势和劣势 优势 劣势 一、GraphQL 是什么 GraphQL 是一种用于 API 的查询语言&#xff0c;也是一个基于服务端的运行引擎。 GraphQL 提供了一套完整的规范和描述用于查询 API&#xf…

Sketch mac98.3(ui设计矢量绘图)

Sketch Mac是一款矢量绘图软件&#xff0c;适用于UI设计、网页设计、图标制作等领域。以下是Sketch Mac的一些主要特点&#xff1a; 简单易用的界面设计&#xff1a;Sketch Mac的用户界面简洁明了&#xff0c;使得用户可以轻松上手操作&#xff0c;不需要复杂的学习过程。强大…

【QT】自定义组件ui类添加到主ui界面方法

1.添加自定义组件到项目中 add new选择如下 写好类方法&#xff0c;确定即可 2.将新创建的ui类加入到主ui界面 选中新创建ui类的父类空块&#xff0c;右键选择提升为 选择并添加新创建的类

【目标检测】——Gold-YOLO为啥能超过YOLOV8

华为 https://arxiv.org/pdf/2309.11331.pdf 文章的出发点&#xff1a;FPN中的信息传输问题 1. 简介 基于全局信息融合的概念&#xff0c;提出了一种新的收集和分发机制&#xff08;GD&#xff09;&#xff0c;用于在YOLO中进行有效的信息交换。通过全局融合多层特征并将全局信…

【DLoopDetector(C++)】DBow2词袋模型loop close学习

0.前言 最近读了两篇论文&#xff0c;论文作者开源了一种基于词袋模型DBoW2库的DLoopDetector算法&#xff0c;自己运行demo测试一下 对应论文介绍&#xff1a;Bags of Binary Words for Fast Place Recognition in Image Sequences 开源项目Github地址&#xff1a;https://gi…

ThreeJS - 封装一个GLB模型展示组件(TypeScript)

一、引言 最近基于Three.JS&#xff0c;使用class封装了一个GLB模型展示&#xff0c;支持TypeScript、支持不同框架使用&#xff0c;具有多种功能。 &#xff08;下图展示一些基础的功能&#xff0c;可以自行扩展&#xff0c;比如光源等&#xff09; 二、主要代码 本模块依赖…

vue3学习实战

vue3新增变化 diff算法变化 vue3的diff算法没有vue2的头尾、尾头之间的diff&#xff0c;对diff算法进行了优化&#xff0c;最长递归子序列。 ref VS reactive ref 支持所有的类型&#xff0c;reactive 支持引用类型&#xff0c;array object Map Setref取值、赋值&#xff…

2023-油猴(Tampermonkey)脚本推荐

2023-油猴&#xff08;Tampermonkey&#xff09;脚本推荐 知乎增强 链接 https://github.com/XIU2/UserScript https://greasyfork.org/zh-CN/scripts/419081 介绍 移除登录弹窗、屏蔽首页视频、默认收起回答、快捷收起回答/评论&#xff08;左键两侧&#xff09;、快捷回…

[CSCCTF 2019 Qual]FlaskLight 过滤 url_for globals 绕过globals过滤

目录 subprocess.Popen FILE warnings.catch_warnings site._Printer 这题很明显就是 SSTI了 源代码 我们试试看 {{7*7}} 然后我们就开始吧 原本我的想法是直接{{url_for.__globals__}} 但是回显是直接500 猜测过滤 我们正常来吧 {{"".__class__}} 查看当前…

Mysql 分布式序列算法

接上文 Mysql分库分表 1.分布式序列简介 在分布式系统下&#xff0c;怎么保证ID的生成满足以上需求&#xff1f; ShardingJDBC支持以上两种算法自动生成ID。这里&#xff0c;使用ShardingJDBC让主键ID以雪花算法进行生成&#xff0c;首先配置数据库&#xff0c;因为默认的注…

Linux shell编程学习笔记4:修改命令行提示符格式(内容和颜色)

一、命令行提示符格式内容因shell类型而异 Linux终端命令行提示符内容格式则因shell的类型而异&#xff0c;例如CoreLinux默认的shell是sh&#xff0c;其命令行提示符为黑底白字&#xff0c;内容为&#xff1a; tcbox:/$ 其中&#xff0c;tc为当前用户名&#xff0c;box为主机…

Spring Boot 技术架构图(InsCode AI 创作助手辅助)

Spring Boot 技术架构是一种用于构建现代应用程序的框架&#xff0c;它可以与各种前端、代理、网关、业务服务、中间件、存储、持续集成和容器服务集成在一起&#xff0c;以创建功能强大的应用程序。 源文件下载链接&#xff01;&#xff01;&#xff01;&#xff01;&#xff…

BASH shell脚本篇3——字符串处理

这篇文章介绍下BASH shell中的字符串处理的相关命令。之前有介绍过shell的其它命令&#xff0c;请参考&#xff1a; BASH shell脚本篇1——基本命令 BASH shell脚本篇2——条件命令 Bash字符串也是一种数据类型&#xff0c;它用于表示文本而不是数字&#xff0c;它是一组可能…

nginx-proxy反向代理流程

1.浏览器发送请求数据到nginx。 2.nginx先处理请求头&#xff0c;后处理请求体。 client_header_buffer_size #ginx可设置客户端上传header缓冲区大小 client_body_buffer_size #nginx可设置客户端上传数据缓冲区大小 client_body_t…