Linux—进程学习—04(进程地址空间学习)

目录

  • Linux—进程学习—4
    • 1.程序地址空间
      • 1.1虚拟地址空间的现象
      • 1.2虚拟地址空间的理解(感性)
    • 2.进程地址空间
      • 2.0 mm_struct结构体
      • 2.1 mm_struct结构体的源代码
      • 2.2分页&虚拟地址空间
        • 解释前面的实验现象
      • 2.3进程地址空间存在的原因
        • 2.3.1第一个原因
        • 2.3.2第二个原因
        • 2.3.3第三个原因(小难)
      • 2.4关于虚拟地址、线性地址、物理地址、逻辑地址
    • 3.总结
    • 4.内核进程调度队列(拓展)

Linux—进程学习—4

学习了环境变量和进程的一些基础概念之后,现在要来学习一个难点,就是进程地址空间

1.程序地址空间

之前在学习c/c++的时候,有提到一个c/c++程序的内存分布这样一个知识,忘记了可以看这个C&C++内存管理复习

当时提到了数据在内存中的分布情况,也就是数据可能会在栈区,堆区,代码段,数据段。

但是当时这只是一个便于理解代码的浅学习,这个叫做程序地址空间,现在来看看到底是怎么一回事。

下图是一个程序地址空间的一个图:

image-20241124214951774

其实之前对其程序地址空间的理解是不到位的,比如我之前认为这个程序地址空间是内存,其实并不是。这个东西是虚拟地址空间!

怎么理解呢?可以通过下面一个实验来理解虚拟地址空间的存在

1.1虚拟地址空间的现象

一个有趣的小tip:

如果想在vim中批量化替换一个名字,可以使用%s/将被替换的/替换的/g

如下图所示:

image-20241124234054995

可以发现实现批量化替换了,将mycode批量化替换成mytest【这里的c99模式编译记得取消掉,不然无法编译后面的程序】

下面做个实验来感受一下虚拟地址空间。

代码如下:

image-20241125003154619

执行结果如下:

image-20241125004332847

注意:父进程和子进程谁先执行是不知道的,完全由系统的调度器决定

我们分析程序的执行结果会发现:

有个现象很奇怪:父子进程读取同一个地址的变量,出现了不一样的值!

在子进程修改了VAL的值后,明明父子进程的VAL地址指向的还是同一个地址,但是VAL的值却不一样,这不是很奇怪吗?和以前学习的知识不一样。

值不一样我们还能理解,之前说过父子进程之间应该是有独立性的,值不一样可以理解,但是VAL的地址确实一样的,这就很奇怪了。

因此这里的地址——绝对不是物理地址!

即是之前在c/c++所指的地址(指针),并不是物理地址!而是虚拟地址(线性地址)【在Linux中也会叫逻辑地址】

总结:

  • 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量
  • 地址值是一样的,说明,该地址绝对不是物理地址!
  • 在Linux地址下,这种地址叫做 虚拟地址
  • 我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理

现在已经知道了虚拟地址空间的存在了,也感受了它的存在,看到了现象,现在需要的是理解它,要理解它就得知道它是怎么来的

1.2虚拟地址空间的理解(感性)

进程会认为它自己是独占了系统的资源,但是实际上并不是。

下面是一个小故事来便于理解:

一个富豪有三个私生子,而私生子是不知道其他私生子的存在的。这三个私生子各自都有各自的生活。这三个私生子一个是工厂老板,一个是老师,一个是还在读博。

这个富豪答应3个私生子,死后的家产就属于它们。

而三个私生子自然会认为富豪的家产已经属于了自己,但是每次向富豪要钱的时候,只能向富豪要一些小钱来完成自己的工作。如果要家产的话,富豪就会拒绝。

在这个故事中:

  • 富豪就是操作系统,在它看来,家产是不可能只给一个私生子的,家产只是给私生子的一个大饼。
  • 私生子就是进程,在它看来,它认为富豪的家产已经被自己独占了,它可以通过向富豪索取家产来完成自己的工作
  • 而家产就是虚拟地址空间,富豪给私生子画的大饼,而虚拟地址空间,就是操作系统给进程画的大饼

