Linux线程

文章目录

  • 线程
  • 线程原理
  • 页表
  • 线程VS进程
  • 线程相关函数
    • pthread_create函数
    • pthread_self
    • pthread_exit
    • pthread_cancel
    • pthread_join
    • pthread_detach
  • 线程ID

线程

什么是线程?为什么要有线程?
线程本质上就是轻量化的进程,一个进程就是一个执行流,在同一时间内只能去做一件事,而我们要是能把一个进程分为多个小的进程,那么就相当于把它分为多个执行流,这样在同一时间内我们就可以让一个进程去在同一时间内左不同的事情,而这个被分成的小进程就称为线程。

线程原理

我们要重谈一下进程地址空间,在之前我们通过fork函数来创建子进程,这个是不属于多线程的,因为子进程仍具有自己独立的虚拟地址空间,父子进程通过各自的页表映射到同一份物理内存,当一方发生数据改变的时候,就会进行写时拷贝,并更改页表的映射关系
在这里插入图片描述
当父子进程加载到内存里面时,因为他们之间数据很多是相同的,所以数据就会被加载两份,占据很大一部分空间,那么如何提高这个效率呢?不让相同的数据加载两份呢?

多线程,多线程相比于父子进程或者其他进程之间,是通过同一份虚拟地址空间来存储这个进程中所有线程的地址的。代码段,未初始化变量,已初始化变量等,在不同线程看来都是看到的同一份,所以只需要一份就可以了,对于线程之间不同的数据,操作系统会对他们进行划分,不同线程去拥有各自的虚拟内存,并让所有的线程通过同一份页表去映射到物理内存,这样就极大的提高了内存的利用率。
在这里插入图片描述

页表

为什么说通过多线程去做一件事,比多进程占用的内存更少呢?因为多线程是一个进程的细分,因此他只需要加载一个页表到内存,而多进程需要加载多个页表。我们一直提页表,那么页表的结构是怎么样的?虚拟地址是怎么转换成物理地址的?
我们以虚拟地址是32位为例,假设我们直接就是在一张表中一个虚拟地址对应一个物理地址,再加上权限等信息,那么一个条目大概就需要10字节,而虚拟内存有4GB,那么光页表需要的内存可能就需要40GB,这是不现实的
在这里插入图片描述

因此我们把页表划分成多级页表,一个虚拟地址是有32位的,我们将这32位划分成10+10+12位分别用来存放不同的信息
在这里插入图片描述
1-10位:用于找到该虚拟地址对应的二级页表
11-20位:用于找到该虚拟地址在二级页表中对应的页表项位置
21-32位用于存放该虚拟地址在物理内存中的偏移量
我们来对上面的映射过程进行总结:
一级页表中每个条目存放的是不同二级页表的起始地址,找到对应的二级页表后,在通过11-20位找到它在二级页表中对应的位置,二级页表中每个条目存放的是它在物理内存中对应的起始地址即找到对应的页框,而物理内存中每个页框大小为4KB,我们再通过21-32位,计算出虚拟地址在该页框中的偏移量。
每个页表对应的条目有2^10=1024个
页框大小为4KB,因此我们在通过21-32位计算出偏移量的时候2^12 = 4 * 2 ^10 = 4KB,因此不会超出该页框大小

线程VS进程

创建线程为什么要比进程更加轻量化?
因为创建线程只需要创建对应的PCB即可,虚拟内存和页表都与主线程共用一份即可。
为什么线程切换比进程切换效率更高?
如果进程切换就要重新加载数据(页表和虚拟内存等),且CPU中通过cache来存储缓存的数据,如果是进程间的切换就要重新缓存,并将数据由冷变热,而线程之间的切换,数据基本是不变的,因此不用再重新热数据
进程是资源分配的基本单位
线程是调度的基本单位
在Linux中线程和进程之间没有明确的划分
线程共享进程数据,但也拥有自己的一部分数据:
1.线程ID
2.一组寄存器(上下文数据)
3.栈
4errno
5.信号屏蔽字
6.调度优先级

进程的多个线程共享同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
1.文件描述符表
2.每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
3.当前工作目录
4.用户id和组id

线程相关函数

pthread_create函数

int pthread_create(pthread_t* thread, const pthread_attr_t attr, void(start_rountinue)(void*), void *arg);
pthread_create函数用来创建线程
说明:线程被创建后将立即执行

