【Linux】线程的理解

embedded/2024/10/21 7:46:44/

目录

页表详解

简单创建线程


线程可谓是大名鼎鼎了,它的重要性不亚于我们之前学的进程,之所以说进程重要,那是因为进程就代表着用户,用户所作的操作都是以进程为载体的,而今天要说的线程甚至比进程还要重要。那么什么是线程呢?

线程是进程内部的一个执行分支,线程是CPU调度的基本单位,当然这对于支持线程的操作系统来说是正确的,我们后面会说到,Linux操作系统并不直接支持线程,而是利用轻量级进程封装出的线程

加载到内存中的程序叫做进程,当然这个解释抽象不具体。所以我们一般说进程=内核数据结构+程序的代码和数据

我们之前创建一个进程,它的代码的执行顺序都是串行(xing)执行的,我们当然可以创建子进程让代码并行执行,但是进程的创建的时间和空间成本都太大了,我们应该如何减小创建的成本呢?

后来我们发现,其实我们想要的只是多执行流,而不是多进程,只要能实现多执行流就可以,所以,与其创建进程,不如将一个程序的代码分份,然后让不同的线程去执行不同的代码,这样我们创建的成本就变小了

那我们应该如何设计线程呢?是不是还要像进程一样创建线程单独的TCB(Thread Control Block)呢?后来Linux的设计者就认为不如直接让线程复用进程的代码,这样会使代码更加稳健,可维护性更强。所以我们的Linux就是使用的进程来模拟的线程,所以有关线程的信息也是存放在task_struct这个结构体中。

我们之前学的进程就是内部只有一个线程,而从今以后就是进程内部至少有一个线程。说到这里,那再聊一聊什么是进程,从资源的角度,进程是承担分配系统资源的基本实体,而线程仅仅是进程内部的一个执行分支。

从CPU的角度,CPU不需要关心task_struct是进程的还是线程的,它还是只负责使用进程调度的那一套算法去调度线程

所以,执行流有时可能是一个进程,有时可能是一个线程,总体下来就是在它们之间,所以我们叫做轻量级进程,这也就解释了我上面说的Linux是用轻量级进程封装出的线程

有了上面的一些理解,我们就不得不说一个进程是如何进行代码划分成多个线程的呢?

其实在磁盘上的程序中的各行代码都是有地址的,不同的线程执行不同的函数,每个进程的虚拟地址经过页表的映射就拿到了只属于它自己的代码和数据,这样就划分好了,下面我们具体的来说明一下这个过程

首先OS肯定是要管理内存的,比如内存有4GB,OS会把内存分成4KB为一个基本单位(所以我们写时拷贝或者new,malloc都是以4KB为基本单位的),这样4GB可以分成1048576份,并且我们说过,磁盘中的文件管理也是以4KB为基本单位的,可执行程序只要写到磁盘中就是按4KB去分块的,我们就把4KB这一个基本单位叫做页框或者页帧。

我们要管理内存,就要对4KB进行描述和组织,我们可以创建struct page用来描述这4KB的空间,同时用一个数组来组织这1048576个4KB,比如叫struct page memory[1048576],并且这个数组也占不了多少空间,大概就几~十几MB,这样对于内存的管理就变成了对于数组的增删查改

页表详解

其次,我们之前对于页表的理解也是有误的,我们可以简单的算一下,进程地址空间有4GB的地址,就是32个比特位最多可以表示2^32-1个虚拟地址,比如一个地址占四个字节,页表中要存这么多的虚拟地址到物理地址的映射,这需要2^32*4*2=32GB这显然是不可能的。

所以真实的页表是这样的:

一个地址有32个比特位,分成三组,10,10和12

在查地址的时候,我们首先取前十位,查页目录,十位可以表示0-1023,一共1024,所以页目录中要存1024个页表的地址。所以最多有1024个页表

找到页表后我们再取中间的十位去页表中找地址,一个页表最多也是存1024个地址。找到了页表中的地址,我们就找到了物理内存中的页框,最后取后12位(就是4096==4KB)作为页框的偏移量就能定位到一个页框中的唯一地址

用图来表示就是:

这样我们再算一算页目录和页表需要多少空间

1024*1025*4=4MB,并且一个程序很小,根本用不到1024张页表

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

简单创建线程

下面我们就来简单的写个线程创建的代码,我们要用到的接口是

man 3 pthread_create

第一个参数是一个输出型参数,就是告诉我们这个线程的对于用户而言的唯一标识符

