C++ 笔记(六): 进程的虚拟地址空间内存划分和布局

进程的虚拟地址空间内存划分和布局

  • (在x86体系 32位linux环境下学习)

前言

  • 任何编程语言产生两种东西: 指令 + 数据

  • 可执行文件在磁盘上,加载到内存当中,不是直接加载到物理内存

  • IBM解释的虚拟的定义

    • 进程的虚拟地址空间: Linux系统会给当前进程分配一个 2^32大小的空间(4G)
    • 虚拟的定义: 不存在看得见
    • 透明的定义: 存在看不见
    • 物理的定义: 存在看的见, 物理的
    • 删除的定义: 不存在看不见

两种语言的字符串指针简单看下内存问题

  • C 字符串和指向字符串的指针
  • 当你在定义一个字符数组时,如果在定义的同时给它一个初始值,那么就需要使用[]来保存这些字符。
  • 而当你定义一个字符指针时,如果需要给它一个初始值,就可以直接赋值为一个字符串常量的地址。
// 当你在定义一个字符数组时,如果在定义的同时给它一个初始值,那么就需要使用[]来保存这些字符。
void strlen_demo(){
    char str[] = "Hello World";
    cout << "Lenth of: " << str << "is: " << strlen(str) << endl;
}
 
// 而当你定义一个字符指针时,如果需要给它一个初始值,就可以直接赋值为一个字符串常量的地址。
void strlen_ptr_demo(){
    char* str = "Hello World";
    cout << "Lenth of: " << str << "is: " << strlen(str) << endl;
}
  • C++ 字符串和指向字符串的指针
// C++ 的字符串
void cppstring_demo(){
   string str = "Hello World";
   cout << "Lenth of: " << str << "is: " << str.length() << endl;
   
}

void cpp_string_ptr() {
   string* str = new string("Hello World");
   int* asd = new int(8);
   cout << "Lenth of: " << *str << "is: " << str->length() << endl;
   delete str;
   delete asd;
}
  • new是C++中的一个关键字,用来在堆上分配内存。它会在堆上为你分配足够的空间,并返回一个指向该内存的指针。

  • 例如,在上面的代码中, new string(“Hello World”) 会在堆上分配一块内存,并使用string的构造函数来创建一个string对象,并将指针str指向这个对象。

  • 因为在堆上分配的内存在程序结束之前不会被释放,所以如果不手动释放这块内存,会造成内存泄漏。所以需要使用delete来手动释放内存

  • 其他变量,如局部变量、全局变量等,都是在栈上分配的。栈是一种先进后出的数据结构,变量在函数调用时进栈,在函数返回时出栈。因此,当函数返回时,所有在该函数中定义的局部变量都会被销毁,其所占用的内存也会被释放,所以不需要手动删除

  • 栈是一种先进后出的数据结构,它可以用来存储函数的调用过程、局部变量、参数等。

  • 在函数调用时,系统会在栈上为该函数分配一块内存,用来存储该函数的局部变量、参数等。在函数返回时,系统会销毁该函数在栈上分配的内存,释放所占用的空间。

进程线程

(https://zhuanlan.zhihu.com/p/101230252#😃

  • 运行一次代码可能会产生一个进程,也可能不会。当你在操作系统中运行一个程序时,操作系统会为该程序创建一个新的进程。这个进程会拥有它自己的内存空间和系统资源,并且可以独立于其它进程运行。但是如果你在运行一个已经在运行中的进程(重新启动这个进程),那么不会产生新的进程,而是会使用已经存在的进程。
  • 进程: 进程是指由计算机中央处理器执行的程序的一个实例。它包括程序代码及其当前活动,以及程序使用的数据。进程具有自己的内存空间和系统资源,并且可以与其他进程和操作系统交互。进程也可以有多个线程,这是进程的较小单元,可以独立和同时运行,允许并行处理。
  • 总之,运行一次代码是不能确定是否会产生一个进程的。
  • 多线程: 多线程指的是一个进程中有多个独立的执行流。线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。每一个线程都有自己的执行流程,可以被操作系统单独调度。多线程可以让一个进程在执行多个任务时有更高的效率,可以提升程序的运行速度。

进程在内存可用的主要的三个布局

  • .data 数据段,主要存放的全局变量,存在于整个程序的生命周期
  • heap 堆内存,通过new delete自己开辟的空间,自己释放的
  • stack 栈上面,出去后就自动销毁了

虚拟地址空间

每一个进程都有一个默认4G的虚拟空间(可以更改的) 被默认划分成两部分

0x00000000
零地址不可访问访问会报错的,例如: 传空指针会出现: Segmentation fault的程序异常
0x08048000以上的空间不能放东西也不能访问
代码段: 主要放 .txt .rodata只读数据段,不能更改
.data专门存放初始化数据的符号表
.bass存放没有初始化的数据的符号表
.heap堆内存
*.dll(Linux) *.so(Windows)动态链接库/加载共享库
stack栈空间asd
命令行参数和环境变量./a.out 192.168.1.100
0xC00000003G (user space 用户空间: 3G )
ZONE DMA, Zone NORMAL, Zone HIGHMEM内核空间被分成了三个
0xFFFFFFFF4G (内核空间 1G ) ZONE DMA NORMAL HIGHMEM

内核空间被划分成三块

Zone DMA: 约16MB
Zone NORMAL: 800MB 存放: 进程控制快,内核空间的线程 栈空间
Zone HIGHMEM: 高级内核 映射高地址的物理内存

用一个案例看零地址访问

访问的时候会报错,因为零地址是不可以访问的,但是按照道理strlen§;也不能够访问,不知道为什么这里可以访问。这也是为什么在使用指针之前,要先检查它是否为nullptr,防止出现错。

void ex1() {
    char *p = nullptr;
    strlen(p);
    char *src = nullptr;
    char dest[100];
    strcpy(dest, src);
}

问题: 指令(.txt)运行的时候放在哪里?

  • 代码段,0地址

问题: 为什么未初始化的数据是0?

  • 因为放在了.bass上面

用全局变量的内存地址

  • 全局变量
    • 全局变量都叫做数据 在符号表中都会产生符号
    • gdata1, 4有初始化都会放在符号表.data上面
    • gdata2, 3, 5, 6 没有初始化或者初始化为0都放在符号表.bass
  • 局部变量
    • 没有被初始化的,用到才会初始化
    • 放在数据段的
    • a, b, c 是不产生符号的,产生的是三个move指令
    • a: ,把12(0CH),移到a的内存里面
    • 静态局部变量是放在符号表的,用到的时候才会初始化的
  • 总结: 全局变量和局部静态变量放在符号表.bass .data里面,根据是否初始化划分,非静态局部变量产生指令放在.txt数据段上,运行的时候cpu在栈上开辟空间放入内存
// ex2 
#if 1
int gdata1 = 10;
int gdata2 = 0;
int gdata3;

static int gdata4 = 11;
static int gdata5 = 0;
static int gdata6;
#endif

int main() {
    // ex2
    #if 1
    int a = 12;
    int b = 0;
    int c;

    static int e = 13;
    static int f = 0;
    static int g;
    cout << "c: " << c << endl;
    
    #endif


    return 0;
}

每一个进程的空间是私有的,但是内核空间是共享的!!!

  • 进程之间的通信方式?
    • 匿名管道通信: 在内核空间划分了一块空间
    • 我pornhub开了一个进程,xvedio也开了一个进程,pornhub的内核写了一个数据,其他进程也看得见

总结:

  • 不要再说什么全局变量区,局部变量区,说.bass .txt .data 这样的话稍微专业点
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值