在这里插入图片描述
参数:
thread:它的类型是pthread_t,底层是无符号长整型,这是一个输出型参数,用于得到该线程ID,我们后面在对线程的操作就是通过该ID来操作的,我们后面再细讲这个无符号长整型,先说明它是一个指向共享区的一块线程起始地址
在这里插入图片描述

attr:用于设置线程的属性,我们一般设为NULL,表示使用默认属性
start_rountine:这是一个函数指针,指向一个参数为void*,返回值为void的函数
arg:它的类型是void
的,该变量用做start_rountine的参数
返回值:
在这里插入图片描述
创建成功返回0,失败返回错误码,注意这里不是返回-1,并设置错误码errno
为什么要这么做?
错误检查:
传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。
pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通过返回值返回
pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值判定,因为读取返回值要比读取线程内的errno变量的开销更小

在这里插入图片描述
创建线程等函数都是被包含在动态库pthread.h中的,因此在对该程序进行链接时,要加上-lpthread选项,去指明要链接的库

#include <iostream>
#include <pthread.h>
#include <unistd.h>using namespace std;void* routine(void* arg)
{cout << "I am a thread" << endl;sleep(5);
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, routine, nullptr);sleep(5);return 0;
}

在这里插入图片描述
while :; do ps -eLf | head -1 && ps -eLf | grep mytest | grep -v grep; sleep 1;done
我们通过打印发现,同一个PID,对应有两个不同的轻量级进程,说明我们创建线程成功了,LWP和PID相同的被称为主线程,LWP:light weight process,轻量级进程

pthread_self

获取线程ID,我们可以通过创建线程的第一个参数来获取,也可以通过pthread_self函数来获取。
在这里插入图片描述
在这里插入图片描述
参数为void
返回值:该函数总是成功,返回的是当前线程的ID

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>using namespace std;void* routine(void* arg)
{cout << "other thread" << pthread_self() << endl;//通过函数获取该线程tid
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, routine, nullptr);cout << "other thread ,tid:" << tid << endl;//通过参数获取刚创建的线程tidcout << "main thread ,tid:" << pthread_self() << endl;//通过函数获取主线程tidsleep(1);return 0;
}

在这里插入图片描述

pthread_exit

线程退出函数,用于退出当前线程
在这里插入图片描述
参数,是一个输出型参数
在这里插入图片描述
返回值为空。

pthread_cancel

用于退出某一个线程
在这里插入图片描述
参数pthread_cancel:要去退出线程的线程ID(tid)

在这里插入图片描述
返回值:成功返回0,失败返回一个不为0的错误码

pthread_join

线程等待函数,用于阻塞式等待回收线程
在这里插入图片描述
参数:
thread:类型是pthread_t,用于指明要去等待线程的ID,也就是tid
retval:是一个void**类型的二级指针,最终指向的是线程的返回值,如果不关心设为nullptr即可

在这里插入图片描述
返回值:成功返回0,失败将返回错误码
对于不同情况的退出,参数retval的返回值也不同:

  1. 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。
  2. 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_CANCELED。
  3. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传pthread_exit的参数。
  4. 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>using namespace std;void* routine(void* arg)
{cout << "other thread" << pthread_self() << endl;//通过函数获取该线程tid
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, routine, nullptr);cout << "other thread ,tid:" << tid << endl;//通过参数获取刚创建的线程tidcout << "main thread ,tid:" << pthread_self() << endl;//通过函数获取主线程tidpthread_join(tid, nullptr);return 0;
}

在这里插入图片描述
在不添加pthread_join(tid, nullptr);的情况下,默认是不等待线程退出的,线程都没来得及去执行routine函数

pthread_detach

分离线程,因为主线程要去回收其余线程,且是以阻塞的方式进行等待,这就会降低CPU的利用率,因此如果我们并不关系线程的返回值是什么,我们可以通过pthread_detch函数来分离线程,就是让线程自动的去释放它对应的资源,而不用再让主线程进行回收
在这里插入图片描述
参数:
thread:要去分离线程的ID
返回值:
在这里插入图片描述
成功返回0,失败返回对应的错误码

线程ID

LWP是底层线程对应的PCB结构体的唯一标识符
tid是上层每一个线程在共享区中的起始地址

LWP:什么是轻量级进程?就是线程。那么相对于我们之前学的进程ID和这个轻量级进程ID有什么关系呢?又和我们前面创建进程时的函数调用pthread_self()的返回值线程ID有什么区别呢?

