凡科網(wǎng)做網(wǎng)站網(wǎng)站策劃報(bào)告
文章目錄
- 一.子進(jìn)程的創(chuàng)建
- 操作系統(tǒng)內(nèi)核視角下的父子進(jìn)程存在形式
- 驗(yàn)證子進(jìn)程對(duì)父進(jìn)程數(shù)據(jù)的寫(xiě)時(shí)拷貝
- 二.進(jìn)程等待
- 進(jìn)程非阻塞等待示例:
- 三.進(jìn)程替換
- 內(nèi)核視角下的進(jìn)程替換過(guò)程:
- 綜合利用進(jìn)程控制系統(tǒng)接口實(shí)現(xiàn)簡(jiǎn)單的shell進(jìn)程
進(jìn)程控制主要分為三個(gè)方面,分別是:子進(jìn)程的創(chuàng)建,進(jìn)程等待,進(jìn)程替換
一.子進(jìn)程的創(chuàng)建
- 父進(jìn)程調(diào)用
fork()
系統(tǒng)接口創(chuàng)建子進(jìn)程后,操作系統(tǒng)會(huì)為子進(jìn)程創(chuàng)建獨(dú)立的PCB
結(jié)構(gòu)體和虛擬地址空間mm_struct
,因此父子進(jìn)程之間具有互相獨(dú)立性
操作系統(tǒng)內(nèi)核視角下的父子進(jìn)程存在形式
- 父進(jìn)程調(diào)用
fork()
函數(shù)之后:
驗(yàn)證子進(jìn)程對(duì)父進(jìn)程數(shù)據(jù)的寫(xiě)時(shí)拷貝
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main(int argc,const char * argv[])
{//創(chuàng)建子進(jìn)程pid_t id = fork();if(id < 0){//創(chuàng)建子進(jìn)程失敗,退出主函數(shù)perror("fork");return 0;}else if(id == 0) {//子進(jìn)程執(zhí)行流//子進(jìn)程對(duì)變量g_val進(jìn)行修改,引發(fā)寫(xiě)時(shí)拷貝g_val=100;printf("childProcess[%d]: %d : %p\n", getpid(), g_val, &g_val);}else{//父進(jìn)程的執(zhí)行流//父進(jìn)程先休眠3秒,讓子進(jìn)程先完成g_val變量的寫(xiě)入sleep(3);printf("parentProcess[%d]: %d : %p\n", getpid(), g_val, &g_val);}sleep(1);return 0;
}
- 執(zhí)行結(jié)果:
- 內(nèi)核視角下,代碼段中的寫(xiě)時(shí)拷貝圖解:
- 寫(xiě)時(shí)拷貝保證了父子進(jìn)程之間互相獨(dú)立的同時(shí)提高了計(jì)算機(jī)整機(jī)的內(nèi)存使用效率
二.進(jìn)程等待
- 子進(jìn)程退出后會(huì)進(jìn)入僵尸狀態(tài),僵尸狀態(tài)進(jìn)程的內(nèi)核數(shù)據(jù)結(jié)構(gòu)對(duì)象會(huì)保留在操作系統(tǒng)內(nèi)核空間中,在父進(jìn)程讀取子進(jìn)程的退出信息后,操作系統(tǒng)才會(huì)釋放掉子進(jìn)程所占用的所有系統(tǒng)資源
- 若父進(jìn)程比子進(jìn)程先退出,那么操作系統(tǒng)就會(huì)接管子進(jìn)程(成為孤兒進(jìn)程),子進(jìn)程退出后,操作系統(tǒng)會(huì)自動(dòng)讀取子進(jìn)程的退出信息.
- 父進(jìn)程讀取子進(jìn)程的退出信息這一過(guò)程稱(chēng)為進(jìn)程等待
- 進(jìn)程等待常用系統(tǒng)接口:
int waitpid(int pid, int *status, int options);
-
形參
int pid
: -
形參
int *status
:用于記錄進(jìn)程退出碼和退出信號(hào)的輸出型參數(shù) -
形參
int options
表示進(jìn)程等待的方式:主要分為阻塞等待和非阻塞等待兩大類(lèi):- 當(dāng)
option
為0
時(shí):進(jìn)程執(zhí)行阻塞等待,此時(shí)進(jìn)程會(huì)進(jìn)入阻塞狀態(tài)(PCB
退出運(yùn)行隊(duì)列,進(jìn)入阻塞隊(duì)列),直到其所等待的子進(jìn)程退出,該進(jìn)程才會(huì)重新進(jìn)入運(yùn)行狀態(tài)讀取子進(jìn)程的退出信息 - 當(dāng)
option
為非零時(shí)(比如使用系統(tǒng)宏WNOHANG
):進(jìn)程執(zhí)行非阻塞等待, 此時(shí)進(jìn)程會(huì)嘗試讀取其子進(jìn)程的退出信息,若沒(méi)能讀取到子進(jìn)程的退出信息,則waitpid
系統(tǒng)接口立即返回0
- 當(dāng)
-
返回值
int
:若waitpid
系統(tǒng)接口讀取到子進(jìn)程的退出信息,返回子進(jìn)程的pid
,若waitpid
執(zhí)行過(guò)程中出現(xiàn)錯(cuò)誤,則返回-1
-
進(jìn)程非阻塞等待示例:
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>int main()
{int pid = 0;//創(chuàng)建子進(jìn)程pid = fork();if(pid == -1){//子進(jìn)程創(chuàng)建失敗printf("fork failed\n");return -1;}else if(pid == 0){//子進(jìn)程的代碼執(zhí)行流int cnt = 5;while(cnt--){sleep(1);printf("child process running\n");}exit(11);}else{//父進(jìn)程的代碼執(zhí)行流//非阻塞等待子進(jìn)程int status = 0;bool quit = 0;while(quit == 0){//循環(huán)非阻塞等待子進(jìn)程int res = waitpid(pid,&status,WNOHANG);if(res > 0){//子進(jìn)程已退出printf("waiting pid success,chilProc exit,退出碼:%d\n",WEXITSTATUS(status));quit = 1;}else if(res == 0){//子進(jìn)程還未退出printf("waiting pid success,childProc still running\n");}else{//等待發(fā)生錯(cuò)誤printf("waiting pid failed\n");}sleep(1);}}return 0;
}
三.進(jìn)程替換
- 進(jìn)程替換的概念:通過(guò)特定的系統(tǒng)調(diào)用接口,操作系統(tǒng)可以將進(jìn)程當(dāng)前執(zhí)行的代碼段替換成指定系統(tǒng)路徑下其他可執(zhí)行程序的代碼段,然后根據(jù)進(jìn)程從頭開(kāi)始執(zhí)行新的代碼段(因此需要通過(guò)進(jìn)程替換系統(tǒng)接口為新代碼段傳入
main
函數(shù)命令行參數(shù))
內(nèi)核視角下的進(jìn)程替換過(guò)程:
- 可見(jiàn)進(jìn)程替換并不是創(chuàng)建新的進(jìn)程(進(jìn)程的
PCB
和虛擬內(nèi)存結(jié)構(gòu)體不變) - 進(jìn)程替換系統(tǒng)接口:
- 形參和返回值統(tǒng)一解釋:
- 參數(shù)
const char*path
:表示將要替換現(xiàn)有代碼段的目標(biāo)可執(zhí)行程序的完整路徑 - 參數(shù)
const char*file
:表示將要替換現(xiàn)有代碼段的目標(biāo)可執(zhí)行程序的文件名,其系統(tǒng)路徑由環(huán)境變量PATH
決定 - 參數(shù)
const char*arg,...
:表示傳給新代碼段main
函數(shù)命令行參數(shù)的字符串,...
表示可變參數(shù)列表,可以傳入多個(gè)字符串,最后一個(gè)字符串需傳入NULL
- 參數(shù)
char *const argv[]
:表示傳給新代碼段main
函數(shù)命令行參數(shù)的字符串?dāng)?shù)組,數(shù)組中最后一個(gè)字符串需傳入NULL
- 參數(shù)
cahr*const envp[]
:表示傳遞給新代碼段的環(huán)境變量字符串?dāng)?shù)組 - 當(dāng)進(jìn)程替換失敗時(shí),
exec
系列系統(tǒng)接口會(huì)返回-1
- 參數(shù)
綜合利用進(jìn)程控制系統(tǒng)接口實(shí)現(xiàn)簡(jiǎn)單的shell進(jìn)程
shell
進(jìn)程的運(yùn)行原理:shell
進(jìn)程接收到用戶(hù)輸入的指令后,對(duì)指令進(jìn)行格式化處理,然后創(chuàng)建子進(jìn)程,子進(jìn)程通過(guò)exec
系列系統(tǒng)接口將自身替換成系統(tǒng)命令并執(zhí)行以響應(yīng)用戶(hù)需求,shell
進(jìn)程的這種運(yùn)行機(jī)制保證了自身的進(jìn)程安全(子進(jìn)程出現(xiàn)錯(cuò)誤不會(huì)影響到父進(jìn)程的運(yùn)行)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <memory.h>//用戶(hù)輸入的命令行的最大長(zhǎng)度
#define CStrLen 1024
//解析命令行得到的格式化字符串?dāng)?shù)組
#define SStrLen 50
//命令行字符串
char CommStr[CStrLen];
//格式化命令行字符串?dāng)?shù)組
char* StdStr[SStrLen];//命令行分隔符
#define Sep " "
//操作系統(tǒng)配置的環(huán)境變量
extern char ** environ;
int main()
{while(1) {printf("[我的命令行解釋器 myshell]$ ");fflush(stdout);memset(CommStr,'\0',sizeof StdStr);//獲取用戶(hù)輸入命令if(fgets(CommStr,sizeof CommStr,stdin)== NULL){continue;}CommStr[strlen(CommStr)-1] = '\0';StdStr[0] = strtok(CommStr,Sep);//根據(jù)空格對(duì)用戶(hù)輸入的字符串進(jìn)行分割并存入StdStr字符串?dāng)?shù)組中int i = 1;while(StdStr[i++] = strtok(NULL,Sep));//部分命令(比如cd命令)需要由shell進(jìn)程自己來(lái)執(zhí)行if(strcmp(StdStr[0],"cd")== 0){//用chdir函數(shù)改變shell進(jìn)程的工作路徑if(StdStr[1] != NULL){chdir(StdStr[1]);}continue;}//shell創(chuàng)建子進(jìn)程來(lái)執(zhí)行系統(tǒng)命令int pid = fork();if(pid == 0){//printf("shell的子進(jìn)程執(zhí)行系統(tǒng)命令:\n");execvp(StdStr[0],StdStr);printf("-mybash: %s: command execute failed\n",StdStr[0]);exit(-1);}else{int status;//父進(jìn)程進(jìn)行阻塞等待,等待子進(jìn)程執(zhí)行完系統(tǒng)命令結(jié)束并獲取其退出碼int waitres = waitpid(pid,&status,0);if(waitres == -1){printf("waitchild process failed\n");}}}return 0;
}