通过命令和代码初步感受存储管理
1.什么是存储器的层次结构,如何通过命令进行查看?
大家所熟知的是内存(RAM)和外存,尽管内存比外存速度快很多,但还是无法与CPU的速度匹配,因此CPU内部就需要更快的存储装置,这就是高速缓存(Cache)。从图中看出,高速缓存并不属于内存,而属于CPU的组成部分。另外,还有内存管理单元MMU(Memory Management Unit),这是为了支持虚拟内存管理而专门设置的硬件机制,也属于CPU的管辖范围。可以看出,为了支持内存管理,CPU使出了浑身解数,CPU和内存的关联更加紧密。
那么如何查看CPU中的Cache ?
我们可以通过lscpu命令查看CPU内部的缓存,
从输出结果看,在x86机器上, L1d 和L1i cache,为一级数据和指令缓存,大小都为32k, L2,L3Cache为二级和三级缓存,分别是521K,8192K,大小各不同。
可以通过free命令查看内存的情况。
free命令查看内存 Mem 行(第二行)是内存的使用情况。 Swap 行(第三行)是交换空间的使用情况。 total 列显示系统总的可用物理内存和交换空间大小。 used 列显示已经被使用的物理内存和交换空间。 free 列显示还有多少物理内存和交换空间可用使用。 shared 列显示被共享使用的物理内存大小。 buff/cache 列显示被 buffer 和 cache 使用的物理内存大小。 available 列显示还可以被应用程序使用的物理内存大小。
2. 如何编译、链接、装载并执行一个程序?
3. Linux中进程地址空间是怎样的?
一个程序编译链接后形成可执行文件,可执行文件加载执行后,就摇身一变成为进程了,进程地址空间如图最右边所示。除了代码区,初始化数据区,BSS区外,还有堆、栈和共享库等,其中,栈中存放存放函数的参数值、返回值、局部变量等,而堆是用来为用户程序中的malloc()等分配内存的,printf函数存放在共享库中。
4. 如何编写代码 感受自己写的代码放在进程地址空间的哪些段中?
代码
请敲如下代码:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int A; //全局未初始化变量 int B=0; //全局初始化为0的变量 int C=2; //全局初始化变量 static int D; //全局静态未初始化变量 static int E=0; //全局静态初始化为0的变量 static int F=4; //全局静态初始化变量 const int G=5; //全局常量 const char H=6; int main(void){ int a; //局部未初始化变量 int b=0; //局部初始化为0的变量 int c=2; //局部初始化变量 static int d; //局部静态未初始化变量 static int e=0; //局部静态初始化为0的变量 static int f=4; //局部静态初始化变量 const int g=5; //局部常量 char char1[] = "abcde";//局部字符串数组 char *cptr="123456"; //p在栈上,123456在常量区 int *heap=malloc(sizeof(int)*4);//堆 printf("PID is :%d\n\n", getpid()); printf("int A A_addr = %p\n",&A); printf("int B=0 B_addr = %p\n",&B); printf("int C=2 C_addr = %p\n",&C); printf("static int D D_addr = %p\n",&D); printf("static int E=0 E_addr = %p\n",&E); printf("static int F=4 D_addr = %p\n",&F); printf("const int G=5 G_addr = %p\n",&G); printf("const char H=6 H_addr = %p\n",&H); printf("\n"); printf("int a a_addr = %p\n",&a); printf("int b=0 b_addr = %p\n",&b); printf("int c=2 c_addr = %p\n",&c); printf("static int d d_addr = %p\n",&d); printf("static int e=0 e_addr = %p\n",&e); printf("static int f=4 f_addr = %p\n",&f); printf("const int g=5 g_addr = %p\n",&g); printf("\n"); printf("char char1[] = 'abcde'\t\t\tchar1_addr = %p\n",char1); printf("char char1[] = 'abcde'\t\t\t&char1_addr = %p\n",&char1); printf("char *cptr = '1'\t\t\tcptr_addr = %p\n",&cptr); printf("value of the cptr\t\t\tcptr_value = 0x%p\n",cptr); printf("value of the %p\t\t\tvalue_0x%p = %d\n",cptr,cptr,*cptr); printf("int* heap = malloc(sizeof(int)*4)\theap__addr = %p\n",heap); printf("int* heap = malloc(sizeof(int)*4)\t&heap__addr = %p\n",&heap); pause(); //程序结束运行之后再回收堆内存,方便观案堆的地址 free(heap); return 0; }
编译并运行该程序:
仔细分析你的运行结果,你发现什么?
结果
你也可以用size命令进行查看。
分析
进程地址空间的段说明
在图片下搜索关键词
32位4GLinux虚拟地址空间布局
【Linux】Linux下4G虚拟地址空间布局
简述代码中关于.data、.bss、.rodata、.text段的意义
kernel space 内核空间
Undefined Region
Stack 栈
Memmory Mapping Region 共享库
heap 堆
bss段(Block(b) Started(s) by Symbol(s)):即用来存储一些未被初始化的全局变量和静态变量的内存区域,一般在初始化时bss段部分将会清零,属于静态内存分配,即程序一开始就将其清零了。
例:
#include<xxx.h> int bss_value1;//全局的未被初始化的变量,处于bss段 static void *bss_value2 = NULL;//全局的指针变量,处于bss段 int main() { //…; return 0; }
注意:BSS段不包含任何数据,只是简单的维护开始和结束的地址,以便内存区能在运行时被有效的清零。并不给该段的数据分配空间,只是记录数据所需空间的大小。不占用可执行文件的空间,BSS段在应用程序的二进制映像文件中并不存在。
特点:可读写。
data段:又称为数据段,通常是指用来存放程序中已被初始化的全局变量,常量,静态变量的一块内存区域。也就是我们通常说的静态存储区。
例:
#include<xxx.h> int bss_value1=5;//全局的被初始化的变量,处于data段 int main() { …; return 0; }
注意:data段为数据分配空间,数据保存在目标文件中。
特点:可读写。
text段 (textsegment):通常指用来存放程序执行代码的一块内存区域,这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读。
在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
特点:只读
rodata段:该段也叫常量区,用于存放常量数据,ro就是read only(只读)。
例:
#include<xxx.h> //int bss_value1=5;//全局的被初始化的变量,处于data段 //const int bss_value1=5;//加上const变为只读的常量,处于rodata段 int main() { //…; return 0; }
注意:并不是所有的常量都放在常量数据段,特殊情况如下
(1):有些立即数与指令编译在一起放在代码段
(2):对于字符串常量,编译器会自动去掉重复的字符串,保证一个字符串在一个可执行文件(EXE/SO)中只存在一份拷贝。
c语言中const修饰的局部变量和全局变量存放在哪个区域
分析
以下皆是个人见解
int A; //全局未初始化变量 bss int B=0; //全局初始化为0的变量 bss int C=2; //全局初始化变量 data static int D; //全局静态未初始化变量 bss static int E=0; //全局静态初始化为0的变量 bss static int F=4; //全局静态初始化变量 data const int G=5; //全局常量 rodata const char H=6; rodata int main(void){ int a; //局部未初始化变量 stack int b=0; //局部初始化为0的变量 stack int c=2; //局部初始化变量 stack static int d; //局部静态未初始化变量 bss static int e=0; //局部静态初始化为0的变量 bss static int f=4; //局部静态初始化变量 data const int g=5; //局部常量 stack char char1[] = "abcde";//局部字符串数组 stack char *cptr="123456"; //p在栈上,123456在常量区 int *heap=malloc(sizeof(int)*4);//堆 //... }
5. 如何查看编译、链接后的汇编代码?
通过objdump命令
1) 查看编译后的汇编代码
objdump -d test.o -Mintel //intel 汇编格式
2)查看链接后的汇编代码
objdump -d test -Mintel //intel 汇编格式
进行对比,说明编译到底做了什么,链接做了什么?