用到动态库的程序运行过程

news2024/12/23 17:15:03

当我们写好了一段代码然后编译运行后会生成可执行文件,该文件会存在磁盘的当前目录下,而当我们开始运行这段程序时,操作系统(加载器)需要将其从磁盘加载进内存然后执行相关操作,而对于用到动态库的程序,同时也会将动态库加载进内存中。

以下的讲解以Linux操作系统为例。

进程地址空间

在此之前首先我们来认识一下进程地址空间:

首先我们要知道进程地址空间是每一个进程独有的一份虚拟地址空间,并不是物理上的地址空间,地址空间存在的意义就是更好的划分和管理进程数据, 而实际上是采用页表来与物理地址构建联系的。而当CPU处理虚拟地址中的数据时,操作系统的MMU(内存管理单元)主要负责虚拟地址和物理地址之间的转换。

 代码段

我们程序其实就是从main函数开始的一个个函数集合。而这一个个函数(不包括变量)其实本质上就是一系列的指令组合,所以函数指令其实就是存储在代码段。而这函数指令是不可修改的,也就是只读权限。

数据段

数据段包括已初始化数据段和未初始化数据段。而初始化数据段又包括全局变量和静态变量,而未初始化数据段也就是未初始化的全局变量和静态变量。其实数据段也叫做全局区和静态区。

堆区(向上增长)

堆区主要负责动态内存分配,如我们C语言的malloc函数和C++的new运算符,都是常见的用来申请堆区空间的,而堆空间要记得使用过后释放,否则会造成内存泄漏的风险,最终导致程序内存不足而卡死。

共享区

共享区属于内存映射段的一种,而内存映射段主要是用于将磁盘文件内容映射到虚拟内存中的,这其中就设计到页表和缺页中断。而我们所说的共享区是主要用来进行共享库加载进程间通信使用的。其实共享就是多个进程共享的数据,其实就是在内存中其实只有一份,但每个进程的虚拟地址空间都会映射一份。

栈区(向下增长)

栈区用于存储局部变量,函数参数以及函数调用的返回地址信息。而我们每当调用一个函数时,其所用到的这些数据都会存在栈区,而其他的函数指令就存在代码段。

命令行参数和环境变量

  • 命令行参数其实就是我们main函数原型的参数:
int main(int argc,const char* agrv[])

而我们在运行可执行程序时附带的参数其实都会当作调用main函数的参数传递过去,而argc就是参数数量,argv就是参数内容。如:ls -a -l其实就是就是-a和-l就是参数,而argv[0]就是ls,也就是程序名。

  • 环境变量存储量了用户定义的全局设置信息,如:PATH环境变量就定义了系统训中可执行文件的目录,如ls指令其实就是一个可执行程序,而我们自己写的可执行程序需要加上./才可以运行。而ls不需要的原因就是ls程序的所在路径已经设置进了PATH环境变量内部。

内核区

内核区是供给操作系统使用的区域,其中存放的是内核的代码和数据,如系统调用函数和内核全局变量等信息。而且内核区还负责管理进程的PCB。

而内核区的存在就保证了内核不受用户的干扰。也就是当我们访问内核数据时需要访问通过内核区,然后调用内核区提供的接口从而进行内核数据的访问与接收等。而且每个进程虚拟地址空间的内核区都是独立的(物理上共一份),所以进程间不会进行互相干扰。

程序运行时操作系统的工作

创建进程与初始化

  • 操作系统首先会创建一个进程,也就是会为进程分配一个唯一的进程PID,用于表示当前进程,以便操作系统能够更好地进行管理。
  • 同时操作系统内核会在内存管理中划分专门的区域为该程序创建一个进程PCB(其中存放了进程状态、程序计数器、相关数据寄存器内容、进程优先级),从而对该进程进行管理。
  • 为进程分配一个独立的虚拟地址空间,以便确定程序和数据在地址空间的布局。
  • 进行初始化创建页表,建设虚拟地址与物理地址的映射框架(后期进行数据填充)。

