一.關于頁表組成
1.權限(rwx)
作用:如1.讓代碼區變成只讀的 2.寫時拷貝的實現:子進程創建時其頁表指向的父進程代碼和數據權限都是只讀的,子進程試圖修改,觸發錯誤,系統開始寫時拷貝。
來源:1.可執行程序本身已經包含了區域權限信息,代碼區不可寫等
2.真實地址是否存在的位(isexits)
作用:節省內存空間:
1.分批加載::系統創建進程先加載進程對應磁盤內一部分數據,將頁表記錄位記錄不存在,等進程使用這部分虛擬地址對應的數據時,再從磁盤加載到指定物理地址位置。2.掛起操作。
二.關于mm_struct初始化時怎么分配各區域的大小
1.可執行程序在編譯時,各個區域已經確定,直接給mm_struct初始化,(程序內申請空間實質是擴大程序記錄的堆的大小(也就是虛擬空間的大小),給mm_struct,操作系統在需要用的時候再開辟空間),執行時mm_struct給操作系統實時開辟
三.進程地址空間(上)
再談創建進程:
寫時拷貝
是什么:創建子進程,子進程實際指向的代碼和數據和父進程相同,修改時再拷貝,互不干擾。
實現:創建子進程時,頁表所有位置權限位設為只讀,一旦子進程和父進程任意一方對共同數據進行寫入,觸發缺頁中斷,系統監測是不是需要寫時拷貝,是,將數據拷貝一份,修改頁表指向,修改權限。互不干擾。
再談進程終止
1.進程正常退出:看進程退出碼判斷運行結果是否正常(return 0,return -1...)
2.進程意外終止:OS提前用信號終止了進程(訪問非法等)。看退出信號判斷情況:退出碼為0則沒收到信號,退出信號判斷進程是否提前退出了,提前退出原因是什么(kill就是給進程信號,退出進程),正常退出了看退出結果是否正確看退出碼。
再談進程等待:
回收等待的進程:子進程結束了,就要將退出信號和退出碼存入PCB并等待,直到父進程回收
回收系統調用:
pid_t wait(int *status);進程一直等待,直到有它的子進程退出,
接收退出狀況status(若不接受可傳入空指針),返回退出進程的pid,失敗時返回?-1
,并設置?errno
?以指示錯誤原因。
pid_t waitpid(pid_t pid,int *status, int options);進程等待一次,若沒退出
pid為要等待的子進程(傳入-1為任意),options為0為阻塞等待,WNOHANG非阻塞等待
status:
兩個判斷status情況的宏:bool WIFEXITED(int status)如果進程正常退出則返回真
int WEXITSTATUS(int status).如果正常退出,提取退出碼
進程替換
不是創建新進程,是在原進程的基礎上替換新的可執行程序的代碼和數據(堆棧重新初始化),PCB不換!
創建一個已有可執行程序為基礎的進程,可以先創建一個子進程,再進程替換(bash也是這么做的,只需要全部寫時拷貝再替換)
補充:
1.readelf -s 可執行程序名
看可執行程序的各個區域大小
2.野指針實質是頁表內沒有對應映射,操作系統不給訪問,運行錯誤
3.為什么要用虛擬地址空間
-->1.虛擬地址空間+頁表保護內存
--->2.讓進程以統一視角看物理內存:加載各個區里的物理地址不必連續,虛擬地址連續,進程不必分別管理一塊一塊。作用:讓代碼數據可以加載到內存的任意地址處,頁表映射尋找效率高O(1)。
---->3.進程管理和內存管理在系統層面解耦合:進程創建只需創建數據結構,其余的代碼加載,進程需要的內存操作(申請空間),操作系統可以先騙過進程,等到使用的時候再真進行內存操作。兩者具有滯后性,可以更好的省內存,省資源。
4.環境變量為什么一直能被進程看到
進程虛擬地址空間內就有環境變量表
5.1. 打印錯誤的代碼
#include <errno.h>
變量int errno最近函數的錯誤碼,(依賴于最近的函數內是否設置了錯誤碼,fopen,fork都設置了)
#include <string.h>
?char *strerror(int errnum);根據錯誤碼返回錯誤原因字符串
(標準C庫的功能)
5.2. 退出代碼
#include<cstdlib>
void exit(int i);讓進程直接退出,通過正常退出機制終止進程,設置退出碼i。且刷新緩沖區(緩沖區是語言級別的概念,語言編譯了維護,不是操作系統的)
_exit(int i);系統調用,退出不刷新緩沖區
exit語言封裝了_exit。
6.數據段(Data Segment)?中的數據在?進程創建之前?就已經存在了。具體來說,這些數據是在?程序編譯和鏈接時?確定的,并存儲在可執行文件中。當程序啟動時,操作系統會加載可執行文件,并將數據段的內容映射到進程的地址空間中。