你真的了解线程吗?

1.概述

课本上的概念是:

比进程更加轻量化的一种执行流/在进程内部执行的一种执行流

for us:

线程是CPU调度的基本单位/进程是承担系统资源的基本实体

进程的地址空间:
在这里插入图片描述线程的地址空间:
在这里插入图片描述

这两个或n 个task_struct
在内存上能共享就就共享,
不能共享就得到自己
该有的一份(将原来的分割)
然后通过页表找到对应的实际物理内存

参与资源的分配
得到的结论:

1.线程创建比进程的创建更简单,创建简单意味着销毁也简单
2.线程在进程的地址空间中运行

linux中的线程机制

OS中存在多个线程,会对多个线程进行管理(创建,暂停,挂起等等情况)
先描述再组织,TCB(线程控制块),T:thread C:control B:block
未来的线程会作为一个链表的形式存放进进程地址块中
对线程的管理就相当于是进程的管理,所有的关于线程的管理操作复用进程的管理形式就好了

windows下创建进程和创建线程都有直接的接口,管理TCB,管理PCB都有自己的对应的方法,
而linux下并不是这样
linux下创建的线程是根据PCB的进行复用,但并不是像创建PCB一样建立地址空间等,
而是直接在PCB内部进行管理

本质

进程来模拟创建线程,但是线程的存在,OS会区分进程和线程吗?
OS只要执行那个对应的代码就可以了,不会管这是进程还是线程

换做以前CPU拿到一个PCB <= 进程, 拿到的是一个进程的执行流,这个进程的执行流是包括当前进程和其他线程
执行流 <= 进程 -->cpu执行一个执行流,这个执行流可能是一个线程,此时是<,可能是一个进程,此时是=.
如何看待以前的进程:
只有一个执行流的进程
在这里插入图片描述

如何看到现在的线程:
内部多个执行流的进程

在这里插入图片描述

什么叫做进程:
task_struct,虚拟内存地址空间,页表,代码和数据这个整个部分都是进程.进程=内核数据结构+代码和数据
(创建)

即:

所谓进程是承担系统资源的基本实体
linux中并不存在真正的线程,他是用进程模拟的线程.
这样的线程也叫做轻量级进程
即:这个进程的执行流 <= 进程

对于一个进程,里面有多个线程,多个线程之间进行协作,共同完成一件事情

2.理解–原理—看待线程/看待进程------linux上的线程 vs OS的线程

创建线程:
pthread_create的使用
在这里插入图片描述
在这里插入图片描述这个pthread_t 本质就是unsigned long int

知识补充static_cast<>使用

思考:
为什么不允许直接转为string* 类型?
但实际上是支持这样的写法,只是推荐一种更安全的写法X* 变量= static_cast(原变量)
void* a = new string(“haha”); // 动态分配一个 string 对象
string* s = static_cast<string*>(a); // 现在 static_cast 是安全的

这样的写法更安全 (演示写法)
static_cast 主要用于在相关类型之间进行转换,例如基本类型的转换(如 int 到 float)或具有继承关系的类指针之间的转换(如从基类指针到派生类指针)

在这里插入图片描述会报错这个报错需要在编译的时候加一个编译选项:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述能同时跑两个循环,且只有一个进程
在这里插入图片描述

打印pid查看:
在这里插入图片描述

查看线程:
ps -aL 解释:-a—>all L(light)轻量级

在这里插入图片描述
LWP:
(Light Weight Process)
OS在调度的时候,是根据LWP进行调度的,LWP与PID相等的那个线程是主线程

多创建几个,查看结果:

在这里插入图片描述
在这里插入图片描述

每一个线程的创建都会执行一个对应的线程方法,这些线程方法可以相同也可以不同
当多个线程执行同一个方法时,这个函数就一直在被重入

线程在进程内部资源是共享的
在这里插入图片描述

在这里插入图片描述

让两个执行流看到同一份资源不再像进程那样,线程执行流资源共享更简单,更方便
线程能并发执行代码

线程比进程更加轻量化:

进程的创建需要建立虚拟地址空间,建立页表映射等操作
线程创建则不需要,直接创建一个PCB对象,指向这个进程指向的虚拟地址空间即可

在切换方面:

线程间切换,地址空间页表是不用替换的,更不用像进程那样替换很多的寄存器,只是替换几个局部的寄存器即可
进程间切换,是需要切换所有的寄存器,还要上下文切换等
查看CPU信息
cat /proc/cpuinfo

在这里插入图片描述

局部性原理的介绍

计算机中存在局部性原理:

即对于一个可执行程序,CPU可能正在访问第10行,那么未来CPU有很大可能访问第 11 12 13行,为什么是 可能?因为会存在函数跳转,进程替换等情况
基于这样一种情况,CPU会将代码读到cache(硬件级别的缓存)缓存中,这样在未来的访问直接从缓存进行访问,速度就能快很多
这一设计就是利用局部性原理

存在在cache里面的数据叫做热数据
线程间的切换不需要切换cache,因为cache中存的本来就是这个虚拟地址空间的数据
进程间切换,cache会失效重新加载,因为两个进程之间的数据没有关系
这也是线程间切换比进程间切换的效率高的主要原因(寄存器少,cache不需要重新更新)

进程的创建需要时间片,时间片会被一个进程中的各个线程进行划分

1.一个任务的进行从串行进行变成了并行执行,线程创建也更轻量化
2.在linux中,线程是在进程的地址空间中进行
3.进程=内核数据结构+代码和数据,他是承担系统资源分配的基本实体
4.线程可以认为是进程的一个子集
5.linux 的线程创建是进程模拟的

参考下面两张图内的1—>7顺序更好的理解线程
在这里插入图片描述
在这里插入图片描述

3.再谈地址空间----虚拟–>物理地址的过程

以前页表的认识是不完全正确的,
为什么?
以4GB内存为例,虚拟内存的大小为2的32次方字节
页表中的内容有:虚拟内存地址(4字节)+物理内存地址(4字节)+权限数字等其他(按2字节算)
那么这个页表空间就比内存还大
所以实际虚拟到物理的转化是(大概描述):

一个4GB的物理内存,有4 * 1024 * 1024个kb大小的单位
按4kb分为n个页框
OS对这些页框的管理也会成为对内核数据结构的管理

struct page
{
	int flag;//page的使用情况
	//page的属性
}

根据物理内存的大小(以4GB为例)维护一个数组来进行管理
struct page pages[1048576];

那么缓冲区的实际指向是指向这个数组

在这里插入图片描述
在这里插入图片描述总结:

1.对于一个很大的文件,OS可能会分批将文件加载到内存
2.一个可执行程序,并不会把虚拟内存全部用完,
3.页目录一定会被加载,但是二级页表不一定,当访问的内容小就只会在前几页进行加载,
后续的页表不会创建,当新的内容加载进来,首先发生缺页中断,然后重新创建新的页表,
创建新的映射关系,这更加说明了页表的内容之大,不可能被存满或不足的情况
4.CPU当中有保存页表起始地址的寄存器,整个虚拟到物理的转化是CPU进行的,这样从CPU出来以后,就能通过总线找到物理地址
5.总结上述:给一个线程分配它对应的代码和数据,实际就是在分割页表
6.划分页表的额本质就是划分地址空间
7.类型的本质就是偏移量,语言层面的创建到了汇编就是对应寄存器的偏移量的计算
8.在进程视角,虚拟地址就是资源

在这里插入图片描述
本期就到此为止,期末耽误好多,要抓紧补了
喜欢不放来个三连~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

温有情

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值