進程描述符 task_structnode
線程建立的時候,加上了 CLONE_VM 標記,這樣 線程的內存描述符 將直接指向 父進程的內存描述符。程序員
內存描述符mm_struct數組
進程棧:stack架構
線程棧:使用mmap系統調用分配的空間,可是mmap分配的系統空間是什麼呢?也就是上圖中的mmap區域或者說共享的內存映射區域是什麼呢?它的方向是向上生長仍是向下生長的?app
mmap其實和堆同樣,實際上能夠說他們都是動態內存分配,可是嚴格來講mmap區域並不屬於堆區,反而和堆區會爭用虛擬地址空間。函數
這裏要提到一個很重要的概念,內存的延遲分配,只有在真正訪問一個地址的時候才創建這個地址的物理映射,這是Linux內存管理的基本思想。Linux內核在用戶申請內存的時候,只是給它分配了一個線性區(也就是虛擬內存),並無分配實際物理內存;只有當用戶使用這塊內存的時候,內核纔會分配具體的物理頁面給用戶,這時候才佔用寶貴的物理內存。內核釋放物理頁面是經過釋放先行區,找到其對應的物理頁面,將其所有釋放的過程。優化
struct mm_struct { struct vm_area_struct *mmap; /* 內存區域鏈表 */ struct rb_root mm_rb; /* VMA 造成的紅黑樹 */ ... struct list_head mmlist; /* 全部 mm_struct 造成的鏈表 */ ... unsigned long total_vm; /* 所有頁面數目 */ unsigned long locked_vm; /* 上鎖的頁面數據 */ unsigned long pinned_vm; /* Refcount permanently increased */ unsigned long shared_vm; /* 共享頁面數目 Shared pages (files) */ unsigned long exec_vm; /* 可執行頁面數目 VM_EXEC & ~VM_WRITE */ unsigned long stack_vm; /* 棧區頁面數目 VM_GROWSUP/DOWN */ unsigned long def_flags; unsigned long start_code, end_code, start_data, end_data; /* 代碼段、數據段 起始地址和結束地址 */ unsigned long start_brk, brk, start_stack; /* 棧區 的起始地址,堆區 起始地址和結束地址 */ unsigned long arg_start, arg_end, env_start, env_end; /* 命令行參數 和 環境變量的 起始地址和結束地址 */ ... /* Architecture-specific MM context */ mm_context_t context; /* 體系結構特殊數據 */ /* Must use atomic bitops to access the bits */ unsigned long flags; /* 狀態標誌位 */ ... /* Coredumping and NUMA and HugePage 相關結構體 */ };
爲何須要區分這些棧,其實都是設計上的問題。這裏就我看到過的一些觀點進行彙總,供你們討論:atom
爲何須要單獨的進程內核棧?命令行
schedule()
讓出 CPU;此時調度器喚醒了另外一個進程 B,碰巧進程 B 也須要系統調用進入內核態。那問題就來了,若是內核棧只有一個,那進程 B 進入內核態的時候產生的壓棧操做,必然會破壞掉進程 A 已有的內核棧數據;一但進程 A 的內核棧數據被破壞,極可能致使進程 A 的內核態沒法正確返回到對應的用戶態了;爲何須要單獨的線程棧?線程
進程和線程是否共享一個內核棧?
dup_task_struct
來建立 task 相關結構體,而內核棧也是在此函數中 alloc_thread_info_node
出來的。所以雖然線程和進程共享一個地址空間 mm_struct
,可是並不共享一個內核棧。爲何須要單獨中斷棧?
進程空間中堆和棧的區別:
空間大小:棧系統指定大小限制在8M(M 級別),棧是連續空間;堆沒有限定,是不連續存儲空間,靠鏈表連接。
分配方式:堆都是程序員代碼中動態分配和回收的,沒有回收會產生內存泄露;棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,好比局部變量的分配。動態分配由alloca函數進行分配,可是棧的動態分配和堆是不一樣的,他的動態分配是由編譯器進行釋放,無需咱們手工實現。
分配效率:棧分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行;堆是經過調用庫函數
棧:在函數調用時,第一個進棧的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的地址,而後是函數的各個參數,在大多數的 C 編譯器中,參數是由右往左入棧的,而後是函數中的局部變量。注意靜態變量是不入棧的。
當本次函數調用結束後,局部變量先出棧,而後是參數,最後棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。
堆:通常是在堆的頭部用一個字節存放堆的大小。堆中的具體內容有程序員安排。
總之,棧比堆效率高,但沒有堆靈活,優先使用棧,大內存使用堆。
char a[] = "hello"; //字符數組a的容量是6個字符,其內容爲hello。a的內容能夠改變,如a[0]= ‘X’
char *p = "world";//指針p指向常量字符串「world」(位於靜態存儲區,內容爲world),常量字符串的內容是不能夠被修改的。
/** *內核空間 *棧:grow down,大小系統設置~8M,連續空間,編譯器自動分配 *Memory Mapping Seg:堆棧共享,線程棧 *堆:grow up,大小硬件定,不連續空間,程序員malloc *BSS:Block Started by Symbol,未初始化的全局變量和靜態變量(靜態data區) *數據段:存放已初始化的全局變量、靜態變量(全局和局部)、const常量數據(常量data區) *代碼段:存放CPU執行的機器指令,代碼區是可共享,而且是隻讀的。這部分區域的大小在程序運行前就已經肯定 **/ #include <string> int a=0; //數據段:全局初始化變量 char *p1; //BSS:全局未初始化變量 void main() { int b;//棧 char s[] = "abc"; //棧 char *p2; //棧 char *p3="123456"; //123456\0在常量區(代碼段??),p3在棧上。 static int c=0; //數據段:全局(靜態)初始化區 *p1 = (char*)malloc(10); //分配得來的10字節區域在堆上 *p2 = (char*)malloc(20); //分配得來的20字節區域在堆上。 strcpy(p1,"123456"); //123456\0放在常量區,編譯器可能會將它與p3所向"123456\0"優化成一個地方。 } //const 常量 或右值常量如"123456"放在數據段仍是代碼段??
malloc與free是C++/C語言的標準庫函數,new/delete是C++的運算符。它們均可用於申請動態內存和釋放內存。可是new/delete會調用對象的構造和析構函數。