进程的虚拟地址空间

进程的虚拟地址空间
先抛出一个问题,什么是进程的虚拟地址空间,它是常说的虚拟内存吗??
不要走,这篇博客虽然不短,但是如果你想成为优秀的人,就耐心的看下去
我们知道任何的编程语言,无非生成的就是两种东西,指令和数据,编译链接完成以后,产生一个xxx.exe可执行的文件(Windows上)存放在我们的磁盘上的,而不是内存中,当程序运行的时候(Windows上双击运行,Linux ./a.out运行起来),才被加载到内存当中的,这就引出了一个问题,运行程序的时候把这个程序的哪些东西加载到内存,加载到内存怎么存放的,内存有没有区域的划分,划分以后是什么样子的,接下来就看看这个问题的解答。
背景:x86体系,32位的Linux平台下
程序运行的时候,Linux系统会给当前的程序分配一个2^32次方大小的虚拟地址空间,也即是4G大小的虚拟空间,我们给它起了个名字,就叫做程序的虚拟地址空间。但是不要以为操作系统把我们计算机上的所有内存(普通计算机目前有4G、8G的内存)都分配给它了,这个虚拟地址空间它其实是不存在的,只是我们假象出来的一种结构,用来描述程序的各个段,方便我们语言上的交流和描述。这个程序最终可能只是占据真正物理内存的几个页面大小,并不会将计算机的所有内存都分配出去,不然我们的电脑只能运行一个程序了,接下来就看看这个虚拟地址空间的划分情况,上图来得直接
get点1,掌握虚拟地址空间的划分情况,熟练画出,并且知道每一段的内容

在这里插入图片描述

1、0x00000000到0x08048000地址
上图就是我们的Linux的4G虚拟地址空间的划分情况,从0x00000000到0xffffffff,先从低地址说起,0x00000000到0x08048000这个区域的地址是禁止访问的,大概有128M大小,一些刚刚学习编程的人,有时候会犯这样的错误,char p = nullptr ;strlen(p) 或者是  char p = nullptr,strcpy(dest,p),写出这样的代码,程序运行起来的时候,程序崩溃了,直接终止,就是因为p指针指向的是一片拒绝访问的地址,0x00000000,所以肯定会崩溃了。曾经遇到过这样一个问题,为什么是0x08048000,这个地方,这个问题的话,我会在接下来的博客中讲述到,读者也可以自行查阅资料。
2、.text和.rodata
接下来就是.text和.rodata了,刚刚说过,任何的编程语言,产生的无非就是两种东西,指令和数据,我们的指令其实都是存放在.text这个段中的,rodata存放的也是和指令具有相同特性的东西的,那就是只允许读,不允许修改。比如char* p = “hello world”,p是栈上的一个指针,指向的是一个常量字符串,存储在rodata段,当出现下面这样的代码,也是崩溃掉的, *p = ‘a’;试图去修改p指针指向的第一个字符的内容,这样肯定是行不通的,因为它是rodata,只读的,不可以修改。
3、.data和.bss
说到.data和.bss段的话,其实它们都是存储数据的,那么既然都是存储数据的,为什么要划分两个段呢??
它们虽然都是存储数据的,但是它们存储数据的特点是不一样的,.data存储的数据都是初始化了的而且初始化的值不为0,相反,.bss即是没有初始化的和初始化的值为0的数据
比如看看下面的代码中的数据存放的位置是.data还是.bss
int gdata1 = 10;
int gdata2 = 0;
int gdata3;
static int gdata4 = 10;
static int gdata5 = 0;
static int gdata6;

int main()
{
    int a = 10;
    int b = 0;
    int c;
    static int d = 10;
    static int e = 0;
    static int f;
    return 0;
}
1234567891011121314151617
按照刚才我阐述的规则,看看这个变量存储的位置吧!
首先是全局变量
gdata1和gdata4都是初始化而且初始化的值不为0,所以存储在.data中
gdata2、gdata3、gdata5、gdata6都是未初始化或者初始化为0的,所以它们存储在.bss段
接下来进入main函数中(记住在main函数中的不一定都是stack中的,有一个关键字叫做 static了解一下)
a、b、c 不是在.data和.bss中的,它们是栈上的变量,在stack
d是在.data中的,e、f是在.bss中的,有人糊涂了,说,a b c d e f 都是在main函数里面,它们不应该都是在栈上吗,应该在stack啊,为什么有的在.data,有的在.bss中啊?
说明你对static这个关键字不熟悉,static修饰局部变量的时候,它的生命周期就和全局变量是一样长的了,虽然它的作用域没有变,既然生命周期变的很长,就不能放在stack上了,因为随着栈帧的回退,它们也就会随之消失,所以把它们放在数据段,(.data或者.bss中),至于具体放在那个段,看它们的具体情况,b初始化了,而且初始化的值不为0,所以放在.data中了,e 和 f 未初始化或者初始化了,但是初始化的值是0的,放在了.bss段
4、堆(heap)
该说到heap(堆),这是一个自由的区域,为什么说是自由呢,因为如果说程序中,没有malloc或者new的话,这个区域就不会存在的,这个动态开辟空间的地方,由程序员自己控制的,它是从低地址到高地址生长的
5、加载共享库
接着是加载共享库,Linux下叫共享库,Windows叫做动态库
*.dll是Windows上动态库的形式
*.so是Linux上动态库的形式
6、栈(stack)
很重要的一个东西,就是stack(栈)
函数的调用,需要栈帧的开辟
函数调用完成后,也需要回退栈帧
上面说的a、b、c变量都是放在栈上的,所以你觉得重要吗?
栈和堆相对而生,说的是它们地址的增长方向,堆是从低到高生长,栈是从高到低生长的
7、命令行参数和环境变量
玩过Linux系统的都知道,运行一个程序的时候,可以给程序的后面,加上一些参数,比如
./a.out 192.168.31.69传入了一个参数,这个就是命令行参数了,具体的内容,读者自行查看,个人能力都是不同的,说的太多,会乱
环境变量
我们写代码的时候,总是#include<stdio.h>,有时候,我们也会包含自己写的头文件,比如#include"test.h",此时用的是双引号,关于尖括号和双引号,可以去看我的另外一篇博客。
环境变量其实就是告诉编译器,如何去搜索我们的头文件,我们写了一个#include"test.h",为什么编译器就知道它在哪里呢??,编译器就这么神奇吗,其实,这都是环境变量的功劳,它指导编译器去那个地方去找文件,是它默默的在后面付出的
8、进入内核空间(可不细究)
分为三块
ZONE DMA大小是16M左右
ZONE NORMAL大小是800M左右,内核里面线程依赖的栈空间、pcb块 task_struct结构体
ZONE HIGHMEM这是做高地址内存地址映射用的
澄清一个事实,进程的虚拟地址空间,和虚拟内存不是一个概念,虚拟内存是操作系统提出的概念,是为了解决内存紧张的问题,而进程的虚拟地址空间,是操作系统给进程的虚拟的空间划分
(若在文章中,出现错误,请指出)
 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值