其实严格来说,这个虚拟地址空间(家产),这个大饼,叫做进程地址空间!

站在进程的角度上,它是认为它独占了所有的系统资源(进程地址空间)的,

站在操作系统角度上,进程地址空间是操作系统给每个进程”画的一个大饼“

一个进程实际上占用了操作系统的部分资源,进程也可以向操作系统申请一定的资源,但是进程如果要全部的资源(地址空间),操作系统只会拒绝

那这个饼(进程地址空间)是怎么画的呢?

  • 画饼对于人来说:就是在人的脑海中构建一个蓝图,让人记住

  • 画饼对于计算机来说:一个数据结构对象,让每个进程都指向一个数据结构的对象。

image-20241125191742419

每个进程都需要给其画饼(地址空间),因此每个进程都指向属于自己的一个饼(地址空间)、而每个进程都需要被管理(PCB)。

自然,给每个进程的饼(地址空间)也需要被管理,这个饼(地址空间)也是结构体对象,也得通过先描述后组织的方式管理。

地址空间的本质: 内核的一种数据结构【在Linux内核中是 mm_struct

2.进程地址空间

之前叫做程序地址空间,是不太准确的,严格来说实际上应该叫做进程地址空间。

image-20241126120041151

由于这个进程地址空间,是完成的连续的地址,很多教材和官方都管它不叫虚拟地址,管它叫做线性地址

2.0 mm_struct结构体

PCB里面有一个struct mm_struct* mm指针,这个mm指向的这个结构体划分了进程地址空间的区域分配情况

那这个mm_struct结构体内部会有什么成员呢?

image-20241125235656000

我们现在已经知道了,对于地址空间,操作系统也是需要管理的

在地址空间上是有各种区域的【堆,栈,等区域】,这个不同的区域要怎么理解呢?

其实就是区域划分,在32位的环境下,从低地址到高地址一共有2^32次方个地址,每个地址1字节。而区域的划分就是在这个地址上划出一个个范围,规定一个个范围分别属于谁的。

而地址一共有2^32次方个,在mm_struct中是用unsigned long存储的

因此,mm_struct结构体 肯定会有很多跟区域划分有关的成员

image-20241126010329090

而一个mm_struct结构体对象mm,就会开始具体的区域划分。

image-20241126011002383

每个区域的起始地址和结束地址当中就会有很多个地址,每个地址都代表着一个位置,比如代码段区域有N个地址,4个地址(字节)存在一个int类型的对象。每个区域中间的这N个地址就可以拿来给用户存放数据。

而这每个区域中的地址,就叫做虚拟地址!也就是这2^32个地址都是虚拟地址!

之前说了,操作系统会给每个进程画大饼,这个大饼就是mm_struct结构体对象(进程地址空间),每个进程都会指向一个mm_struct结构体对象,这个指针存在于进程的PCB中。这意味着每个进程都可以拥有2^32个地址空间吗?不是的,只是一个大饼罢了。

既然有了区域划分,就会有区域调整,比如一个区域的范围要扩大,就叫区域扩大,区域要缩小,就叫区域缩小。

我们知道,栈区和堆区这两个区域的范围不是固定的,有些时候会扩大和缩小。这就涉及到了区域调整。而区域调整的本质:就是调整mm_struct结构体对应区域的start和end

比如之前在C/C++中,使用new/malloc申请空间 ,其实就是在扩大堆区,而定义很多局部变量,就是在扩大栈区。当申请的空间被free/delete掉之后,扩大的堆区就会缩小回原来的大小,当栈帧销毁的时候,因局部变量扩大的栈区也会缩小回原来的大小

2.1 mm_struct结构体的源代码

下图是Linux内核中,mm_struct结构体的源代码:

image-20241126105937890

image-20241126110122821

image-20241126110147257

这个有很多看不懂很正常,只需要看下面这个就行

image-20241126110714166

