系統中的每個進程運行的時候都會認爲本身有連續和完整的的地址空間,這些地址空間被稱做虛擬地址。數據和程序被存儲在固態介質中,當系統系統須要某一段數據或程序時就會被搬運到內存當中,而不是將整個程序搬運到內存當中再運行起來,這樣大大提升了程序的運行效率,和節約了內存空間。
如上圖所示,每一個程序都認爲本身有連續和完整的地址,實際上只佔了內存的一小部分。內存會生成每塊爲4K大小的頁框,存在固態介質中的程序也會分紅每塊4k的頁,頁框用來存放頁的內容,頁框和頁分別有頁框碼和頁碼,MMU將會經過這兩個碼創建頁表。
當處理器試圖訪問一個虛擬內存頁面時,先去頁表中去查詢該頁是否已映射到頁框中。若是在,則MMU會把頁碼轉換成頁框碼,並加上虛擬地址提供的頁內偏移量造成物理地址後去訪問物理內存,再根據物理內存地址去訪問虛擬內存;若是不在,則意味着該虛存頁面尚未被載入內存,這時MMU就會通知操做系統:發生了一個頁面訪問錯誤(頁面錯誤),接下來系統會啓動所謂的「請頁」機制,即調用相應的系統操做函數,判斷該虛擬地址是否爲有效地址。函數
注意:
圖中有兩段空間是不可訪問的。佈局
棧內存屬於自動存儲區,內存空間的分配,釋放由系統自動完成,不須要用戶參與,棧內存中存了,環境變量,命令行參數,函數調用產生的局部變量。地址是從高地址向地址值增加的。
棧內存的特色:
1.空間有限,尤爲嵌入式的環境中。儘可能不要去使用過多的棧空間;
2.每當一個函數被調用的時候就會產生一個新的棧空間(棧幀),用來存放該函數的局部變量;
3.每當一個函數退出的時候他的棧幀將會被釋放。釋放後內存的內容還在,只不過內存已經歸還給系統進行下一次分配。操作系統
ulimit -a //查看棧的大小 ulimit -s 1024 //臨時將棧設置成1024K的大小
數據段分爲三種數據,分別是未初始化的靜態變量(.bss段),已初始化的靜態數據(.data)和常量(.rodata)。
靜態數據有全局變量(定義在函數體外面的變量)和被關鍵字static修飾的局部變量。命令行
int kl ; // 全局變量 , 未初始化的靜態變量 int func(int arg , char const * argv[]) { int a ; // 未初始化的局部變量 static char b ; // 使用static 修飾的局部變量,未初始化的靜態變量 static int j = 0 ;// 使用static 修飾的局部變量,並初始化爲0 return 0 ; }
注意:
1.未初始化的靜態變量被存放到.bss段中,而且它的值所有零(被初始化爲0);
2.初始化語句只會被執行一次;
3.靜態數據從程序開始運行就已經存在,直到程序退出纔會被釋放;
4.static 修飾的局部變量:會把本來應該存放在棧空間的數據改成存放到數據段中;
5.static 修飾的全局變量:使得本來是全部文件可見變爲本文件可見(縮小了可見範圍)。3d
代碼段分爲.text(用戶代碼)和.init(系統初始化代碼)指針
堆空間(heap)又稱爲堆內存、自由內存。因爲該區域的內存分配以及釋放都由用戶本身決定。
特徵:
相對與棧內存來講他的內存空間很是大,堆空間的大小隻受限於物理內存,系統不會對堆內存有過多的干預;
堆內存從低地址往高地址增加;
堆內存的名字都是匿名,只能經過指針來訪問;
堆內存的空間只有用戶手動釋放,否則他永遠都存在,直到程序退出;
堆內存空間操做的API接口:
申請: malloc / calloc / realloc
釋放: free
清空: bzero / memsetcode
頭 文 件:#include <stdlib.h>
定義函數:void * malloc(size_t size);
參數分析:size --> 須要申請內存空間字節大小(堆內存)
返 回 值:成功 返回一個指針指向該空間的起始地址 失敗 返回NULLblog
頭 文 件:#include <stdlib.h>
定義函數:void free(void *ptr);
參數分析:ptr --> 須要釋放的原先配置內存的起始地址
返 回 值:無接口
示例代碼: int *p_int = NULL ; p_int=(int *)malloc(4);//在堆內存中申請一篇4字節的空間 *p_int = 1900 ; printf("*p_int:%d\n" , *p_int); printf("p_int:%p\n" , p_int); printf("&p_int:%p\n" , &p_int); free(p_int); // 釋放掉 p_int所指向的內存空間 p_int = NULL ;
頭 文 件:#include <stdlib.h>
定義函數:void *calloc(size_t nmemb, size_t size); //申請成功後還會將申請的內存清零
參數分析:nmemb --> 須要申請的內存塊數(多少個房間;size --> 每一塊內存的大小 (房間的大小)
返 回 值:成功 返回內存的入口地址 失敗 返回NULL進程
int * p = calloc(10 , sizeof(int) ); for (int i = 0; i < 10 ; i++) { printf("p +_ %d : %d \n " , i , *(p + i) ); //能夠發現打印出來的值都是0 }
頭 文 件:#include <stdlib.h>
定義函數:void *realloc(void *ptr, size_t size);
參數分析:ptr --> 須要從新分配的內存的首地址 (老房子) size --> 新內存的大小
返 回 值:成功 返回新的入口地址 失敗 NULL
頭 文 件:#include <string.h>
定義函數:void bzero(void *s, int n);
參數分析:s --> 須要清空內存的起始地址 n --> 須要清空數據的大小
返 回 值:無
頭 文 件: #include <string.h>
定義函數: void * memset(void *s, int c, size_t n);
參數分析:s --> 須要清空內存的起始地址 c --> 須要填入的內容(字符型) n --> 須要填入的字節數
返 回 值:成功 返回指向 s 的指針 失敗
申請時注意:
1.memset 是按一字節來填充內存的,unsigned char, 因此範圍在 0 到 255 之間。
2.malloc申請內存,他的初始值是隨機值,通常須要使用bzero/memset來清空
2.calloc 申請內存,默認初始化爲0
3.free 只能釋放堆空間 ,不能夠釋放其餘內存區的內容
釋放時注意:
1.釋放意味着這篇內存交換給系統。
2.釋放內存是不會改變指針的指向的,須要手動指向NULL 。
3.釋放內存只是單純的交還給系統,並無對內存的內容有任何的修改。
#include <stdio.h> #include <stdlib.h> #include <string.h> //函數聲明 void func (void ); // 全局變量 int d ;// 數據段 .bss 未初始化的靜態數據 int e = 100 ; // 數據段 .data 已初始化的靜態數據 int main(){ int a = 100 ; // 棧空間 static int b ; //數據段 .bss 未初始化的靜態數據 static int c = 100 ; // 數據段 .data 已初始化的靜態數據 char * p = "Hello " ; // 數據段 .rodata 常量區 char *p1 = malloc(4); // p1 在棧空間,指向堆空間 printf("&a:%p\n" , &a ); printf("p1:%p\n" , p1 ); printf("&b:%p\n" , &b ); printf("&c:%p\n" , &c ); printf("&d:%p\n" , &d ); printf("&e:%p\n" , &e ); printf("p:%p\n" , p ); printf("&func:%p\n" , &func ); return 0; } void func (void ){ //代碼段 return ; }