当前位置: 首页 > news >正文

【Linux线程篇】探索Linux多线程:并行编程的入门指南

W...Y的主页 😊

代码仓库分享💕 

 Linux线程概念

什么是线程

在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”一切进程至少都有一个执行线程线程在进程内部运行,本质是在进程地址空间内运行
在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流。

在Linux系统下,线程与进程的最终目的都是一样的,都是想要让程序并发执行,所以我们可以将代码进行分块执行,所以就要创建自己的tcb结构体来维护自己的上下文、优先级等等。但是Linux设计者认为pcb与tcb具有极度的相似性没必要设计单独设计数据结构和算法,直接复用既可,所以Linux中的线程也被称为“轻量化进程”!!!

所以进程是一个或多个执行流加页表加地址空间加代码和数据。进程在内核角度为承担分配系统资源的实体!!!

那多个执行流是怎么划分代码呢?
我们经常说申请内存和释放内存,所以我们应该对内存做管理,其实物理内存也会被划分为4kb(大部分划分规则)的数据块,而磁盘中的数据块也是4kb的,他们被我们称为页框或页帧。当我们内存与数据块的交互时就是以数据块为单位的交互。操作系统要管理内存,肯定要先描述再组织,所以内存有属于自己的结构体struct page,其结构体的内容也应该是记录一个数据块的状态,使用者是谁,是否被使用,是否可以交换等等。假如我们物理内存为4GB,其就有1048576个4kb数据块,我们就可以创建一个是结构体数组进行存储结构体内容。

其源码一部分:

所以我们的页表也不可能是所谓的kv关系,因为kv分别代表一个指针,再加上其对应权限可能有10个字节,但是如果是一一对应的关系其页表可能会有40GB大小,所以页表是怎么映射的呢?虚拟地址是32位二进制,划分为三个部分10位10位12位,其第一个10位被称为页目录,需要创建2^10个格子,第二个10位被称为页表,页目录保存的是二级页表的地址,所以页表应该有1024个,每个页表中有1024个页表项,每一个页表项中存放的是4kb内存块的起始地址(也就是页框的物理地址)。而我们的后12位的大小为4kb,所以我们又知道其对应的起始地址,所以我们对应的起始物理地址+后12位偏移量就是最终所访问的数据。所以我们的后12位被称为页内偏移。

我们也可以再页表中加上标志位,让CPU与页表中的标志位进行比对,如果相同就可以访问物理内存的内容。 

所以给不同的线程分配不同的区域,本质就是让不同的线程各自看到全部页表的子集。

那是怎么才能看到不同的页表呢?这里我们得转到应用层面来看,所以让我们先了解一些系统调用函数:

第一个参数线程id,第二个参数线程属性,第三个参数是返回值void*参数也是void*的函数指针,第四个参数是我们给第三个函数指针所指向的函数所传递的参数。

一般情况下我们的第二个参数可以写nullptr。

注意:在我们使用pthread库中函数时,在编译时必须链接上pthread库!!!

这样我们就可以简短写一个代码:

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>void *newthreadrun(void *args)
{while (true){std::cout << "I am new thread, pid: " << getpid() << std::endl;sleep(1);}
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, newthreadrun, nullptr);while (true){std::cout << "I am main thread, pid: " << getpid() << std::endl;sleep(1);}
}

这时我们的代码就有两个执行流,我们从main函数开始运行,当走到pthread_create函数时新线程创建成功会进入newthreadrun函数中去,主线程继续往下执行即可。

 在这里我们只能看到一个进程,我们可以使用ps -aL查看轻量级进程就可以看到两个进程了。

主线程的PID==LWP,但是其线程所有的PID都是相同的。所以我们在回归到上述问题上每一句代码都有其对应的虚拟地址,主线程拥有的是主函数的虚拟地址其余线程得到的是其他函数的虚拟地址,对应其页表就可以访问到不同的数据。 

所谓的Linux就根本没有线程这个说法,只有轻量化进程,所以对应的Linux内核就没有对线程的系统调用只有对轻量化进程的系统调用。而用户只知道线程不知道轻量化进程,所以设计者在用户和内核之间设计一个软件层pthread库——原生线程库。这个库的作用就是将Linux的轻量级进程系统调用进行封装,转化成线程相关接口提供给用户。

轻量级进程也有直接的系统调用,但是太复杂一般没有人想用!!! 


以上就是本次全部内容,感谢大家观看! 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 深入了解 PXE:定义、架构、原理、应用场景及常见命令体系
  • 常见的网络协议在不同的模型中层次分布
  • 阿里云 Ubuntu 开启允许 ssh 密码方式登录
  • 云原生监控-Kubernetes-Promethues-Grafana
  • 04-ArcGIS For JavaScript的可视域分析功能
  • 架构面试-分布式存储系统HA高可用原理及应用案例实战
  • vite+vue3拍照上传到nodejs服务器
  • Python获取对象属性的三大方法(__dict__、vars()、__slots__)的区别
  • 商汤绝影秀肌肉:端到端潮流来袭
  • Emacs有什么优点,用Emacs写程序比IDE更方便吗?
  • 简单小插画:成都亚恒丰创教育科技有限公司
  • 2024年浙江省高考分数一分一段数据可视化
  • 如何通过SPI机制去实现读取配置文件并动态加载对应实现类
  • SLAM中的块矩阵与schur补
  • 平安银行秋招攻略,考试内容详解
  • 分享一款快速APP功能测试工具
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • Android框架之Volley
  • angular学习第一篇-----环境搭建
  • Docker: 容器互访的三种方式
  • IE报vuex requires a Promise polyfill in this browser问题解决
  • Javascript Math对象和Date对象常用方法详解
  • Java方法详解
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • storm drpc实例
  • 番外篇1:在Windows环境下安装JDK
  • 翻译 | 老司机带你秒懂内存管理 - 第一部(共三部)
  • 前端攻城师
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 突破自己的技术思维
  • 限制Java线程池运行线程以及等待线程数量的策略
  • raise 与 raise ... from 的区别
  • 大数据全解:定义、价值及挑战
  • ​如何在iOS手机上查看应用日志
  • #HarmonyOS:基础语法
  • #前后端分离# 头条发布系统
  • (+4)2.2UML建模图
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (leetcode学习)236. 二叉树的最近公共祖先
  • (vue)el-cascader级联选择器按勾选的顺序传值,摆脱层级约束
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (紀錄)[ASP.NET MVC][jQuery]-2 純手工打造屬於自己的 jQuery GridView (含完整程式碼下載)...
  • (六)vue-router+UI组件库
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (学习总结16)C++模版2
  • (转)编辑寄语:因为爱心,所以美丽
  • (转)利用ant在Mac 下自动化打包签名Android程序
  • .gitignore
  • .NET 3.0 Framework已经被添加到WindowUpdate
  • .net 7 上传文件踩坑
  • .net framework 4.8 开发windows系统服务
  • .net Signalr 使用笔记
  • .NET 命令行参数包含应用程序路径吗?
  • .NET版Word处理控件Aspose.words功能演示:在ASP.NET MVC中创建MS Word编辑器
  • .vollhavhelp-V-XXXXXXXX勒索病毒的最新威胁:如何恢复您的数据?