0

0

初识Linux · 线程概念

蓮花仙者

蓮花仙者

发布时间:2025-06-21 12:12:26

|

182人浏览过

|

来源于php中文网

原创

前言:

linux的学习从开始到现在,我们已经经历了许多大boss,从一开始的熟悉指令,到第一次在gcc环境下编译c语言的代码,到理解文件系统,比如理解了文件的权限,万物皆文件的概念,此时,是我们经历的第一次大boss,文件系统。

之后,我们从shell开始慢慢理解Linux的系统内核部分,最典型的是我们慢慢开始理解了什么是进程,从pcb->task_struct到mm_struct地址空间,到页表部分,最后我们从物理内存出发,一步一步的理解了程序的地址,之后,我们学习了进程的状态,学习了进程的控制,学习了进程等待,学习了进程终止,以及进程的各种信息,比如Pid,这是第二次大boss。

今天,我们学习的是Linux中的第3个大boss,线程。线程我们同样,从概念入手,再到线程的控制,线程同步,线程互斥等,和前两个一样,都是需要我们反反复复学习的知识点。

那么,话不多说,本文作为线程的概念篇,主要是解释线程中的概念,并且结合少许的代码。

进入第一个主题吧!线程的概念。


线程的背景

介绍线程概念我们打算从地址空间入手,在地址空间篇章,我们就提及到过,地址空间我们是要多次介绍的,一共要介绍4次左右,今天就是第四次。

我们知道,之前对于地址空间的理解就是:

初识Linux · 线程概念

这个图我们已经十分熟络了,通过页表和MMU可以将虚拟内存和物理内存建立某种联系。可是,今天我们对于页表要理解的更深层一点。

假设在磁盘中有一个hello.o,它是文件吧?那么既然他是文件,它就应该有自己的inode,它也应该存在于物理内存中。既然是和物理内存挂钩,在前文介绍的磁盘管理中,分为扇区等区域,并且是以4kb的大小进行管理。此时,真实的数据块加载到了物理内存里面,通过某种方式,和虚拟内存建立了联系。

那么,我们不妨来解析一下虚拟内存,我们将虚拟内存分为三部分:0000 0000 00,前10位是第一部分,00 0000 0000,中间10位,是第二部分,0000 0000 0000是第三部分。

好吧,在此之前我们其实还是应该再深度解析一下物理内存。

磁盘中的文件的数据是以4kb的方式存在的,我们将每个物理内存叫做页框,每个文件的数据块叫做页帧,其实现在操作系统的书已经没有区分的那么多了。

对于IO来说,IO的基本单位是4kb,OS要进行内存管理的话,不是以字节的方式管理的,而是以内存块的方式管理的,所以文件的数据块,物理内存的页框都是内存管理的对象。

那么OS应该如何管理页框页帧呢?

当然是先描述再组织。在Linux中有一种结构体是struct page,里面存的就是页框的信息。

在Linux的源码里应该如何管理呢?已经描述好了,那么使用一个数组就管理起来了。

现在我们在来刨析虚拟内存,前10个比特位,其实是指向的页目录,我们是拿32位机器举例,所以页目录实际上是由1024个,那么一个一个的页目录,指向了一个一个的页表,对于虚拟地址的中间10位,指向的就是页表,对于后12位的虚拟地址,就是先定位到了页目录之后,才好定位到页框的位置,这样,就能通过虚拟地址找到物理内存了。

那么,什么是函数呢?或者说,虚拟地址的本质是什么呢?

其实在mm_struct中,正文的代码本来就是由一个一个的地址构成的,对于函数来说,不过是一连串的地址而已。所以对于代码数据划分的本质,不过是一种对于资源的划分!


线程的概念和Linux中的线程实现

上面其实是对于页表的一种重新理解,可能有人觉得和今天的主题线程没有关系,实则不然,因为今天实际上会对之前进程的理解有一个颠覆性的理解。

在深入了解操作系统这本书里面说的好,进程是一种抽象。我们之前认为的进程是task_struct + mm_struct + 页表集合等。认为进程 = 内核数据结构 + 自己的代码和数据。

可是,在内核的观点里面,认为进程实际上是承担分配系统资源的基本实体。

今天就不讲故事了,我们直接说,以前认为task_struct就是进程,认为cpu调度的时候就是调度的task_strcut。

实际上不是的,一个进程可以存在多个task_struct,而对于task_struct就是Linux中的线程,为什么说是Linux中的线程呢?因为对于windows来说,windows也有自己的线程标准。

Android架构基本知识 中文WORD版
Android架构基本知识 中文WORD版

