【Linux进程篇】进程地址空间(2)

【Linux进程篇】进程地址空间(2)

目录

  • 【Linux进程篇】进程地址空间(2)
      • 进程地址空间的再次理解
        • 什么是虚拟地址?fPIC与地址无关码是什么?为什么动态库里有,静态库里没有呢?

作者:爱写代码的刚子

时间:2023.11.21

前言:本篇博客将会介绍进程地址空间的深入理解。

进程地址空间的再次理解

继上一篇的动静态库,再次理解进程地址空间。

  • 动态库在进程运行的时候,是要被加载的(静态库没有)
  • 常见的动态库被所有的可执行程序(动态链接的),都要使用动态库——共享库

所以,动态库在系统中加载之后,会被所有进程共享,如何做到?(磁盘中的动态库在使用前先加载到物理内存,通过页表映射的方式映射到虚拟内存中的共享区

结论:建立映射,从此往后,我们执行的任何代码,都是在我们的进程地址空间中进行执行。

事实:系统在运行中,一定会存在多个动态库 ——OS管理(先描述再组织),系统中所有库的加载情况,OS非常清楚

  • 所以当存在另一个进程想要使用这个动态库时,操作系统会识别这个动态库有没有被加载到内存,如果已经加载到物理内存,直接通过页表映射到这个进程的共享区。

所以动态库也称为共享库,通过地址空间加自己的页表,将地址空间对应的库映射到自己的地址空间里。(如果存在全局变量,其中一个进程修改了它,则触发写实拷贝。数据一旦被多个人共享,它所在页的引用计数就会增加)

什么是虚拟地址?fPIC与地址无关码是什么?为什么动态库里有,静态库里没有呢?

谈谈地址


  • 程序没有加载前的地址(程序)

【问题】程序编译好之后,内部有地址的概念吗?

有的!(C++虚表)

程序在没有加载进内存的时候就已经被分成了很多段。(历史)

平坦模式(现在):按0~4GB将可执行程序编好,编译的时候就已经考虑了加载的问题。编译器也要考虑操作系统!

可执行程序在被编址的时候形成了**.code**(代码区)、.rdonly(只读数据区)、.data(已初始化的全局变量的一块内存区域)、.bss(存放程序中未初始化的或者初始值为0的全局变量的一块内存区域)已经是虚拟地址了,但是在可执行程序这里,程序还没有加载到内存,我们称这种地址为逻辑地址(磁盘当中我们程序形成时所对应的地址)。

  • objdump -S 可执行文件查看反汇编代码

  • 每个指令是有长度的,知道入口地址,加上每条指令都有长度,就能在逻辑上将代码跑起来。(所以指令的地址可以不用出现)

【问题】:CPU怎么知道指令的地址?CPU读到的指令里面用的地址,是什么地址?

CPU内置了指令集(精简指令集和复杂指令集)

重要的指令都会有两套地址:

(1)内部采用的,加载到内存之前的逻辑地址

(2)加载到物理内存时所具备的物理地址


  • 程序加载后的地址(进程)

可执行程序在编译形成的时候,它已经在可执行程序的头部,写好了entry(入口地址),不是物理地址,是逻辑地址,在内存中就叫虚拟地址。

CPU内存在EIP寄存器/PC指针。 task_struct进程里面存在cwd工作目录,也能找到自己的可执行程序。(先加载内核数据结构)读取可执行程序时,先将入口地址load到EIP寄存器中 。(可能会存在缺页中断),(虚拟地址加载好后每一条指令都天然具备物理地址,虚拟到物理填入页表中,映射关系建立好(4kb级别)),由于每条指令有大小,于是可以通过偏移执行下一条指令。

CPU内读取到的指令,内部可能有数据,也可能有地址(函数调用,函数的地址(虚拟地址)),找到对应的虚拟地址,转成物理地址(通过MMU硬件),再指向物理内存中指令的地址。同理,若页表中找不到对应的物理地址,则会再次发生缺页中断。整个过程中,凡是我们读到的地址都是虚拟地址。(指令间:虚拟地址,找到指令:物理地址)

虚拟地址到物理地址转换的知识扩展

编译器编译考虑地址空间,进程创建时也考虑地址空间,两者协同重要的表现之一。


  • 动态库的地址

可执行程序在调用某一动态库的函数时,需要将动态库加载到内存并建立映射,通过页表映射到共享库。

【问题】:共享库大了,具体映射到哪里呢?这个函数是采用绝对编址吗?

动态库被加载到固定地址空间中的位置是不可能的,库可以在虚拟内存中的任意位置加载。操作系统在动态库加载到内存的时候,操作系统会记住libc.so:start(起始地址),因为操作系统要对我们的库做管理(先描述再组织),所以一个库加载到了地址空间的什么地址,操作系统也需要知道并做管理。所以之后做库函数调用的时候,一旦识别到了库函数,跳转到共享区,找到对应动态库的起始地址,通过起始地址加偏移量的方式找到该函数。所以库函数内部不采用绝对编址,只表示每个函数在库中的偏移量(lib_start+偏移量,编译代码的时候就以符号的形式进行了实例化(这一块很复杂,可深挖))

  • ldd + 可执行程序显示调用的库

【Linux进程篇】进程地址空间(2)_第1张图片


  • fPIC:产生位置无关码

gcc在编译时,直接用偏移量进行对库中函数进行编址

【问题】:静态库为什么不谈加载,不谈与位置无关?

静态库直接将代码拷贝到程序,静态库在进行编址的时候直接按照平坦模式,0~4GB,绝对编址按线性编好,所以不谈与位置无关码,静态库的函数地址是确定的。


你可能感兴趣的:(Linux,linux,java,算法)