在这里插入图片描述
每一个线程,它在内核中都对应有一个进程描述符,这个内核中的进程描述符就是LWP,我们在应用层又要求同一个进程中的线程pid的返回值相同,这又是怎么做到的呢?
Linux下的轻量级进程是一个PCB,每个轻量级进程都有一个自己的轻量级进程ID(PCB中的pid,也就是LWP),而同一个程序中的轻量级进程组成线程组,拥有一个共同的线程组ID
同时每一个线程又有一个tid,这个tid指向用户层的tcb,在加载到共享区的线程库中,每一个线程都有它对应的tcb结构
每个进程中都有一个LWP与线程组ID相等的线程,这个线程被称为主线程,因此当一个进程中只有一个线程时,通过LWP和进程pid来找到调度该线程是等价的的。
pthread_create是一个库函数,功能是在用户态创建一个用户线程,而这个线程的运行调度是基于一个轻量级进程实现的。
对于线程的创建,我们是通过原生线程库给我们提供的应用层接口来实现的,我们要先把原生线程库加载到共享内存当中,然后在这个共享内存中创建线程,而每一个线程都在共享内存中对应一个起始地址,这里的pthread_create的第一个参数就是这里的tid,也就是一个共享内存地址。
在这里插入图片描述
通过__thread设置线程局部存储,该变量属于每个线程的私有变量
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

信息论安全与概率论

目录 一. Markov不等式 二. 选择引理 三. Chebyshev不等式 四. Chernov上限 4.1 变量大于 4.2 变量小于 信息论安全中会用到很多概率论相关的上界&#xff0c;本文章将梳理几个论文中常用的定理&#xff0c;重点关注如何理解这些定理以及怎么用。 一. Markov不等式 假定…

Protobuf 编码规则及c++使用详解

Protobuf 编码规则及c使用详解 Protobuf 介绍 Protocol Buffers (a.k.a., protobuf) are Google’s language-neutral, platform-neutral, extensible mechanism for serializing structured data Protocol Buffers&#xff08;简称为protobuf&#xff09;是谷歌的语言无关、…

多层负载均衡实现

1、单节点负载均衡 1&#xff09;站点层与浏览器层之间加入了一个反向代理层&#xff0c;利用高性能的nginx来做反向代理 2&#xff09;nginx将http请求分发给后端多个web-server 优点&#xff1a; 1&#xff09;DNS-server不需要动 2&#xff09;负载均衡&#xff1a;通过ngi…

Python深度学习028:神经网络模型太多,傻傻分不清?

文章目录 深度学习网络模型常见CNN网络深度学习网络模型 在深度学习领域,有许多常见的网络模型,每种模型都有其特定的应用和优势。以下是一些广泛使用的深度学习模型: 卷积神经网络(CNN): 应用:主要用于图像处理,如图像分类、物体检测。 特点:利用卷积层来提取图像特…

《数据分析-JiMuReport》积木报表详细入门教程

积木报表详细入门教程 一、JimuReport部署入门介绍 积木报表可以通过源码部署、SpringBoot集成、Docker部署以及各种成熟框架部署&#xff0c;具体可查看积木官方文档 当前采用源码部署&#xff0c;首先下载Jimureport-example-1.5.6 1 jimureport-example目录查看 使用ID…

喜报|迪捷软件“ModelCoder 建模及形式化验证代码生成软件”荣登浙江省首版次产品目录

近日&#xff0c;浙江省经济和信息化厅公布《2023年浙江省首版次软件产品应用推广指导目录》&#xff0c;浙江迪捷软件科技有限公司的“ModelCoder 建模及形式化验证代码生成软件”经过多轮审核及专家评定被纳入目录&#xff0c;这是迪捷软件自主研发的产品继“天目全数字实时仿…

【前缀和】【单调栈】LeetCode2281:巫师的总力量和

作者推荐 map|动态规划|单调栈|LeetCode975:奇偶跳 涉及知识点 单调栈 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 题目 作为国王的统治者&#xff0c;你有一支巫师军队听你指挥。 给你一个下标从 0 开始的整数数组 strength &…

【Matlab in VSCode】在VSCode中编辑MATLAB文件

【Matlab in VSCode】在VSCode中编辑MATLAB文件 1.安装插件 插件&#xff1a;在vscode拓展商店下载 MATLABMatlab in VSCode 其他&#xff1a;Windows环境MATLAB2019bpython3.7.9 2.插件配置 MATLAB插件下载后不用配置。 Matlab in VSCode需要进行相应的配置。 Windows…

