進程)
最近學了Linux軟件編程中進程的部分相關內容進程和線程這一部分定義比較多有的比較難以理解建議搞懂、多背定義。一、程序和進程很多人會把程序和進程弄混其實兩者很好區分。程序是存放在磁盤上的代碼文件屬于靜態的放在那里不會自己運行。進程是程序跑起來之后的動態過程包含創建、調度、消亡完整生命周期。程序運行就生成進程。每個進程都有自己獨一無二的標識 PIDPPID代表它父進程的ID用來描述進程之間的父子關系。二、進程常用命令寫代碼之前先要學會在終端觀察進程。top實時查看CPU占用看進程運行狀態ps -ef查看全部進程能看到PID、PPIDpstree以樹狀打印直觀看到父子進程關系ps -aux查看進程詳細運行狀態殺死進程kill -9 進程PIDkillall -9 進程名后臺運行程序在命令末尾加上./a.out jobs查看當前終端下后臺任務fg 編號把后臺任務切回前臺運行修改進程優先級nice -n 優先級 程序renice -n 優先級 進程PID三、進程虛擬內存空間32位系統每個進程擁有0?4G虛擬內存空間分成幾個段文本段存放我們寫的指令代碼數據段字符串常量、全局變量、靜態變量。分為初始化和未初始化兩部分系統數據段堆、棧。堆用于動態內存malloc棧保存局部變量、函數調用。關鍵點每個進程擁有獨立虛擬地址空間。但是全部進程共享0?4G虛擬內存由操作系統做分時復用。兩個進程看著地址一樣實際對應的物理內存不是同一塊。四、進程調度與進程狀態系統同時跑很多進程依靠調度算法分配CPU時間。常見調度算法先來先執行、高優先級調度、時間片輪轉、多級隊列反饋、負載均衡。時間片CPU分配給單個進程一小段執行時間。宏觀上多個程序同時運行微觀上CPU交替執行同一時刻CPU只跑一個任務。進程的7種狀態就緒態R任務已經準備好了等待分配CPU運行態R正在CPU上面執行可喚醒等待態S缺資源主動掛起收到信號可以被喚醒不可喚醒等待態D等待過程不能被信號喚醒停止態T進程被暫停僵尸態Z代碼已經執行完畢但是資源沒有被父進程回收結束態X資源已經回收進程徹底消失五、進程編程核心函數fork()pid_t fork(void);功能創建子進程。返回值規則一定要記牢父進程返回新創建子進程的PID大于0子進程返回0創建失敗返回?1調用fork之后會復制一份父進程地址空間父子兩個進程從fork之后的代碼分別往下執行。注意子進程不會從頭重新執行main從fork返回處繼續運行。如果子進程不寫return/exit退出子進程還會繼續執行后面的代碼甚至繼續fork生成大量多余子進程。getpid() / getppid()getpid() 獲取當前自己進程PIDgetppid() 獲取父進程PIDexit()void exit(int status);結束當前進程。main函數里面exit()和return效果基本一致普通函數內部return只是函數返回不會結束進程想要結束進程必須調用exit。ps、僵尸進程怎么產生如何避免產生原因子進程代碼執行結束但是父進程沒有回收子進程資源。子進程就變成僵尸進程ZPID還占著內存沒有釋放。解決辦法父進程先退出子進程變成孤兒進程會被系統init進程收養由init幫忙回收。父進程調用wait()主動回收子進程這是我們寫代碼最常用的方式。七、wait函數pid_t wait(int *wstatus);功能回收子進程資源具備阻塞特性。如果子進程還沒結束父進程會卡在wait這里原地等待。返回值成功返回被回收子進程PID失敗返回?1。參數wstatus用來保存子進程退出狀態配合一組宏使用WIFEXITED(wstatus) 判斷子進程是否正常退出WEXITSTATUS(wstatus) 獲取子進程退出返回值WIFSIGNALED(wstatus) 判斷進程是不是被信號殺死WTERMSIG(wstatus) 獲取殺死進程的信號編號八、waitpid函數我們知道子進程退出之后如果父進程不去回收它子進程就會變成僵尸進程仍然占用系統資源。waitpid就是用來回收指定子進程的系統調用。pid_t waitpid(pid_t pid, int *wstatus, int options);參數說明pid想要回收的子進程PIDwstatus指針用來保存子進程退出狀態信息傳NULL代表不關心退出狀態options選項0阻塞回收。子進程沒有退出父進程就卡在這一行一直等子進程結束。WNOHANG非阻塞回收。如果子進程還沒結束函數直接返回0父進程不會卡住可以繼續干別的事。返回值成功返回被回收的子進程pid失敗返回-1設置WNOHANG時如果子進程還沒有結束返回0wait只能隨便等待任意一個子進程waitpid可以精準指定回收哪一個子進程還能選擇阻塞或者非阻塞處理僵尸進程更加靈活。九、exec函數族進程內部執行另一套程序fork創建子進程之后子進程復制父進程的代碼。如果我們想要子進程去跑一個完全不一樣的程序就需要exec函數族。exec不會創建新進程它復用當前進程的PID直接把原來的代碼替換成新程序的代碼。執行成功之后后面的代碼全部不會再運行。一共有6個exec系列函數名字后綴有含義llist參數以列表形式逐個傳最后必須以NULL結尾vvector參數放到指針數組里面傳進去p會去系統環境變量PATH路徑里面查找程序文件不用寫完整路徑e可以自己傳入自定義環境變量int execl(const char *path, const chararg, …/(char *)NULL */);int execlp(const char *file, const chararg, …/(char *)NULL */);int execle(const charpath, const chararg, …/,(char)NULL, char *const envp[] */);int execv(const char *path, char *const argv[]);int execvp(const char *file, char *const argv[]);int execvpe(const char *file, char *const argv[], char *const envp[]);返回值特點exec執行成功沒有返回值原程序代碼直接被覆蓋只有執行失敗的時候才會返回?1繼續運行后面代碼。區分帶p不用寫完整路徑依靠PATH找程序不帶p必須寫程序完整路徑。帶l一個個羅列參數帶v把參數放到字符串數組。帶e可以自定義環境變量。十、環境變量環境變量就是進程運行時臨時存放配置信息的變量。比如我們經常見到的PATH就是環境變量系統靠它尋找可執行程序。環境變量是繼承關系父進程的環境變量默認會復制給子進程。exec執行新程序的時候默認沿用原有環境變量如果使用帶e后綴的exec函數可以傳入一套全新環境變量。setenv 修改環境變量int setenv(const char *name, const char *value, int overwrite);參數name環境變量名字value要設置的值overwrite是否覆蓋舊值非0如果這個變量已經存在覆蓋原來的值0已經存在則不做修改保留舊值返回值成功返回0失敗返回?1。這部分是進程編程的重點寫fork子進程調用exec執行外部程序搭配waitpid回收是非常經典的代碼模型。