進程/線程先申請虛擬地址空間4G,其中1G的內核空間是全部普通進程/線程共享的。每一個在建立的時候在內核棧底部申請一個thread_info,進程經過fork,線程clone(區別是傳入一些共享的東西)。thread_info 結構體中有一個 struct task_struct task,task 指向的就是這個進程或線程相關的 task_struct 對象。thread_info 結構體中有一個 struct task_struct task,task 指向的就是這個進程或線程相關的 task_struct 對象。linux
task_struct描述進程/線程虛擬空間位置信息等。函數
volatile long state;//進程狀態 void *stack; //內核棧 struct thread_info *thread_info。thread_info 指向該進程/線程的基本信息。 struct mm_struct *mm。mm_struct 對象用來管理該進程/線程的頁表以及虛擬內存區。 struct mm_struct *active_mm。主要用於內核線程訪問主內核頁全局目錄。 struct fs_struct *fs。fs_struct 是關於文件系統的對象。 struct files_struct *files。files_struct 是關於打開的文件的對象。 struct signal_struct *signal。signal_struct 是關於信號的對象。 pid_t pid; pid_t tgid; ……
總體虛擬內存圖示:佈局
棧 | 局部變量、函數參數、返回地址等 |
堆 | 動態分配的內存(有碎片,brk或mmap申請,內存池/垃圾回收) |
BSS段 | 未初始化或初值爲0的全局變量和靜態局部變量 |
數據段 | 已初始化且初值非0的全局變量和靜態局部變量 |
代碼段 | 可執行代碼、字符串字面值、只讀變量 |
mmap | 高效的文件I/O方式, 於是被用於裝載動態共享庫 |
mm_struct:spa
線程私有:棧,寄存器,線程局部存儲TLS(線程級別全局變量,寄存器不會用來存儲須要的數據空間很寶貴)。公用:代碼,數據,進程空間,打開文件。
線程包含了表示進程內執行環境必需的信息,其中包括進程中標示線程的線程ID,一組寄存器值,棧,調度優先級和策略, 信號屏蔽字,errno變量以及線程私有數據。進程的全部信息對該進程的全部線程都是共享的,包括可執行的程序文本,程序的全局內存和堆內存,棧以及文件描述符,因此線程的mm_struct *mm指針變量和所屬進程的mm指針變量相同。
在建立線程的時候,能夠經過pthread_attr_t來初始化線程的屬性,包括線程的棧佈局信息,如棧起始地址stackaddr, 棧大小stacksize。線程棧的空間開闢在所屬進程的堆區,線程與其所屬的進程共享進程的用戶空間,因此線程棧之間能夠互訪。線程
系統調用:用戶態切換到內核態。用中斷實現
中斷號,中斷處理程序 一一對應,維護在中斷向量表。中斷號有限,通常用一個號(linux 是0x80)表示全部系統調用,再到系統調用表中根據系統調用號獲取指針
fork過程:
使用fork函數建立的子進程從父進程的繼承了所有進程的地址空間,包括:進程上下文、進程堆棧、內存信息、打開的文件描述符、信號控制設置、進程優先級、進程組號、當前工做目錄、根目錄、資源限制、控制終端等。
1.fork建立子進程,首先調用int80中斷,而後將系統調用號保存在eax寄存器中,進入內核態後調用do_fork(),
2.申請系統堆棧,將子進程pcb插入到隊列中(task_struct到內核棧?)
3.建立一份父進程的拷貝,他們的內存空間裏包含了徹底相同的內容,包括當前打開的資源,數據,固然也包含了程序運行到的位置,也就是說fork後子進程也是從fork函數的位置開始往下執行的,而不是從頭開始。
4.對子進程資源初始化
5.爲了判別當前正在運行的是哪一個進程,fork函數返回了一個pid,在父進程裏標識了子進程的id,在子進程裏其值爲0,在咱們的程序裏就根據這個值來分開父進程的代碼和子進程的代碼。
寫時複製:雖然父子是兩份,但只有改時纔會變兩份code
exec。當前進程替換爲新的elf(可執行文件)
若想並存:對象
#include <stdio.h> #include <unistd.h> int main(){ if(!fork()) execve("./test",NULL,NULL); else printf("origin process!\n"); return 0; }
elf爲程序編譯連接後的,加載到進程虛擬內存的棧task_struct、各類segment中。進程有本身的虛擬內存繼承