除了对应的代码段,栈、堆等区域的划分,甚至连命令行参数都有划分区域

2.2分页&虚拟地址空间

现在已经知道了,每个进程的PCB中都有一个指针指向进程地址空间(mm_struct结构体对象),每个进程都拥有了自己的进程地址空间了,但是这个是虚拟的地址空间。

此时有个问题,那进程要如何找到加载到物理内存中的代码呢?

image-20241126120742991

此时就需要一个新的东西——页表

image-20241126121302576

注意:(下面这个只是了解,不详细学习,等到后面学文件系统的时候会详细学习)

内存在和磁盘这种外设做数据的输入输出的时候,叫做IO。而IO一般来说一次是4KB,也就是4096个字节。而每一次加载到内存,一般都直接加载成一个page单位,一个page大小为4KB,占据内存4KB的空间,其实可以认为内存会把自己看做一个个4KB大小的page。【在32位环境下,一共会有4GB/4KB个page】

继续回到页表,虚拟地址空间是通过页表来实现与物理内存的联系的。如何联系呢?——通过映射,看下图:

image-20241126122627502

可以举个实例:

我们在C语言中写了一个代码 int a = 10,如果此时取地址,前面也实验过,取的其实是虚拟地址上的地址,但是如果是修改其值,就会通过页表来找到虚拟地址所对应的物理地址,也就是真正存储a的物理地址,然后访问这个地址并修改所存储的值

注意:这里页表看似很简单,实际上页表是一个复杂的东西,它是一个类似于map的树状数据结构。涉及到多级页表【并且页表的功能也不仅仅只有映射】

解释前面的实验现象

知道了进程地址空间和物理内存是如何联系起来的之后,就可以解释上面实验的现象了**【父子进程取同样的地址,但是值不一样】**

由于子进程会直接按照父进程为模版,进行拷贝【这里子进程的PCB和进程地址空间拷贝的都是父进程的】,因此父子进程的关于VAL变量的虚拟地址是相同的。并且通过页表的映射关系找到的存储VAL的物理地址也是一个地址

但是在子进程运行过程中,修改了VAL这个共享变量,如果两个页表的映射仍然指向一个地址,那么父子进程的独立性就会被破坏、

因此Linux为了保持父子进程的独立性。在VAL这个共享变量被任何一方修改的时候,都会单独在内存中找一个一样大的地方,拷贝VAL到新地址,并且修改对应进程的页表的映射关系。如果是子进程修改那就修改子进程的页表映射关系,父进程就修改父进程的**【这种操作叫做写时拷贝】**

这样处理的话,尽管在进程地址空间上,父子进程的VAL的地址是一样的。但是通过各自的页表所找到的物理地址是不同的,不同的物理地址存放着父子进程各自的VAL变量

下图是关于这个现象的解释:

image-20241125231937980

这上面的所有操作和功能,都是OS帮我们做的、

2.3进程地址空间存在的原因

2.3.1第一个原因
  • 首先就是直接访问物理内存这种方式,对于在内存的数据来说非常不安全
  1. 自己写的程序,如果越界访问了,会直接修改到其他地址的数据
  2. 如果有恶意进程可以扫描物理内存,那就可以恶意修改数据或者获得关键数据并上传。

但是只能说直接访问物理内存不行,并没有说进程地址空间就行。

  • 访问进程地址空间配上页表就安全非常多

访问进程地址空间,由于是虚拟的,最终数据的存取仍然要通过访问物理内存实现,但是页表可以判断本次操作是否合法。如果合法就通过页表来映射物理内存,从而访问物理内存。如果不合法那就拒绝访问物理内存。

因此哪怕进程的执行内容会有野指针和越界以及恶意程序的情况,影响的也只是这个进程对应的进程地址空间,不会影响到物理内存。

2.3.2第二个原因

进程地址空间的存在——可以更方便的进行不同进程之间数据和代码的解耦,保证了进程的独立性。

具体的案例分析在上面解释实验现象【父子进程取同样的地址,但是值不一样】