本文档主要讲述的是Android架构基本知识;Android依赖Linux内核2.6来提供核心服务,比如进程管理、网络协议栈、硬件驱动。在这里,Linux内核作为硬件层和系统软件栈层之间的一个抽象层。这个操作系统并非类GNU/Linux的,因为其系统库,系统初始化和编程接口都和标准的Linux系统是有所不同的。 Android 包含一些C/C++库、媒体库、数据库引擎库等等,这些库能被Android系统中不同的组件使用,通过 Android 应用程序框架为开发者提供服务。希望本文档会给有需要的朋友带来帮助

下载

我们这样理解吧,对于国家来说,分配资源的实体是一个一个的家庭,家庭中的许多成员就是一个一个的线程。

那么windows对于线程是提供了真实线程控制块,对于Linux来说,是直接复用的内核代码,不然单独创建线程控制块,增加管理成本,源码还要多写很多很多行。

所以Linux中的线程实际上是集成在进程里面的。所以之前理解的进程实际上是只有一个线程的进程,我们之后要学习的就是一个进程含有多个线程。

那么对于cpu来说,是否要区分什么是线程,什么是进程呢?因为task_strcut理解存放的是进程的信息,但是实际上进程执行任务的时候使用的是线程。

cpu是不用区分的,因为cpu看到的执行流

那么既然有了多进程,为什么还要多线程呢?

因为cpu内部调度的时候,时间片一到,进程切换需要存上下文吧?地址空间,页表全部都要切换吧?那么这个成本是不是十分高了就?如果使用的是多线程,线程之间共享的是地址空间,页表,切换的时候成本就很低了。所以这是多线程的优势。

但是就和家庭一样,一个线程如果奔溃了,其他线程也都是会崩溃的。


线程杂谈

说了那么多,我们总的看看吧?

初识Linux · 线程概念

使用函数我们可以创建线程,其实第一个参数不解释了,第二个参数我们设置为nullptr即可,对于第三个参数就是函数指针,信号那里我们也见过,第四个参数是线程的名字。

我们直接来一份代码:

代码语言:javascript代码运行次数:0运行复制
int gval = 100;void *threadStart(void *args){    while (true)    {        sleep(1);        std::cout << "new thread running..." << ", pid: " << getpid() << std::endl;        // std::cout << "new thread running..." << ", pid: " << getpid()        //           << ", gval: " << gval << ", &gval: " << &gval << std::endl;    }}int main(){    srand(time(nullptr));    pthread_t tid1;    pthread_create(&tid1, nullptr, threadStart, (void *)"thread-new");    // pthread_t tid2;    // pthread_create(&tid2, nullptr, threadStart, (void *)"thread-new");    // pthread_t tid3;    // pthread_create(&tid3, nullptr, threadStart, (void *)"thread-new");        // 主线程    while (true)    {                std::cout << "main thread running..." << ", pid: " << getpid() << std::endl;        // gval++;        sleep(1);    }    return 0;}

按照平常,我们直接g++是编译不过去的,因为它需要链接库,没想到到,重制版介绍为什么。

所以需要在Makefile文件里面加-lpthread

按照常理来说,两个死循环是不会同时打印的,可是一个进程使用两个线程调用不同的任务,就可以同时打印了:

初识Linux · 线程概念

像这样。

那么我们定义一个全局变量,看看g_val的变化:

初识Linux · 线程概念

发现线程是共享数据的,也就是地址空间都是同一份,这个我们也成功验证了。

初识Linux · 线程概念

当我们输入ps -aL指令,我们能查看线程的部分信息,可以发现和进程Pid相等的线程实际上是主线程,其他的是非主线程了,那么提问了,对于cpu来说,调度的时候看的是pid还是lwp呢?

当然是lwp了,毕竟是线程来执行的任务。

这里提及一个非常重要的点:

线程虽然共享了许多资源,但是线程私有的部分有一组寄存器,栈。前者要存储硬件的上下文,后者是线程运行的时候会形成各种临时变量,肯定都会被自己的线程保存在自己的栈区里。

本文不过是对线程的非常粗略的概念理解,请各位佬不要介意没有解释清楚大部分知识,重制版一定非常清晰的介绍清楚了。


感谢阅读!

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

390

2023.06.20

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

614

2023.07.25

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

353

2023.08.02

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

256

2023.08.09

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

597

2023.09.05

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

524

2023.09.20

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

640

2023.09.20

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

599

2023.09.22

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

43

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 7.3万人学习

Git 教程
Git 教程

共21课时 | 2.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号