程序加载和内存映射

  • 操作系统加载器会将可执行程序文件从磁盘加载进内存。实际上就是通过解析磁盘可执行程序文件的ELF格式来确定程序的入口和代码段数据段的位置,按照其格式规定的布局加载程序进内存中,同时在地址空间为其分配地址范围。
  • 如果程序链接了动态库的话,系统会会先进行检查内存中是否已经存在该动态库数据信息,如果存在的话后续就直接页表映射,不存在的话就加载进内存中。
  • 进一步的完善页表的映射,将程序所在的虚拟页和实际的物理页框进行映射。我们的程序在编译链接形成可执行程序时,虽然此时还没有进行运行,也就没有加载进内存,但是其内部代码数据就已经采用逻辑地址的方式进行编址好了,也称作逻辑地址。当程序开始运行加载进内存以后,这些逻辑地址会被操作系统转换成地址空间的虚拟地址和物理内存的物理地址。所以此时页表也就构建出虚拟地址-物理地址的映射关系。但是页表并不是一次性完成所有页面的映射,对于动态分配的数据和还未访问的数据部分,其页表项并未填充。

CPU调度执行

  • 操作系统在就绪队列中根据调度算法选择一个进程进行执行,此时程序才正式开始运行。
  • 当CPU开始执行进程指令时,遇到内存访问指令的话,会先通过页表将虚拟地址转换成物理地址,进而去访问物理内存中的数据。如果是首次访问某个页面的话,则会发生缺页中断。此时操作系统会暂停处理当前运行的进程,去进行缺页中断处理,也就是访问磁盘空间,将所需页面加载进物理内存,重新在页表中构建映射关系,处理结束后就恢复进程的运行。
  • 进程运行的过程中可能会进行系统调用,也就是访问内核空间,此时会进行用户态到内核态的切换。操作系统会根据系统调用的函数进行处理。

异常与中断处理

  • 当程序运行的过程中如果出现了异常情况,如除零错误,越界访问等情况时,会产生一个异常信号,操作系统会暂停当前进程的运行,进而对信号进行相应的的处理。
  • 外部设备产生中断,如键盘摁下Ctrl c或者网络数据到来等情况,操作系统会暂停当前进程的运行,进而处理中断,也就是根据中断调用对应的中断处理程序,后续处理结束也可能会恢复中断的进程,继续从上次中断的位置开始执行(PCB中的程序计数器会保存下一次的执行指令)。

动静态库对比

  • 静态库在编译链接的过程中就直接将程序代码中所用到的静态库方法直接拷贝进可执行程序里。然后在运行的时候就将含有静态库方法的可执行程序一起加载进内存中。所以我们多个程序运行都用到了同一个静态库的话,那么内存中就会存在多份,此时对于内存空间就是一种极大的浪费。
  • 动态库则是在程序运行的过程中被加载进物理空间中,而动态库中的方法被调用时才会将动态库加载进共享区。动态库在物理内存中只会存在一份,而每个使用了动态库的进程会在个地址空间的共享区中通过页表直接和物理内存中的动态库构建映射关系。动态库的共享区从而就节省了内存空间。

首先我们知道当形成动态库时,编译和静态库一样,需要包含库文件的路径与库名称,但是在运行时,静态库可以直接进行运行,而动态库运行时需要和库文件在同一目录下或者建立软链接,或者将库文件拷贝到lib64目录下,这样在运行时就会默认去该库下寻找所需库文件,或者直接去/exc/ld.so.conf.d目录下更改动态库配置文件,也就是创建一个文件,同时将我们库的绝对路径写进文件中。因为动态库并没有将库中的数据拷贝进代码中,所以在运行时需要寻找动态库的所在位置,然后才可以调用动态库中的方法。而且动态库形成过程时,生成.o文件时需要带上-fPIC(与位置无关码)。

地址空间中的函数调用

函数调用

在程序进行编译时,会通过程序入口地址确定函数的偏移地址。在链接阶段这些相对地址会在虚拟地址空间中转换成绝对地址。后续在虚拟地址的代码段中进行函数调用时直接通过存放下一条指令地址的寄存器找到函数入口地址直接进行调用函数。