2.3.3第三个原因(小难)
  • 让进程以统一的视角来看待进程对应的代码和数据等各个区域(也就是每个进程都有代码区和数据区等区域),方便使用【可以做到10个进程的main函数地址都是一样的】
  • 让编译器以统一的视角来编译代码【编译完即可直接使用】

这两个原因其实有点难理解,可以看下面的分析来理解、

首先要知道一个点——不要认为虚拟地址空间只有在OS中才会用到,编译器也会遵守对应的规则

我们自己写的程序,在加载到物理内存之前,在编译的时候,每句代码和数据就已经存在地址了.【程序需要经过编译和链接才能生成可执行文件!】

什么意思呢?可以看下面这个图:

这个编址是编译器在编译的时候编址的,可以说是按照虚拟地址空间一样的方式去编址的,一样会有代码段,数据段等区域的划分。【栈区和堆区在编译的时候是不会开辟出这个区域的(这两个区域是程序执行之后动态生成的)】

在磁盘中这个地址说是虚拟地址不太准确,严格来说应该是逻辑地址。但是在Linux中是一回事

然后呢,编译完成之后,就要链接并加载到物理内存。

image-20241127153519624

这个时候需要注意,虽然代码其本身自带逻辑地址,在加载到内存之后,天然的就会被赋予物理地址

image-20241127154149921

此时有两套地址

  1. 物理地址:标识代码和数据在物理内存的位置
  2. 代码的逻辑地址:标识程序内部代码和数据的地址【比如函数跳转时,用的地址就是逻辑地址】

由于此时加载到物理地址,因此OS就可以拿到代码和数据在物理内存的位置,此时就会将这个位置记录下来并记录在页表中

image-20241127154448525

那如果此时有一个进程,要执行这个程序,这个进程该如何找到程序的位置呢?

之前说了每个进程都有其对应的mm_struct进程地址空间,因此这个进程地址空间在初始化的时候,在划分区域的时候,就会拿这个加载到内存的可执行程序的逻辑地址来加载进程地址空间中的虚拟地址。

image-20241127160046648

通过这样的方式,页表就被OS建立好了。进程地址空间可以通过页表找到代码的物理地址。

下面CPU调度该进程并执行代码的过程:

此时如果CPU在调度该进程,并且到了要执行该程序的时候,就会将进场地址空间中的code_start等区域的起始地址交给CPU,然后CPU就会根据该虚拟地址,去找到对应的main函数【OS会将页表的映射关系处理,找到虚拟地址对应的物理地址】,找到之后,开始执行代码。如果中间碰到函数跳转,进程地址空间有对应的虚拟地址,页表也记载了对应的物理地址,CPU可以继续根据虚拟地址来完成代码的执行

要注意:CPU只能接收指令!通过虚拟地址然后经过页表的映射在物理地址找到代码指令,然后执行指令。而这个指令就自带地址,这个地址是虚拟地址!【因为加载到内存了,如果在磁盘上就是逻辑地址】

而**CPU在执行完整个进程的这段时间,是见不到物理地址的!它一直都在用虚拟地址!**中间通过进程地址空间的虚拟地址并通过页表找到物理地址的过程,是OS完成的,CPU并不知道。

在VS2022中为什么说程序要在32位或者64位环境下编译?因为就跟编译器要在编译程序的时候为程序以什么环境编址有关,也就是逻辑地址。

2.4关于虚拟地址、线性地址、物理地址、逻辑地址

由于这个进程地址空间,是完成的连续的地址,很多教材和官方都管它不叫虚拟地址,管它叫做线性地址

在Linux中我们认为虚拟地址和线性地址是一样的

而虚拟地址和物理地址之间的联系是通过页表完成映射关系的

逻辑地址:前面在第三个原因处提到的逻辑地址,这只是逻辑地址的一种形成方案,它多种形成方式

下面是两种典型的逻辑地址的形成方案:

image-20241128125438346

  • 新的是直接按照线性去编址,code区域假如是0~100,那么data区直接从101开始编址,这样的好处是,形成的逻辑地址和进程地址空间的虚拟地址比较相似,加载到内存可以直接当做虚拟内存看待。

