【Linux】解锁线程基本概念和线程控制,步入多线程学习的大门
目录
1、线程初识
1.1线程的概念
1.2.关于线程和进程的进一步理解
1.3.线程的设计理念
1.4.进程vs线程(图解)
1.5地址空间的第四谈
2.线程的控制:
2.1.关于线程控制的前置知识
2.2创建线程的系统调用:
这个几号手册具体代表的什么含义?
2.3.线程终止
我们怎么没有像进程一样获取线程退出的退出信号呢?
2.4.线程等待:
为什么需要线程等待?
2.5. 分离线程
3.关于进程和线程比较的深层次问题
3.1已经有多进程了,为什么还要有多线程??
3.2线程的切换vs进程的切换
4.总结:
4.1.线程的优点:
4.2.线程的缺点:
4.3.线程异常:
1、线程初识
1.1线程的概念
线程是进程内部的一个执行分支,线程是CPU调度的基本单位
那什么是进程呢?
我们之前学习了解到的进程是加载到内存中的程序
进程 = 内核数据结构 + 进程代码和数据。
今天我们要推翻这一观点,该观点是片面的,不正确的!
我们之前认为的task_struct就是进程,其实这一个个的task_struct是我们的进程的执行流!!!那进程究竟是什么呢?
进程其实是包含文件描述符表,pcb,页表等等,上面框的一整套才是真正的进程!
1.2.关于线程和进程的进一步理解
我们要抛弃之前的想法,进程不仅仅是PCB数据结构。而是一整套资源的时候,我们就应该清楚进程创建成本很高。原因就是:创建进程时还需要构建文件描述符,信号表,PCB,页表,等等这就会造成空间和时间的浪费。
从内核来看:进程本质上是是一个容器,承担分配系统资源的基本实体,包括执行流资源、地址空间资源、页表映射关系,代码和数据这一整套的资源。
线程只是进程当中的一个执行分支~
1.3.线程的设计理念
线程我们一般称为tcb(进程是pcb),对于线程来说,也一定要和进程一样需要对应操作方法:新建,暂停 ,销毁,调度。那我们如何对线程进行这些操作呢?
如果我们要设计线程,OS也要对线程进行管理。(先描述,再组织)
Linux的设计者认为,进程和线程都是执行流,具有极度的相似性,没必要单独设计数据结构和算法,直接复用代码,所以Linux是用进程模拟的线程!
1.4.进程vs线程(图解)
横向的是进程,纵向的是执行流。我们以前讲的进程,是今天讲的特殊情况
1.5地址空间的第四谈
如何从页表定位到物理地址?
页目录保存的是二级页表的地址
以后我们在查页表的时候,先拿虚拟地址中的前10位,查页目录,选择具体哪一个页表,然后再查询虚拟地址的中间10位就能找到页框,就肯定能找到一个物理地址,最后我们拿着虚拟地址的最后12位,就是页内偏移,因此就直接找到物理内存中的地址了,有页框的物理初始地址,后面还有虚拟地址后12位的页内偏移就可以定位一个物理地址了!
页表里面存储的是页框的物理地址。
给不同的线程分配不同的区域,本质就是让不同的线程,各自看到全部页表的子集!
2.线程的控制:
2.1.关于线程控制的前置知识
在学习之前,我们要清楚关于创建新线程的前置知识——POSIX线程库。
- 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的
- 要使用这些函数库,要通过引入头文<pthread.h>
- 链接这些线程函数库时要使用编译器命令的“-lpthread”选项(makefile文件要加上-lpthread”选项)
用户层面:用户知道“轻量级进程”这个概念吗?并没有,只有线程和进程,
系统层面:将轻量级进程的系统调用进行封装,转成线程相关的接口提供给用户
内核层面:Linux到底有没有真线程呢?没有,Linux只有轻量级进程。Linux系统,不会有线程相关的系统调用,只有轻量级进程的系统调用
新线程和主线程,谁先运行呢?不确定,由调度器决定
2.2创建线程的系统调用:
这个几号手册具体代表的什么含义?
这个3就是代表他在3号手册中
这种几号手册,一般来说,1号手册都是一些命令,2号手册都是系统调用,3号手册就是C库函数。就是分门别类放置不同的东西。
功能:创建一个新的线程
原型
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
参数:
thread:返回线程ID
attr:设置线程的属性,attr为NULL表示使用默认属性
start_routine:是个函数地址,线程启动后要执行的函数
arg:传给线程启动函数的参数
返回值:成功返回0;失败返回错误码
代码:
#include <iostream>
#include <pthread.h>
#include <unistd.h>// 新线程
void *threadStart(void *args)
{while (true){std::cout << "new thread running..." << std::endl;sleep(1);}
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadStart, (void *)"sthread-ew");// 主线程while (true){sleep(1);std::cout << "main thread running..." << std::endl;}return 0;
}
当我们进行make编译的时候,会报错:(.text+0x1b): undefined reference to main
线程未定义,之所以会出错是因为Linux下使用线程需要引用线程库。必须在makefile文件中加上-lpthread,因为线程库里面是函数。编译器并不认识,不是C语言/C++自带的,而是Linux自己创建的原生线程库文件
再加上线程库之后,就可以正常运行了。
可以看见,主线程和新线程是可以同时运行的!并且主线程和新线程用的是同一个pid。
所以这两个虽然都是不同的执行流,但是是属于同一个进程内部的,我们可以使用ps -al来查看不同的线程信息
这个pid是对应进程的pid,这个LWP其实就是这个线程的id!!!
2.3.线程终止
同一个进程内的线程,大部分的资源都是共享的,地址空间是共享的!
主线程退出 == 进程退出 == 所有线程都要退出
多线程代码往往健壮性不好。进程之间是独立的,不能共享资源哦。但是线程可以共享资源哦!
我们怎么没有像进程一样获取线程退出的退出信号呢?
因为线程出异常了,会将整个进程退出,根本没有机会读到pthread_read的退出信号,因此该函数并不考虑退出信息。但是父进程会接受子进程退出时的异常信息
如果需要只终止某个线程而不终止整个进程,可以有三种方法:
- 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
- 线程可以调用pthread_ exit终止自己。
- 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程
不能用exit终止线程,因为他是终止进程的!
pthread_ exit函数
功能:线程终止
原型
void pthread_exit(void *value_ptr);
参数
value_ptr:value_ptr不要指向一个局部变量。
返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)
pthread_ cancel函数
功能:取消一个执行中的线程
原型
int pthread_cancel(pthread_t thread);
参数
thread:线程ID
返回值:成功返回0;失败返回错误码
2.4.线程等待:
为什么需要线程等待?
- 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。不然也会造成内存泄露问题!
- 创建新的线程不会复用刚才退出线程的地址空间。
pthread_join函数
功能:等待线程结束
原型
int pthread_join(pthread_t thread, void **value_ptr);
参数
thread:线程ID
value_ptr:它指向一个指针,后者指向线程的返回值
返回值:成功返回0;失败返回错误码
2.5. 分离线程
分离线程的概念:
- 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
- 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。这就是分离线程
int pthread_detach(pthread_t thread);
可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离:
pthread_detach(pthread_self());
被分离的线程不需要join,虽然线程已经被分离了,但是资源还是要共享,所以当主线程退出时,被分离的线程也是要退出的,底层也是同一个进程!只是不需要退出了。我们希望主线程是最后一个退出的。所以一般程序的主线程永远不会退出,是永驻进程!
3.关于进程和线程比较的深层次问题
3.1已经有多进程了,为什么还要有多线程??
创建一个进程需要创建PCB,地址空间,页表,加载代码与数据,创建文件缓冲区等很多操作,但创建一个线程,只需要创建一个PCB,复用原本的地址空间。创建进程的成本比创建线程高很多!切换进程时不仅仅要更换上下文数据,更换地址空间等很多操作,切换线程只需要切换PCB!!!线程删除成本也很低。
但是线程也有缺陷:一个线程出错(野指针)就是这个进程出错了,因为他们使用同一个地址空间,所以其他的线程也会报错退出!!! 线程的健壮性很差!而进程是独立的互不影响!进程和线程各有特长!
3.2线程的切换vs进程的切换
CPU里面的cache会将后续代码提前缓存到cache(默认会从后续代码编译,如果调用函数跳转代码就会读取失败,也就是cache命中失败,但命中失败的概率较低),这样就不用一直向内存中提取内容,大大提高了CPU寻址的效率,所以以后CPU读取数据的时候,就不需要从内存中读取数据了,直接从CPU里面的cache里面读取
所以进程切换不仅仅要考虑到寄存器之间的变化,但也要考虑cache的切换,cache可能只对上一个进程有效,切换进程之后,就会丢弃,重新寻址,这部分要消耗的成本很大
但是如果是线程切换,寄存器、上下文代码同样也要变化,但是因为是线程,共享同一份代码所以cache不需要更换,因此就减少了很多的成本。
因此线程切换要比进程切换更简单,消耗的成本更低!
4.总结:
4.1.线程的优点:
- 创建一个新线程的代价要比创建一个新进程小得多
- 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
- 线程占用的资源要比进程少很多
- 能充分利用多处理器的可并行数量
- 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
- 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现I
- /O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作
4.2.线程的缺点:
- 健壮性降低
编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。 - 缺乏访问控制
进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。 - 编程难度提高
编写与调试一个多线程程序比单线程程序困难得多
4.3.线程异常:
- 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃
- 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出
相关文章:

【Linux】解锁线程基本概念和线程控制,步入多线程学习的大门
目录 1、线程初识 1.1线程的概念 1.2.关于线程和进程的进一步理解 1.3.线程的设计理念 1.4.进程vs线程(图解) 1.5地址空间的第四谈 2.线程的控制: 2.1.关于线程控制的前置知识 2.2创建线程的系统调用: 这个几号手册具体…...

uniapp学习(005-2 详解Part.2)
零基础入门uniapp Vue3组合式API版本到咸虾米壁纸项目实战,开发打包微信小程序、抖音小程序、H5、安卓APP客户端等 总时长 23:40:00 共116P 此文章包含第41p-第p47的内容 文章目录 mainifest.json文件配置获取微信小程序appid注册微信小程序微信小程序控制台图形界…...

深度学习的关键概念和术语
特征 特征是图像上可进行视觉辨识的区域。特征通常代表对应用相关的内容(缺陷、对象、对象的特定部分)。 特征尺寸 仅用于聚焦模式下的绿色分类、红色、蓝色定位和蓝色读取工具。 您认为对分析图像内容最重要的图像特征的主观大小。该特征尺寸确定用于…...

navicate可视化数据库操作-cnblog
1 连接数据库 点击链接,自定义名称,输入root密码 2 准备按照图例创建数据库demo 3 新建数据库...

