栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++基础(一)——底层分析

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++基础(一)——底层分析

01 进程的虚拟地址空间 一、操作系统让进程访问的是虚拟地址空间,而不是物理地址

1.任何程序在编译时都会产生指令和数据,进行地址编号,但是如果地址不连续,就会程序运行不起来,编译器的地址管理比较麻烦(无法动态的获知物理空间的使用情况,也就无法为数据进行编号)

2.进程直接访问物理地址,如果此时有一个野指针,那么在进行操作野指针的时候可能会改变其他空间的数据,造成不安全的事件发生(无法进行内存访问控制)

3.程序运行空间通常需要一块连续的空间,空间利用率低,通过虚拟地址空间映射到物理内存上进行数据存储,可以实现数据在物理空间上离散式存储,提高内存的利用率,并且每个进程都有一份属于自己的连续空间使用

二、进程的虚拟地址空间

指令存放在.text段

int a = 12;
int b = 0;
int c;
// 编译后成为汇编指令 
// mov dword ptr[a],0ch
//存放.text段
//局部变量 指令

数据存放在

static int e = 13;
static int f = 0;
static int g;
//局部变量静态变量e存放在.data段 不为0 f、g存放在.bss段 为0/未初始化

可打印g的值为0,无法打印c的值(栈上的无效值)

#include 
using namespace std;

int main(){
    int c;
    static int g = 0;
    cout << c << g << endl;
    
    return 0;
}
//无法打印c的值 可以打印g的值为0

1.包括了用户空间、内核空间

2.全局变量均存放在data段(不为0)、bss段(为0 或者 未初始化)

3.局部变量 产生指令

局部变量中的静态变量 产生数据(data段、bss段)

三、进程和线程的区别

进程是系统进行资源分配和调度的一个独立单位

线程是进程的一个实体,是CPU调度和分配基本单位

简而言之

一个程序至少要有一个进程,一个进程至少要有一个线程

四、多进程

1.有各自的用户空间(隔离),共享内核空间

2.进程之间通信方式 : 匿名管道通信(内核空间内存数据可共享)

参考链接

1.https://blog.csdn.net/weixin_45975835/article/details/115010310

2.《深入理解计算机系统》 第七章

3.https://blog.csdn.net/yaosiming2011/article/details/44280797

02 函数调用堆栈
#include 
using namespace std;

int sum(int a, int b) {
    int temp;
    temp = a + b;
    return temp;
}


int main() {
    int a = 10;//局部变量不产生符号 mov dword ptr[ebp-4], 0Ah
    int b = 20;// mov dword ptr[ebp-8], 14h
    int ret = sum(a,b);
    
    cout << "ret is " << ret << endl;

    system("pause");
    return 0;
}

Q1:main函数调用sum,sum执行完以后,怎么知道回到哪个函数中?

通过汇编语言中的call指令存储了下一条指令的地址。

Q2:sum函数执行完,回到main以后,怎么知道从哪一行指令继续运行的?

在sum函数调用结束以后,ret指令在CPU的PC寄存器中读取刚刚存储的地址,回到原来的函数。

03 C++的编译与链接 编译过程

main.cpp sum.cpp

extern int gdata;// gdata *UND*  符号引用
int sum(int, int);// sum *UND*

int data = 20; //data .data段

int main() { //main .text段
    int a = gdata;
    int b = data;
    
    int ret = sum(a, b);
    
    return 0;
}
int gdata = 10;//gdata .data段

int sum(int a, int b) { //sum_int_int  .text段
    return a + b;
}

预编译:

’#‘开头的命令在预编译完成

#pragma lib
#pragma link
//在链接阶段处理

编译:

gcc
g++ -o

汇编:

主要是符号表的输出

二进制可重定位的目标文件(*.obj) main.o sum.o

其中,.o文件的格式组成:

elf文件头

.text

.data

.bss

.symbal

.section table
链接过程

链接:编译完成的所有.o文件和静态库文件

步骤一:

所有.o文件段的合并==(.text、.bss、.data等)==

符号表合并后,进行符号解析(UND所有对符号的引用,都要找到该符号的定义的地方,可能会报错,符号未定义、重定义等)

给所有的符号分配虚拟地址(在此时给符号分配虚拟地址)

步骤二:

符号的重定位(重定向

在代码段将符号的地址填成正确的地址

查询符号段、地址指令
objdump -s sum.o
objdump -t a.out
readelf -h a.out

输出

a.out

读取a.out的入口地址,入口地址为main函数的地址

都是各种段组成,.out相比于.obj多了progrma headers段,有两个load,告诉系统运行到这个程序的时候,把哪些内容加载到内存中(.text、.data)

-o 可执行程序的名称

a.exe

可执行文件加载过程:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/847217.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号