上面在第三个原因所讲述的逻辑地址,就是按照新的方式去编址的【它恰好和虚拟地址非常像】

  • 旧的是按照偏移量的方式去编址的,code区域假如是0~100,那么data区域是从起始地址+偏移量100,然后再从0开始编址。这样的坏处是,形成的逻辑地址加载到物理内存后,仍然需要在物理地址去加上偏移量才能得到代码的虚拟地址。

3.总结

经过上述的理解和学习,现在需要知道3个问题的答案

  • 进程地址空间是什么?

进程地址空间是OS为了管理进程的一种虚拟化解决方案,实际上进程地址空间是为进程所画的"大饼",让进程认为自己独占了系统资源(地址空间)、

  • 进程地址空间为什么存在?

仔细说就看上面讲述的三个原因,这里大概总结一下

三个原因:

  1. 防止进程中存在越界访问和野指针等非法操作,保证物理内存的其他数据的安全性。

  2. 保证进程与进程之间数据和代码的解耦,保证了进程与进程之间的独立性

    1. 让进程以统一的视角来看待进程对应的代码和数据等各个区域(也就是每个进程都有代码区和数据区等区域),方便使用

    2. 让编译器以统一的视角来编译代码【编译完的逻辑地址即可直接使用】

  • 进程地址空间如何实现的?

为了管理进程需要有PCB,进场地址空间自然也需要管理,要遵守先描述后组织,因此进程地址空间就是内核的一种数据结构结构体mm_struct,每个进程PCB中都有一个mm指针指向一个mm_struct结构体对象,结构体对象会实现地址空间的区域划分。如果需要到物理内存寻址,OS会通过页表来实现进程地址空间与物理内存之间的联系

现在回过来看这个图,理解就多了一些。

但是需要注意的是,32位环境下为进程准备的进程地址空间,并不是全部空间都是给用户准备的,这里有1/4的空间是给内核准备的,这个内核空间后面会讲到

image-20241124214951774

4.内核进程调度队列(拓展)

下图是Linux2.6内核中进程队列的数据结构

image-20241128134548647

一个CPU拥有一个runqueue(运行队列)

如果有多个CPU就要考虑进程个数的负载均衡问题

优先级普通优先级:100~139(我们都是普通的优先级,想想nice值的取值范围,可与之对应!)

实时优先级:0~99(不关心)

关于优先级的详细解答:Linux的进程优先级 NI 和 PR - 简书

活动队列

  • 时间片还没有结束的所有进程都按照优先级放在该队列

  • nr_active: 总共有多少个运行状态的进程

  • queue[140]: 一个元素就是一个进程队列,相同优先级的进程按照FIFO规则进行排队调度,所以,数组下标就是优先级!

从该结构中,选择一个最合适的进程,过程是怎么的呢?

  1. 从0下表开始遍历queue[140]

  2. 找到第一个非空队列,该队列必定为优先级最高的队列

  3. 拿到选中队列的第一个进程,开始运行,调度完成!

  4. 遍历queue[140]时间复杂度是常数!但还是太低效了!

  • bitmap[5]:一共140个优先级,一共140个进程队列,为了提高查找非空队列的效率,就可以用5*32个比特位表示队列是否为空,这样,便可以大大提高查找效率!

过期队列

  • 过期队列和活动队列结构一模一样

  • 过期队列上放置的进程,都是时间片耗尽的进程

  • 当活动队列上的进程都被处理完毕之后,对过期队列的进程进行时间片重新计算

active指针和expired指针

  • active指针永远指向活动队列

  • expired指针永远指向过期队列

  • 可是活动队列上的进程会越来越少,过期队列上的进程会越来越多,因为进程时间片到期时一直都存在的。没关系,在合适的时候,只要能够交换active指针和expired指针的内容,就相当于有具有了一批新的活动进程

在系统当中查找一个最合适调度的进程的时间复杂度是一个常数,不随着进程增多而导致时间成本增加,称之为进程调度O(1)算法!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/481117.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