kubernetes中的微服务
目录 一 什么是微服务 二 微服务的类型 三 ipvs模式 3.1 ipvs模式配置方式 四 微服务类型详解 4.1 clusterip 4.2 ClusterIP中的特殊模式headless 4.3 nodeport 4.4 loadbalancer 4.5 metalLB 4.6 externalname 五 Ingress-nginx 5.1 ingress-nginx功能 5.2 部署…...

Python 量子机器学习及其应用
Python 量子机器学习及其应用 目录 🌀 量子机器学习的基础概念💡 量子计算的原理与经典计算的区别🔑 量子算法在机器学习中的应用潜力⚛️ 量子计算与经典机器学习算法的结合🚀 案例展示:量子算法提升机器学习效率&a…...

echarts显示隐藏柱状图柱子的背景色
showBackground: true, //控制是否显示背景色backgroundStyle: {// color: rgba(180, 180, 180, 0.4) //背景色的颜色color: red} 关键代码是 showBackground: true, //控制是否显示背景色 设置为false或者直接而不写就是不显示背景色,默认是不显示背景色 true的时…...

QT文件操作【记事本】
mainwindow.h核心函数 QFileDialog::getOpenFileName()QFileDialog::getSaveFileName() #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include<QFileDialog> #include<QMessageBox> #include<QDebug> #include<QFile> #…...