【C语言】自定义类型:结构体深入解析(二)结构体内存对齐宏offsetof计算偏移量结构体传参

文章目录 &#x1f4dd;前言&#x1f320; 结构体内存对齐&#x1f309;内存对齐包含结构体的计算&#x1f320;宏offsetof计算偏移量&#x1f309;为什么存在内存对⻬?&#x1f320; 结构体传参&#x1f6a9;总结 &#x1f4dd;前言 本小节&#xff0c;我们学习结构的内存对…

C++面向对象(OOP)编程-STL详解(vector)

本文主要介绍STL六大组件&#xff0c;并主要介绍一些容器的使用。 目录 1 泛型编程 2 CSTL 3 STL 六大组件 4 容器 4.1 顺序性容器 4.1.1 顺序性容器的使用场景 4.2 关联式容器 4.2.1 关联式容器的使用场景 4.3 容器适配器 4.3.1 容器适配器的使用场景 5 具体容器的…

大模型ChatGLM下载、安装与使用

在人工智能领域&#xff0c;清华技术成果转化的公司智谱AI启动了支持中英双语的对话机器人ChatGLM内测。ChatGLM是一个初具问答和对话功能的千亿中英语言模型&#xff0c; 并针对中文进行了优化&#xff0c;现已开启邀请制内测&#xff0c;后续还会逐步扩大内测范围。 ChatGLM…

Unity中Shader平移矩阵

文章目录 前言方式一&#xff1a;对顶点本地空间下的坐标进行相加平移1、在属性面板定义一个四维变量记录在 xyz 上平移多少。2、在常量缓冲区进行申明3、在顶点着色器中&#xff0c;在进行其他坐标转化之前&#xff0c;对模型顶点本地空间下的坐标进行转化4、我们来看看效果 方…

Tomcat报404问题解决方案大全(包括tomcat可以正常运行但是报404)

文章目录 Tomcat报404问题解决方案大全(包括tomcat可以正常运行但是报404)1、正确的运行页面2、报错404问题分类解决2.1、Tomcat未配置环境变量2.2、IIs访问权限问题2.3、端口占用问题2.4、文件缺少问题解决办法&#xff1a; Tomcat报404问题解决方案大全(包括tomcat可以正常运…

智能优化算法应用:基于龙格-库塔算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于龙格-库塔算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于龙格-库塔算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.龙格-库塔算法4.实验参数设定5.算法结果…

@vue/cli脚手架

0_vue/cli 脚手架介绍 目标: webpack自己配置环境很麻烦, 下载vue/cli包,用vue命令创建脚手架项目 vue/cli是Vue官方提供的一个全局模块包(得到vue命令), 此包用于创建脚手架项目 脚手架是为了保证各施工过程顺利进行而搭设的工作平 vue/cli的好处 开箱即用 0配置webpack babe…

算法模板之栈图文详解

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;算法模板、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. ⛳️模拟栈1.1 &#x1f514;用数组模拟实现栈1.1.1 &#x1f47b;栈的定义1.1.…

SQL---Zeppeline前驱记录与后驱记录查询

内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…

JMeter常见配置及常见问题修改

一、设置JMeter默认打开字体 1、进入安装目录&#xff1a;apache-jmeter-x.x.x\bin\ 2、找到 jmeter.properties&#xff0c;打开。 3、搜索“ languageen ”&#xff0c;前面带有“#”号.。 4、去除“#”号&#xff0c;并修改为&#xff1a;languagezh_CN 或 直接新增一行&…

Zookeeper集群搭建,四字命令监控,Leader选举原理以及数据如何同步

Java学习面试指南&#xff1a;https://javaxiaobear.cn 1、集群角色 Leader&#xff1a; 领导者。 事务请求&#xff08;写操作&#xff09;的唯一调度者和处理者&#xff0c;保证集群事务处理的顺序性&#xff1b;集群内部各个服务器的调度者。对于create、setData、delete…

汽车制造厂设备故障预测与健康管理PHM

在现代汽车制造工业中&#xff0c;设备的可靠性和稳定性对于保证生产线的高效运行至关重要。为了提高生产效率、降低维修成本以及确保产品质量&#xff0c;汽车制造厂逐渐采用设备故障预测与健康管理&#xff08;PHM&#xff09;系统&#xff0c;以实现对设备状态的实时监测和预…