信息安全实验--密码学实验工具:CrypTool

1. CrypTool介绍💭 CrypTool 1的开源教育工具,用于密码学研究。通过CrypTool 1,可以实现加密和解密操作,数字签名。CrypTool1和2有很多区别的。 2. CrpyTool下载🔧 在做信息安全实验--密码学相关实验时,发…

nodejs30: CSS 剪辑路径clip-path导致伪元素不可见问题及解决方法

相关问题 应用圆角裁剪时无法显示::after 取消clip-path设置&#xff1a; 完整问题代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, i…

三、计算机视觉_08YOLO目标检测

0、前言 YOLO作为目前CV领域的扛把子&#xff0c;分类、检测等任务样样精通&#xff0c;本文将基于两个小案例&#xff0c;用YOLO做检测任务&#xff0c;看看效果如何 1、对图片内容做检测 假设我有一张名为picture.jpeg的图片&#xff0c;其内容如下 我将图片和代码放到了同…

STM32 ADC --- 知识点总结

STM32 ADC — 知识点总结 文章目录 STM32 ADC --- 知识点总结cubeMX中配置注解单次转换模式、连续转换模式、扫描模式单通道采样的情况单次转换模式&#xff1a;连续转换模式&#xff1a; 多通道采样的情况禁止扫描模式&#xff08;单次转换模式或连续转换模式&#xff09;单次…

SQL Server 实战 - 多种连接

目录 背景 一、多种连接 1. 复合连接条件 2. 跨数据库连接 3. 隐连接 4. 自连接 5. 多表外连接 6. UNION ALL 二、一个对比例子 背景 本专栏文章以 SAP 实施顾问在实施项目中需要掌握的 sql 语句为偏向进行选题&#xff1a; 用例&#xff1a;SAP B1 的数据库工具&am…

Nginx:ssl

目录 部署ssl前提 nginx部署ssl证书 部署ssl部署建议 部署ssl前提 网站有域名根据域名申请到ssl证书&#xff0c;并下载证书部署到nginx中 部署了ssl证书后&#xff0c;访问的流量是加密的。 nginx部署ssl证书 #80端口跳转到443 server {listen 80;return 302 https://1…

MySQL之单行函数

目录 1. 函数的理解 单行函数 2. 数值函数 2.1 基本函数 2.2 角度与弧度互换函数 2.3 三角函数 2.4 指数与对数 2.5 进制间的转换 3. 字符串函数 4. 日期和时间函数 4.1 获取日期、时间 4.2 日期与时间戳的转换​编辑 4.3 获取月份、星期、星期数、天数等函数 4.4 …

Next.js-样式处理

#题引&#xff1a;我认为跟着官方文档学习不会走歪路 Next.js 支持多种为应用程序添加样式的方法&#xff0c;包括&#xff1a; CSS Modules&#xff1a;创建局部作用域的 CSS 类&#xff0c;避免命名冲突并提高可维护性。全局 CSS&#xff1a;使用简单&#xff0c;对于有传统…

Leetcode 每日一题 104.二叉树的最大深度

目录 问题描述 示例 示例 1&#xff1a; 示例 2&#xff1a; 约束条件 题解 方法一&#xff1a;广度优先搜索&#xff08;BFS&#xff09; 步骤 代码实现 方法二&#xff1a;递归 步骤 代码实现 结论 问题描述 给定一个二叉树 root&#xff0c;我们需要返回其最大…

SQL基础入门——SQL基础语法

1. 数据库、表、列的创建与管理 在SQL中&#xff0c;数据库是一个数据的集合&#xff0c;包含了多个表、视图、索引、存储过程等对象。每个表由若干列&#xff08;字段&#xff09;组成&#xff0c;表中的数据行代表记录。管理数据库和表的结构是SQL的基础操作。 1.1 创建数据…

IP与“谷子”齐飞,阅文“乘势而上”?