动态库函数调用

动态库由于是在程序运行时才加载进虚拟地址空间的共享区,但是动态库在虚拟地址空间的加载位置是不确定的,所以说,动态库中的函数地址也是不确定的。而想要通过虚拟地址调用对应的动态库函数就与动态库生成与位置无关码有关联。

需要用到动态库的程序被编译生成可执行程序时,其会保存所用动态库函数的一个相对偏移量。也就是所调用的动态库函数相对于动态库的起始未知的偏移量。

而当程序运行时,动态库的信息和程序用到动态库函数的偏移量关系也会加载进内存中。

而且当程序的代码段中使用到了对应动态库中的函数方法时,动态链接器才开始将动态库加载进虚拟内存的共享区中的某个位置。因此就确定好了库的起始地址,那么通过偏移量就可以将库函数映射到共享区中的具体位置(那么说也就确定了绝对地址)。那么当程序调用动态库函数时,通过动态库起始地址+偏移量的方式进行调用对应动态库函数。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ansible自动化运维(一)配置主机清单

目录 一、介绍 1.1了解自动化运维 1.2 ansible简介 1.3 ansible自动化运维的优势 1.4 ansible架构图 二、部署ansible 2.1 基本参数 2.2 Ansible帮助命令 2.3 配置主机清单 2.3.1 查看ansible的所有配置文件 2.3.2 /etc/ansible/ansible.cfg常用配置选项 2.3.3 ssh密…

高效集成:将聚水潭数据导入MySQL的实战案例

聚水潭数据集成到MySQL:店铺信息查询案例分享 在数据驱动的业务环境中,如何高效、准确地实现跨平台的数据集成是每个企业面临的重要挑战。本文将聚焦于一个具体的系统对接集成案例——将聚水潭的店铺信息查询结果集成到MySQL数据库中,以供BI…

Spark基本命令详解

文章目录 Spark基本命令详解一、引言二、Spark Core 基本命令1、Transformations(转换操作)1.1、groupBy(func)1.2、filter(func) 2、Actions(动作操作)2.1、distinct([numTasks])2.2、sortBy(func, [ascending], [numTasks]) 三、…

[在线实验]-ActiveMQ Docker镜像的下载与部署

镜像下载 下载ActiveMQ的Docker镜像文件。通常,这些文件会以.tar格式提供,例如activemq.tar。 docker的activemq镜像资源-CSDN文库 加载镜像 下载完成后,您可以使用以下命令将镜像文件加载到Docker中: docker load --input a…

CQ 社区版 2024.11 | 新增“审批人组”概念、可通过SQL模式自定义审计图表……

CloudQuery 社区 11 月新版本来啦!本月版本依旧是 CUG(CloudQuery 用户组)尝鲜版的更新。 针对审计模块增加了 SQL 模式自定义审计图表;在流程模块引入了“审批人组”概念。此外,在 SQL 编辑器、连接管理等模块都涉及…

mac终端自定义命令打开vscode