Linux 定时备份系统日志
Linux 定时备份系统日志 SSH跨机免密登录复制备份到另一台虚机上开启定时任务 SSH跨机免密登录 定时备份首先要实现免登入 一、scp 一个文件从其他服务器到本机,怎么跳过ssh登录验证呢? 要在使用SCP时跳过密码登录,你可以设置SSH密钥认证。首…...

音视频入门基础:FLV专题(15)——Video Tag简介
一、引言 根据《video_file_format_spec_v10_1.pdf》第75页,如果某个Tag的Tag header中的TagType值为9,表示该Tag为Video Tag: 这时StreamID之后紧接着的就是VideoTagHeader,也就是说这时Tag header之后的就是VideoTagHeader&…...

尚硅谷rabbitmq2024 第15-18节 springboot整合与可靠性答疑
在spring boot项目中,只引入了一个amqp的starter,为什么在写listener的时候能看到rabbitmq相关的类,比如RabbitListener( public void processMessage(String dataString, Message message, channel channel){ 这里的Message就是rabbitmq下面…...

ctfshow-web 萌新题
给她 pyload: 1.dirsearch扫描,发现git 2. GitHack工具得到.git文件 <?php $passsprintf("and pass%s",addslashes($_GET[pass])); $sqlsprintf("select * from user where name%s $pass",addslashes($_GET[name])); ?>addslashes函…...

基于RPA+AI的网页自动填写机器人 | OPENAIGC开发者大赛高校组优秀作品
在第二届拯救者杯OPENAIGC开发者大赛中,涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到,我们特意开设了优秀作品报道专栏,旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者,希望能带给…...

Tmux常用操作--云GPU版
Tmux是什么,作用? Tmux是一个终端复用器(terminal multiplexer),属于常用的开发工具。 作用 使用Tmux创建守护进程,可以使得关闭PyCharm或者其他终端的情况下,远程服务器(云GPU&a…...

股市入门常见术语介绍
鉴于最近行情讨论火热,我也想借此平台,结合我大学时期身边同学老师的投资经历,写一篇交易入门术语简介。内容不多但是足以达到科普之用。 希望大家能谨慎对待投资,始终保持谦虚学习的态度。不要迷失在瞬息万变的金融市场&…...

专栏十九:单细胞大数据时代使用scvi和scanpy整合数据
慢更ing,主要是记录自己在分析中的一些困惑 一、基础知识和解惑 放在最前面,是因为scvi整合不像harmony,傻瓜式操作,很多地方还是要注意一下的。 1.如何正确的寻找HVGs 一般我们使用的函数就是scanpy.pp.highly_variable_genes,里面的参数较为复杂。 Q:输入数据的格…...

C语言编程必备知识
C语言是编程领域中基础且广泛使用的语言之一,掌握C语言编程需要一些核心知识,涵盖基本语法、内存管理、数据结构等方面。以下是C语言编程中的一些必备知识点: 1. **基础语法** - **变量声明**:所有变量都需要在使用前声明&…...

k8s 1.28 集群部署
文章目录 环境配置安装docker安装cri-dockerd(Docker与Kubernetes通信的中间程序): 部署kubernetes 环境配置 关闭Selinux #永久 sed -i s/enforcing/disabled/ /etc/selinux/config #临时 setenforce 0 关闭Swap #临时 swapoff-a #永久 sed -ri s/.*swap.*/#&a…...

python入门教程
Python 是一种非常流行的编程语言,因其简单易学的语法和广泛的应用领域(如数据分析、人工智能、Web 开发等)而备受欢迎。以下是一个入门级 Python 教程,适合初学者快速掌握 Python 的基础知识。 1. 安装 Python 你可以从 Python…...

bat(批处理脚本学习)
输出banner echo off echo () echo JL echo ^|^| echo LJ echo _,--"""""""---. echo , …...

【JAVA毕业设计】基于Vue和SpringBoot的渔具租赁系统
本文项目编号 T 005 ,文末自助获取源码 \color{red}{T005,文末自助获取源码} T005,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 渔…...

Maven和Gradle的对比
Maven和Gradle都是Java项目构建工具,它们在帮助开发者管理项目依赖、编译、打包等方面发挥着重要作用。 Maven和Gradle的区别 1、语法与配置文件 Maven使用XML作为配置文件(如pom.xml)的语言,XML结构清晰但相对冗长。Gradle则使…...

Windows安装Ollama环境
在Windows环境下,可以安装Ollama,然后在其上面下载相应的大语言模式,下面是目前支持的LLM及相应的命令等信息: Model Parameters Size Download Llama 38B4.7GBollama run llama3Llama 370B40GBollama run llama3:70bPhi-33.8B2.3GBollama run phi3Mistral7B4.1GBollama ru…...

Java入门:11.抽象类,接口,instanceof,类关系,克隆
1 JDK中的包 JDK JRE 开发工具集(javac.exe) JRE JVM java类库 JVM java 虚拟机 jdk中自带了许多的包(类) , 常用的有 java.lang 该包中的类,不需要引用,可以直接使用。 例如࿱…...

【软件部署安装】OpenOffice转换PDF字体乱码
现象与原因分析 执行fc-list查看系统字体 经分析发现,linux默认不带中文字体,因此打开我们本地的windows系统的TTF、TTC字体安装到centos机器上。 安装字体 将Windows的路径: C:\Windows\Fonts 的中文字体,如扩展名为 TTC 与TT…...

工程师 - 开源硬件公司Adafruit介绍
https://www.adafruit.com/ https://github.com/adafruit 开源硬件公司 Adafruit 的发展历程 如果你是一名创客(Maker),那么你肯定听过 Adafruit;如果你在项目中使用过 Arduino,那么你应该也会知道 Adafruit。假如你没…...

PostgreSQL学习笔记五:数据库基本操作
在 PostgreSQL 中,您可以执行一系列基础操作来管理数据库、备份和恢复数据。以下是一些常用的命令和步骤: 创建数据库 使用以下命令创建新数据库: CREATE DATABASE database_name;您也可以在创建时指定数据库所有者和其他参数:…...

住房公积金 计算器-java方法
计算了一下房贷压力,以全额公积金贷款为例,贷款四十万,等额本金方式还款,房贷利率为2.85%,基本情况就是如下: 还款总额达到 提前还款的好处 按三十年计算,如果第一年借用亲朋好友的钱&#x…...

Spring-Smart-DI
参考文章 作用 用注解的方式动态切换实现类实现方式。 比如我们有多个消息中间件或多个短信服务商,需要动态切换的时候,无需自己写判断逻辑来进行服务商的切换。只用一套注解就可以解决问题 开始使用 引入依赖 <dependency><groupId>io…...

Python 在树莓派上实现WIFI连接
开发语言:Python 硬件环境:树莓派 4B 运行环境:Raspberry Pi OS 64-bit 连接WIFI还是比较简单的,用了好几个Python 库都效果不怎么理想,最后还是使用命令的方式是最自在的 先断开当前的连接 wlan0 可以替换成你当前…...