爆火的“谷子经济”&#xff0c;又捧出一只“潜力股”。 近日&#xff0c;阅文集团股价持续上涨&#xff0c;5日累计涨幅达13.20%。这其中&#xff0c;周三股价一度大涨约15%至29.15港元&#xff0c;强势突破20日、30日、120日等多根均线&#xff0c;市值突破280亿港元关口。 …

EXCEL截取某一列从第一个字符开始到特定字符结束的字符串到新的一列

使用EXCEL中的公式进行特定截取 假设列A是一组产品的编码&#xff0c;我们需要的数据是“-”之前的字段。 我们需要在B1单元格输入公式“LEFT(A1,SEARCH("-",A1)-1)”然后选中B1至B4单元格&#xff0c;按“CTRLD”向下填充&#xff0c;就可以得出其它几行“-”之前的…

重塑视频新语言,让每一帧都焕发新生——Video-Retalking,开启数字人沉浸式交流新纪元!

模型简介 Video-Retalking 模型是一种基于深度学习的视频再谈话技术&#xff0c;它通过分析视频中的音频和图像信息&#xff0c;实现视频角色口型、表情乃至肢体动作的精准控制与合成。这一技术的实现依赖于强大的技术架构和核心算法&#xff0c;特别是生成对抗网络&#xff0…

多头注意力机制:从原理到应用的全面解析

目录 什么是多头注意力机制&#xff1f; 原理解析 1. 注意力机制的核心公式 2. 多头注意力的扩展 为什么使用多头注意力&#xff1f; 实际应用 1. Transformer中的应用 2. NLP任务 3. 计算机视觉任务 PyTorch 实现示例 总结 近年来&#xff0c;“多头注意力机制&…

力扣637. 二叉树的层平均值

给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。 提示&#xff1a; 树中节点数量在 [1, 104] 范围内-231 < Node.val < 231 - 1 代码&#xff1a; /*** Definition for a binary tree node.* stru…

Opencv+ROS实现摄像头读取处理画面信息

一、工具 ubuntu18.04 ROSopencv2 编译器&#xff1a;Visual Studio Code 二、原理 图像信息 ROS数据形式&#xff1a;sensor_msgs::Image OpenCV数据形式&#xff1a;cv:Mat 通过cv_bridge()函数进行ROS向opencv转换 cv_bridge是在ROS图像消息和OpenCV图像之间进行转…

Perforce SAST专家详解:自动驾驶汽车的安全与技术挑战,Klocwork、Helix QAC等静态代码分析成必备合规性工具

自动驾驶汽车安全吗&#xff1f;现代汽车的软件包含1亿多行代码&#xff0c;支持许多不同的功能&#xff0c;如巡航控制、速度辅助和泊车摄像头。而且&#xff0c;这些嵌入式系统中的代码只会越来越复杂。 随着未来汽车的互联程度越来越高&#xff0c;这一趋势还将继续。汽车越…

架构-微服务-服务配置

文章目录 前言一、配置中心介绍1. 什么是配置中心2. 解决方案 二、Nacos Config入门三、Nacos Config深入1. 配置动态刷新2. 配置共享 四、nacos服务配置的核心概念 前言 服务配置--Nacos Config‌ 微服务架构下关于配置文件的一些问题&#xff1a; 配置文件相对分散。在一个…

攻防世界GFSJ1193 cat_theory

题目编号&#xff1a;GFSJ1193 附件下载后是一个jpg文件和一个sage文件&#xff08;python&#xff09;&#xff1a; 1. 分析图片&#xff08;.jpg文件&#xff09; 这个交换图展示的是一个加密系统的 同态加密 性质&#xff0c;其核心思想是&#xff1a;加密前的操作与加密后…

qt QGraphicsPolygonItem详解

1、概述 QGraphicsPolygonItem是Qt框架中QGraphicsItem的一个子类&#xff0c;它提供了一个可以添加到QGraphicsScene中的多边形项。通过QGraphicsPolygonItem&#xff0c;你可以定义和显示一个多边形&#xff0c;包括其填充颜色、边框样式等属性。QGraphicsPolygonItem支持各…