1.打开终端配置文件 open -e ~/.bash_profile终端安装了zsh,那么配置文件是.zshrc(打开zsh配置,这里举🌰使用zsh) sudo open -e ~/.zshrc 2.在zshrc配置文件中添加新的脚本(这里的code就是快捷命令可以进…

关于单片机的原理与应用!

成长路上不孤单😊😊😊😊😊😊 【14后😊///计算机爱好者😊///目前正在学习C😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于单片…

深入解析 MySQL 启动方式:`systemctl` 与 `mysqld` 的对比与应用

目录 前言1. 使用 systemctl 启动 MySQL1.1 什么是 systemctl1.2 systemctl 启动 MySQL 的方法1.3 应用场景1.4 优缺点优点缺点 2. 使用 mysqld 命令直接启动 MySQL2.1 什么是 mysqld2.2 mysqld 启动 MySQL 的方法2.3 应用场景2.4 优缺点优点缺点 3. 对比分析结语 前言 MySQL …

简单介绍下 VitePress 中的 vp-doc 和 vp-raw

VitePress 是一个轻量级的静态网站生成器,专为快速构建文档网站而设计。它是基于 Vite 和 Vue 3 构建的,旨在提供快速的开发体验和高效的构建过程。 存在两个需要注意的点:vp-doc 和 vp-raw,它们代表了不同的 CSS 样式类和用途&a…

HTML前端开发-- Flex布局详解及实战

引言 Flex布局,全称为Flexible Box Layout,是一种现代CSS布局技术,它提供了一种更有效的方式来设计响应式布局和复杂页面布局。本文将详细介绍Flex布局的基本概念、属性以及实战应用。 一、基本概念 Flex布局的核心是Flex容器(…

【前端】理解 JavaScript 中 typeof 操作符的独特行为

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: 前端 文章目录 💯前言💯typeof 操作符的基本使用💯为什么 typeof 数组是 "object"?💯为什么 typeof {} 返回 "object"?&…

一键解析RAW文件,GPS定位展示,摄影师专用照片管理软件

作为一款精心打造的数码影像管理工具,bkViewer以其轻量化设计和强大的功能特性脱颖而出。这款软件不仅能够完美处理各类主流图片格式,更整合了专业级的图像信息处理系统,包含完整的EXIF、XMP、IPTC、GPS、ICC等元数据解析能力,并通…

import.meta.glob动态加载图片

import.meta.glob 基于Vite(Vue 3 默认构建工具),用于动态导入模块,特别是当你需要批量导入文件或模块时. const modules import.meta.glob(/path/to/files/**/*.js);注意:import.meta.glob 是针对 源代码&#xff…

[高阶数据结构六]最短路径算法

1.前言 最短路径算法是在图论的基础上讲解的,如果你还不知道图论的相关知识的话,可以阅读下面几篇文章。 [高阶数据结构四] 初始图论_初始图结构-CSDN博客 [高阶数据结构五] 图的遍历和最小生成树_图的遍历和生成树求解-CSDN博客 本章重点:…

开源的跨平台SQL 编辑器Beekeeper Studio

一款开源的跨平台 SQL 编辑器,提供 SQL 语法高亮、自动补全、数据表内容筛选与过滤、连接 Web 数据库、存储历史查询记录等功能。该编辑器支持 SQLite、MySQL、MariaDB、Postgres 等主流数据库,并兼容 Windows、macOS、Linux 等桌面操作系统。 项目地址…

mysql 5.7安装及安装后无法启动问题处理

下载安装包,直接解压 配置环境变量 创建my.ini文件 [mysqld] #端口号 port 3306 #mysql-5.7.27-winx64的路径 basedirD:/soft/mysql57 #mysql-5.7.27-winx64的路径\data datadirD:/soft/mysql57/data #最大连接数 max_connections200 #编码 character-set-server…

spine 动画层 动态权重

前奏.业务背景 这边想实现一个功能,项目中有 一只猫 猫的头会盯着逗猫棒移动。因为素材还没到所以这里使用了 spine 自带的猫头鹰。他的动画 刚好挺有针对性:(关联上篇)https://blog.csdn.net/nicepainkiller/article/details/144…

Spark 内存管理机制

Spark 内存管理 堆内内存和堆外内存 作为一个 JVM 进程,Executor 的内存管理建立在 JVM(最小为六十四分之一,最大为四分之一)的内存管理之上,此外spark还引入了堆外内存(不在JVM中的内存),在spark中是指不…

Vision Transformer(vit)的主干

图解: 代码: class VisionTransformer(nn.Module):def __init__(self, img_size224, patch_size16, in_c3, num_classes1000,embed_dim768, depth12, num_heads12, mlp_ratio4.0, qkv_biasTrue,qk_scaleNone, representation_sizeNone, distilledFalse,…

mongodb配置ssl连接

mongodb5.0.9 centos7.6x86 1、正常启动mongod -f mongodb.conf 2、生成所需要的ssl证书 服务端ssl配置: 2.1生成ca.pem证书 #-x509: 用于生成自签证书,如果不是自签证书则不需要此项 #-days: 证书的有效期限&…