【hello Linux】程序地址空间
目录
1. 内存空间布局
2. 虚拟地址的引出
3. 进程地址空间
补充:
Linux🌷
1. 内存空间布局
在之前我们学习C语言时,我们知道可以大致将内存划分为:堆区、栈区和静态区;
其实这样的划分是相当粗略的,下来看一下较为细致的空间布局图:
用户空间是供我们使用的,内核空间是供操作系统使用的。
下来用一段代码来验证一下地址空间划分是否真如上述一般:
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>int g_val=100;int g_unval; int main(int argc,char* argv[],char* env[]) { const char* s="hello linux!\\n"; printf("code addr: %p\\n",main); printf("const string addr: %p\\n",s); printf("init_val addr: %p\\n",&g_val); printf("uninit_val addr: %p\\n",&g_unval); char* heap1=(char*)malloc(1); char* heap2=(char*)malloc(1); char* heap3=(char*)malloc(1); printf("heap-1 addr: %p\\n",heap1); printf("heap-2 addr: %p\\n",heap2); printf("heap-3 addr: %p\\n",heap3); int a=10; int b=2; printf("stack-1 addr: %p\\n",&s); printf("stack-2 addr: %p\\n",&heap1); printf("stack-3 addr: %p\\n",&heap2); printf("stack-4 addr: %p\\n",&heap3); printf("stack-5 addr: %p\\n",&a); printf("stack-6 addr: %p\\n",&b); printf("cmd addr: %p\\n",argv[0]); printf("env addr: %p\\n",env[0]); return 0; }
经过证实,内存空间布局确实如上述图中所画。
由低地址->高地址,首先是:代码区、字符串常量区、已初始化的全局变量、未初始化的全
局变量、堆区(地址由小到大)、栈区(地址由大到小)、命令行参数、环境变量。
2. 虚拟地址的引出
先来看一段代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h> int g_val=100; int main()
{ //创建子进程 if(fork()== 0) { //child int count=5; while(count) { printf("I am child, times: %d ,g_val=%d ,&g_val=%p\\n",count,g_val,&g_val); count--; sleep(1); if(count==3) { printf("******child开始更改数据************\\n"); g_val=200; printf("******child更改数据完成************\\n"); } } } else { //parent while(1) { printf("I am father,g_val=%d,&g_val=%p\\n",g_val,&g_val); sleep(1); } } return 0;
}
上述代码:创建了一个子进程,父子进程分别打印g_val的值和g_val的地址,当子进程在count=3
时,将g_val的值更改,发现父子进程打印出来的g_val的值不一样了,这个我们知道—是因为“写时
拷贝”的原因呢。但我们惊奇的发现,它们的值不一样,但是地址却一样。
在这时我们很疑惑,难道同一地址空间能存不同的值吗?显然是不能的!
那是怎么回事呢?
其实我们打印出来的这个地址,绝对不是物理地址(物理内存),是虚拟地址!
我们在用C/C++语言所看到的地址,全部都是虚拟地址!
物理地址,用户一概看不到,由OS统一管理。
OS负责将虚拟地址转化为物理地址!
3. 进程地址空间
进程每次被创建出来,其实OS不止为进程创建task_struct,其实还为每个进程开辟了虚拟地址空
间mm_struct(大小4GB / 在32位下),以及页表(保存虚拟地址—>物理地址的映射)
如下所示:
在这时我们便要对进程这一概念进一步完善:
进程 = task_struct + mm_struct + 页表 + 数据 + 代码
补充:
我们在学习进程优先级时,知道nice为优先级的优先级的修正值,其取值为-19~20,共40个级别;
其实进程的运行队列也是有40个,将优先级相同的进程放在一个运行队列中,这时候更加方便
CPU的调度。
坚持打卡!
😃