第二个参数是线程的一些属性,我们给nullptr就行

第三个参数是让新线程执行的函数

第四个参数是给第三个参数传的参数

我们简单来用一下

//thread.cc
#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<sys/types.h>
using namespace std;
void* newthreadrun(void*args)
{while(1){cout<<"I am new thread"<<" pid: "<<getpid()<<endl;sleep(1);}
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,newthreadrun,nullptr);while(1){cout<<"I am main thread"<<" pid: "<<getpid()<<endl;sleep(1);}return 0;
}
//makefile
thread:thread.ccg++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:rm -f thread

我们可以看到结果中主线程和新线程的pid是一样的,证明它们确实是一个进程。那如何区分主和新线程呢?我们可以用ps -aL,这个命令就是查看轻量级进程的意思

我们可以看到同样的pid,LWP(light weight process轻量级进程)是不同的,并且主线程的pid和LWP是一样的

所以操作系统在线程调度时,拿的就是LWP

OS没有直接提供像getpid一样直接获取LWP的系统调用

并且我们可以看到在makefile中要加上-lpthread,意思就是要显示链接上Linux的原生线程库


http://www.ppmy.cn/embedded/86883.html

相关文章

PHP压缩打包,下载目录或者文件,解压zip文件

函数 /*** 压缩整个文件夹为zip文件* 本地需要绝对路径&#xff0c;服务器需要相对路径*/function makeZipFile($zip_path , $folder_path ) {$rootPath realpath($folder_path);$zip new ZipArchive(); // $zip->open($zip_path, ZipArchive::CREATE | ZipArchi…

org.springframework.context.annotation.DeferredImportSelector如何使用?

DeferredImportSelector 是 Spring 框架中一个比较高级的功能&#xff0c;主要用于在 Spring 应用上下文的配置阶段延迟导入某些组件或配置。这个功能特别有用&#xff0c;比如在处理依赖于其他自动配置的场景&#xff0c;或者当你想基于某些条件来决定是否导入特定的配置类时。…

从零开始构建你的第一个Python Web应用

在本文中&#xff0c;我们将带领你从零开始构建一个简单的Python Web应用。不需要任何先验知识&#xff0c;我们会一步步地指导你完成设置、框架选择、代码编写到部署的整个过程。无论你是Web开发新手还是希望扩展技能的老手&#xff0c;这篇文章都将为你提供一个实践操作的起点…

免费听书,看小说全搞定

今天分享几个免费听书&#xff0c;看小说的app。 首先是之前分享过的 笔趣阁&#xff0c;搜书名&#xff0c;作者&#xff0c;主角。 开始阅读。 设置样式。 漫画分类。 第2个是内置书源版的小书包&#xff0c;它是基于阅读二次开发的版本&#xff0c;继承了阅读中的实用功能&…

使用git工具管理泰山派内核源码目录及抽打补丁简易流程

目录 使用git工具管理泰山派内核源码目录及抽打补丁简易流程 一、使用git维护源码 二、git 常用的一些操作 三、抽补丁 四、打补丁 五、补充 使用git工具管理泰山派内核源码目录及抽打补丁简易流程 最近&#xff0c;在做linux开发的过程中入手了一块泰山派RK3566的开发板…

新能源汽车的充电网络安全威胁和防护措施

1. 物理攻击&#xff1a;例如恶意破坏、搬走充电设施等&#xff0c;这可能会对充电设施造成损害&#xff0c;妨碍正常的电力传输。 2. 网络攻击&#xff1a; 黑客可能利用系统漏洞攻击网络&#xff0c;破坏设备&#xff0c;并窃取用户的个人信息、支付信息等&#xff1b; 车辆…

OpenJudge | 波兰表达式

总时间限制: 1000ms 内存限制: 65536kB 描述 波兰表达式是一种把运算符前置的算术表达式&#xff0c;例如普通的表达式2 3的波兰表示法为 2 3。波兰表达式的优点是运算符之间不必有优先级关系&#xff0c;也不必用括号改变运算次序&#xff0c;例如(2 3) * 4的波兰表示法为…

ctfshow-web入门-php特性(web147-web150_plus)

目录 1、web147 2、web148 3、web149 4、web150 5、web150_plus 1、web147 ^&#xff1a;匹配字符串的开头。 $&#xff1a;匹配字符串的结尾&#xff0c;确保整个字符串符合规则。 [a-z0-9_]&#xff1a;表示允许小写字母、数字和下划线。 *&#xff1a;匹配零个或多个前面…