目錄
1.進程創建
1.1fork()函數初識
1.2寫時拷貝
1.?提升系統效率
2.?隔離錯誤影響
3.?支持并行計算
2.進程終止:
2.1進程退出場景:
2.2進程常見退出方法:
2.3_exit()系統調用接口
2.4exit函數
2.5return退出
1.進程創建
1.1fork()函數初識
在linux中fork函數是非常重要的函數,它從已存在進程中創建一個新進程。新進程為子進程,而原就進程為父進程
#include<unistd.h>
pid_t fork(void);
返回值:子進程返回0,父進程返回子進程的pid,創建失敗返回-1
進程調用fork,當同志轉移到內核中的fork代碼后,內核做:
- 分配新的內存塊和內核數據結構給子進程
- 將父進程部分數據結構內容拷貝至子進程
- 添加子進程到系統進程列表當中
- fork返回,開始調度器調度
當一個進程調用fork之后,就有兩個二進制代碼相同的進程。而且它們都運行到相同的地方。但每個進程都將可以開始它們自己的旅程,看如下程序:
##include<unistd.h>
#include<stdio.h>
#include <sys/types.h>
#include<stdlib.h>
int main()
{pid_t pid;printf("Before: pid is %d\n", getpid());if((pid=fork())==-1){perror("fork()");exit(1);}printf("After:pid is %d, fork return %d\n",getpid(),pid);sleep(1);return 0;
}
這里看到了三行代碼,一行befor,進程28791先打印before消息,然后它有打印after。另一個after消息有28791打印的。注意到進程28791沒有打印before,為什么呢?如下圖
所以,fork之前父進程獨立執行,fork之后,父子兩個執行流分別執行。注意,fork之后,誰先執行完全由調度器決定。
(小擴展:在Linux操作系統中,調度器是一個負責分配CPU資源給不同進程的子系統。調度器的主要作用是根據不同的調度算法,決定哪個進程可以獲得CPU時間片,以實現進程間的公平競爭和高效利用CPU資源。Linux操作系統中有多種調度器可供選擇,如CFS (Completely Fair Scheduler)、O(1)調度器等。通過調度器的工作,Linux操作系統能夠有效地管理進程的運行順序,提高系統性能和響應速度.)
1.2寫時拷貝
了解寫時拷貝之前,我們得先知道,為什么要在一個進程之中,創建一個子進程?子進程的作用是什么?
1.?提升系統效率
當父進程需要完成多個獨立的任務時,它可以創建若干個子進程分別執行這些任務。這種方式使得父進程無需等待某個任務完成后才能繼續下一個任務,而是可以在等待期間執行其他工作或者進入休眠狀態以節省資源。
2.?隔離錯誤影響
子進程與父進程之間存在一定的隔離機制。即使子進程中出現了致命錯誤(如段錯誤),通常也不會直接影響到父進程及其正常運行流程。這種設計有助于增強整個程序架構的安全性和穩定性。
3.?支持并行計算
現代計算機硬件大多具備多核處理器能力,利用子進程可以讓不同CPU核心同時處理不同的數據集或算法部分,進而充分利用硬件資源達到加速目的。
所以:子進程需要與執行與父進程不同的代碼段。例如:父進程等待客戶端申請,生成子進程來處理請求。
于是,我們就需要寫時拷貝。
通常、父子代碼共享,父子再不寫入時,數據也是共享的,當任意一方試圖寫入,便以寫時拷貝的方式各自一份副本。
2.進程終止:
2.1進程退出場景:
- 代碼運行完畢,結果正確
- 代碼運行完畢,結果不正確
- 代碼異常終止
1、2中,統一會采用進程的退出碼進行判定結果是否正確。
進程退出碼有什么作用?(一般而言,父進程會關心子進程的完成情況,所以需要子進程退出碼來判斷子進程完成情況)
2.2進程常見退出方法:
正常終止(可以通過echo $? 查看進程退出碼---最后一次進程的退出碼):
- 從main返回
- 調用exit
- _exit
main函數的返回值,本質表示:進程運行完成時是否是正常的結果,如果不是,可以用不同的數字,表示不同的出錯原因
異常退出:ctrl + c,信號終止
進程出現異常,本質是我們的進程收到了對應的信號
2.3_exit()系統調用接口
參數:status定義了進程的終止狀態,父進程通過wait來獲取該值
說明:雖然status是int,但是僅有低8位可以被父進程所用。所以_exit(-1)時,在終端執行$?發現返回值是255(下一章--進程等待會詳細講關于status的存儲)
2.4exit函數
#include<unistd.h>
void exit(int status);
exit最后也會調用_exit,但在調用_exit之前,還做了其他工作
- 執行用戶通過atexit或on_exit定義的清理函數
- 關閉所有打開的流,所有的緩存數據均被寫入
- 調用_exit
#include <stdio.h>
#include <stdlib.h>void cleanup_function(){printf("Cleanup function called.\n");
}int main(){atexit(cleanup_function);puts("Before calling exit.");exit(0); // This will call the registered 'cleanup_function'
}
#include <stdio.h>
#include <unistd.h> int main(void){char buffer[]="This message may be lost\n";fwrite(buffer , sizeof(char), strlen(buffer)-1 , stdout );_exit(0); // No flushing occurs here so output might get discarded.
}
2.5return退出
return是一種更常見的退出進程的方法。執行return n等同于執行exit(n),因為調用main的運行時函數會將main的返回值當作exit參數