Linux練級寶典->進程控制詳解(進程替換,fork函數)

目錄

進程創建

fork函數

寫時拷貝

進程終止?

進程退出碼

exit函數

_exit函數

return,exit _exit之間的區別和聯系

進程等待

進程等待的必要性

獲取子進程status

進程等待的方法

wait

waipid

多子進程創建理解

非阻塞輪詢檢測子進程

進程程序替換

替換原理

exec系列替換函數

命名理解

shell(linux指令解釋器)簡易版


進程創建

fork函數

我喜歡叫分叉函數,用處就是,在當前進程的基礎下,創建一個子進程。

返回值

返回值有三種:0? -1? 子進程的PID

返回值 == 0:此時說明當前進程是子進程。

返回值 == -1:說明子進程創建失敗。

返回值 == 子進程的PID:說明此時是父進程(因為父進程要管理子進程的退出)。

內核中fork函數:

  • 分配一塊內存創建子進程PCB,將其放入管理隊列中。
  • 父進程的內容拷貝到子進程。
  • 添加子進程到系統進程列表中。
  • fork返回,開始執行代碼。

我們發現在fork前的代碼執行了一次,后面的代碼子進程和父進程都執行了一次。

fork函數為什么能有兩種返回值?

?fork函數在內核中,給子進程分配空間并且管理起來。此時fork函數還沒結束的。它還有一個return語句:

此時在返回時二者都要進行返回,所以這個返回值就可以有兩個。?然后我們在PCB中是有保存PID和PPID的,此時就可以根據這個確認誰是子進程誰是父進程,此時就能控制返回值。

寫時拷貝

顧名思義:只有寫的時候才進行拷貝。

我們子進程和父進程是共享一塊空間的,為什么不直接給子進程創建新的空間,如果父子進程只有讀操作,重新開辟空間copy一份父進程的變量給子進程不是浪費嗎?

所以只有子進程和父進程需要更改變量時,才需要把父進程的變量拷貝一個新的變量給到子進程。

什么時候,代碼也會被替換?那就是程序替換的范疇了下面會說。

fork通常用于程序替換

?要和 exec函數等程序替換函數配合使用。

可以把子進程的執行邏輯直接切換成別的程序。但是父進程依然可以對子進程進行管理(守護進程)。

進程終止?

進程退出三種情況:

1.代碼運行完畢,結果正確。

2.代碼運行完畢,結果不正確。

3.代碼異常終止(進程崩潰)。

進程退出碼

我們先了解下:main函數到底是什么?

main函數在我們寫程序中開來就是一切的起源。但是他是怎么被啟動的。

VS2013中main函數被一個叫做__tmainCRTStartup的函數調用。而這個函數又被操作系統通過加載器調用。這就是說main函數也是被系統操作的。

通常我們main函數的return值是0,而這個0,其實就是一個退出碼。

0退出碼代表執行成功,0以外都代表了執行錯誤

?我們知道C語言中有個函數可以獲取錯誤代碼的信息 strerror(數字)。

我們編寫一個這樣的代碼

執行后?

?我們發現只有0是success,其他都是一些錯誤表示,所以退出碼的選擇。

可以用echo $??查看上一次程序的退出碼是什么。

exit函數

exit函數就是用來用來退出程序的。并且有三步:

1.執行用戶定義的atexit清理函數。

2.關閉這個進程打開的所有流,所有的緩存數據都被寫入。

3.調用_exit函數終止進程。

所有數據都會被寫入相對的流重點,才會退出。

_exit函數

_exit函數就是 exit函數的退化版,因為exit函數已經包含了_exit。因為_exit不會做任何處理,直接關閉進程。

?這個代碼執行結果就是:沒有任何輸出,我們的printf是先寫到對應屏幕的緩沖區,此時_exit在還沒寫入時就把程序關了,所以就沒有任何輸出結果。

return,exit _exit之間的區別和聯系

return通常都只是返回,但是在main函數中返回即終止主進程,整個代碼完結。exit和_exit就是在子函數中強制退出進程用的。

下圖是exit和_exit的區別。

進程等待

進程等待的必要性

1.僵尸進程:如果父進程一直不讀取子進程退出信息,子進程就變成僵尸進程了,內存泄漏

2.僵尸進程無法被主動刪除。

3.對于進程來說,最關心自己的就是父進程,因為父進程需要知道子進程的任務完成情況。

4.父進程通過進程等待的方式,回收子進程資源,獲取對應的退出信息。

獲取子進程status

這是一個wait和waitpid中的一個參數,這個參數是輸入輸出型(傳入后,在函數中被更改后傳出)。如果這個參數給到NULL則表示不關心子進程退出情況。

status是一個整型變量,但是status其實作用就是一個類似位圖的運用。

?這里只研究低16位。后八位代表終止信號,即退出碼。前7位代表的終止信號,即信號碼,第八位的core dump標志。

?正常來說退出后只有退出狀態被設置,剩下的都沒設置,而終止后core dump也會被設置。

exitCode = (status >> 8) & 0xFF; //退出碼
exitSignal = status & 0x7F;      //退出信號

通過位運算就能取出對應的碼和信號值

進程等待的方法

wait

函數原型:pid_t wait(int * status);

作用:等待任意子進程

返回值:成功則返回對應的子進程pid,失敗返回-1

參數:status,獲取狀態碼。不關心就設置為NULL

waipid

函數原型:pid_t waitpid(pid_t pid, int* status, int options);

作用:等待指定子進程或任意子進程。

返回值:
1、等待成功返回被等待進程的pid。
2、如果設置了選項WNOHANG,而調用中waitpid發現沒有已退出的子進程可收集,則返回0。
3、如果調用中出錯,則返回-1,這時errno會被設置成相應的值以指示錯誤所在。

參數:
1、pid:待等待子進程的pid,若設置為-1,則等待任意子進程。
2、status:輸出型參數,獲取子進程的退出狀態,不關心可設置為NULL。
3、options:當設置為WNOHANG時,若等待的子進程沒有結束,則waitpid函數直接返回0,不予以等待。若正常結束,則返回該子進程的pid

WNOHANG:設置以后,本來就是子進程不退出,父進程就一直卡著不動,如果設置了子進程沒返回,那就先返回0給父進程,此時給父進程在一個循環里持續獲取子進程退出信息,再循環體里父進程就可以繼續做自己的事情

多子進程創建理解

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{pid_t ids[10];for (int i = 0; i < 10; i++){pid_t id = fork();if (id == 0){//childprintf("child process created successfully...PID:%d\n", getpid());sleep(3);exit(i); //將子進程的退出碼設置為該子進程PID在數組ids中的下標}//fatherids[i] = id;}for (int i = 0; i < 10; i++){int status = 0;pid_t ret = waitpid(ids[i], &status, 0);if (ret >= 0){//wait child successprintf("wiat child success..PID:%d\n", ids[i]);if (WIFEXITED(status)){//exit normalprintf("exit code:%d\n", WEXITSTATUS(status));}else{//signal killedprintf("killed by signal %d\n", status & 0x7F);}}}return 0;
}

這個代碼就是用來創建了十個子進程,然后把10個子進程的的pid放到數組里,之后再讓父進程for循環等待子進程結束。

?可以看到我們上面的進程創建出來好,父進程是一個個等待結束的。

那能不能讓父進程在沒子進程退出時在執行一套自己的邏輯呢?

非阻塞輪詢檢測子進程

這里就要用到之前說的WNOHANG:WAIT NO HANG,就是說等待不會被掛起的意思。

此時我們給waitpid函數加上WNOHANG。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{pid_t id = fork();if (id == 0){//childint count = 3;while (count--){printf("child do something...PID:%d, PPID:%d\n", getpid(), getppid());sleep(3);}exit(0);}//fatherwhile (1){int status = 0;pid_t ret = waitpid(id, &status, WNOHANG);if (ret > 0){printf("wait child success...\n");printf("exit code:%d\n", WEXITSTATUS(status));break;}else if (ret == 0){printf("father do other things...\n");sleep(1);}else{printf("waitpid error...\n");break;}}return 0;
}

此時發現我們父進程除了等待外,還有其他的判斷語句此時就是父進程在等待時能做其他事情的意思

進程程序替換

替換原理

本來子進程和父進程執行的是同一個代碼,只是可以用if else 分支,但是怎么讓子進程直接去執行另一個程序呢?

如上圖,在我們創建好子進程后,將其與磁盤中的程序進行替換。?

進程替換時,有沒有新的進程創建

都叫進程替換了,那就說明了就是把子進程替換了而已,只是這個新的程序依然可以被父進程管理。pid,進程地址空間,頁表都沒變,只有代碼和數據變了。

那會發生寫時拷貝嗎?

當然,這里的整個 子進程的代碼數據都變了,所以和父進程就發生了寫時拷貝,此時就不會影響父進程的數據了

exec系列替換函數

總共有6種exec系列函數

一、int execl(const char *path, const char *arg, ...);

第一個參數是程序路徑,第二個可變參數列表,表示的就是這個程序的選項,怎么執行這個程序。以NULL結尾

execl("/usr/bin/ls", "ls", "-a", "-i", "-l", NULL);

?上述代碼就是:ls程序的執行。

二、int execlp(const char *file, const char *arg, ...);

第一個參數是要執行程序的名字,和環境變量搭配使用。?第二個和上面一樣代表怎么執行這個程序

execlp("ls", "ls", "-a", "-i", "-l", NULL);

三、int execle(const char *path, const char *arg, ..., char *const envp[]);

?第一個參數是地址,第二個參數還是老樣子,第三個參數是自己想設置的環境變量。

char* myenvp[] = { "MYVAL=2021", NULL };
execle("./mycmd", "mycmd", NULL, myenvp);

如上代碼:執行后,在環境變量中就多了一項 MYVAL,此時這個VAL就能被直接使用。在一些程序中,可以配置一些環境變量,便于使用。

四、int execv(const char *path, char *const argv[]);

?第一個參數是要執行代碼的地址,第二個就是一個字符指針(字符串)數組,只是把上面的在參數里傳遞,改編成了傳遞一個字符串數組。(一樣要以NULL結束)

char* myargv[] = { "ls", "-a", "-i", "-l", NULL };
execv("/usr/bin/ls", myargv);

五、int execvp(const char *file, char *const argv[]);

?第一個參數是要執行的程序的名字,第二個參數還是執行程序的選項的字符串數組。

char* myargv[] = { "ls", "-a", "-i", "-l", NULL };
execvp("ls", myargv);

六、int execve(const char *path, char *const argv[], char *const envp[]);

?第一個參數是路徑,第二個參數是選項和執行內容,第三個參數是要設置的環境變量。

char* myargv[] = { "mycmd", NULL };
char* myenvp[] = { "MYVAL=2021", NULL };
execve("./mycmd", myargv, myenvp);

上述全部函數成功調用,則直接執行新的程序,不再返回。

失敗則返回-1.

命名理解

我們發現exec系列的函數,開頭都是exec只是后面帶的剩余字母不同:

l(list):表示參數采用列表的形式,可變參數列表。

v(vector):采用數組的形式。

p(path):表示自動搜索環境變量PATH,進行相應程序的查找。

e(env):表示可以傳入自己設置的環境變量。

只有execve是系統調用,其它5個函數最終都是調用execve。?

shell(linux指令解釋器)簡易版

這里制作的shell采用父子進程制作,即bash(shell)接收命令,然后創建子進程然后把子進程的邏輯替換為對應指令即可

#include <stdio.h>
#include <pwd.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#define LEN 1024 //命令最大長度
#define NUM 32 //命令拆分后的最大個數
int main()
{char cmd[LEN]; //存儲命令char* myargv[NUM]; //存儲命令拆分后的結果char hostname[32]; //主機名char pwd[128]; //當前目錄while (1){//獲取命令提示信息struct passwd* pass = getpwuid(getuid());gethostname(hostname, sizeof(hostname)-1);getcwd(pwd, sizeof(pwd)-1);int len = strlen(pwd);char* p = pwd + len - 1;while (*p != '/'){p--;}p++;//打印命令提示信息printf("[%s@%s %s]$ ", pass->pw_name, hostname, p);//讀取命令fgets(cmd, LEN, stdin);cmd[strlen(cmd) - 1] = '\0';//拆分命令myargv[0] = strtok(cmd, " ");int i = 1;while (myargv[i] = strtok(NULL, " ")){i++;}pid_t id = fork(); //創建子進程執行命令if (id == 0){//childexecvp(myargv[0], myargv); //child進行程序替換exit(1); //替換失敗的退出碼設置為1}//shellint status = 0;pid_t ret = waitpid(id, &status, 0); //shell等待child退出if (ret > 0){printf("exit code:%d\n", WEXITSTATUS(status)); //打印child的退出碼}}return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/71887.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/71887.shtml
英文地址,請注明出處:http://en.pswp.cn/web/71887.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

RabbitMq--消息可靠性

12.消息可靠性 1.消息丟失的情況 生產者向消息代理傳遞消息的過程中&#xff0c;消息丟失了消息代理&#xff08; RabbitMQ &#xff09;把消息弄丟了消費者把消息弄丟了 那怎么保證消息的可靠性呢&#xff0c;我們可以從消息丟失的情況入手——從生產者、消息代理&#xff0…

Windows中在VSCode/Cursor上通過CMake或launch文件配置CUDA編程環境

前置步驟 安裝符合GPU型號的CUDA Toolkit 配置好 nvcc 環境變量 安裝 Visual Studio 參考https://blog.csdn.net/Cony_14/article/details/137510909 VSCode 安裝插件 Nsight Visual Studio Code Edition 注意&#xff1a;不是vscode-cudacpp。若兩個插件同時安裝&#xff0c;…

Spark(8)配置Hadoop集群環境-使用腳本命令實現集群文件同步

一.hadoop的運行模式 二.scp命令————基本使用 三.scp命令———拓展使用 四.rsync遠程同步 五.xsync腳本集群之間的同步 一.hadoop的運行模式 hadoop一共有如下三種運行方式&#xff1a; 1. 本地運行。數據存儲在linux本地&#xff0c;測試偶爾用一下。我們上一節課使用…

聚焦兩會:科技與發展并進,賽逸展2025成創新新舞臺

在十四屆全國人大三次會議和全國政協十四屆三次會議期間&#xff0c;代表委員們圍繞多個關鍵議題展開深入討論&#xff0c;為國家未來發展謀篇布局。其中&#xff0c;技術競爭加劇與經濟轉型需求成為兩會焦點&#xff0c;將在首都北京舉辦的2025第七屆亞洲消費電子技術貿易展&a…

【音視頻】ffmpeg命令提取像素格式

1、提取YUV數據 提取yuv數據&#xff0c;并保持分辨率與原視頻一致 使用-pix_fmt或-pixel_format指定yuv格式提取數據&#xff0c;并保持原來的分辨率 ffmpeg -i music.mp4 -t "01:00" -pixel_format yuv420p music.yuv提取成功后&#xff0c;可以使用ffplay指定y…

【從零開始學習計算機科學】計算機體系結構(二)指令級并行(ILP)

【從零開始學習計算機科學】【從零開始學習計算機科學】計算機體系結構(二)指令級并行(ILP) ILP流水線(pipeline)流水線調度循環展開和循環流水循環展開。循環展開的具體步驟可以描述為,軟件流水(循環流水)。我們可以通過流水線的思想處理循環的執行,即不需要這一次的…

android edittext 防止輸入多個小數點或負號

有些英文系統的輸入法,或者定制輸入法。使用xml限制不了輸入多個小數點和多個負號。所以代碼來控制。 一、通過XML設置限制 <EditTextandroid:id="@+id/editTextNumber"android:layout_width="wrap_content"android:layout_height="wrap_conten…

2019年藍橋杯第十屆CC++大學B組真題及代碼

目錄 1A&#xff1a;組隊&#xff08;填空5分_手算&#xff09; 2B&#xff1a;年號字符&#xff08;填空5分_進制&#xff09; 3C&#xff1a;數列求值&#xff08;填空10分_枚舉&#xff09; 4D&#xff1a;數的分解&#xff08;填空10分&#xff09; 5E&#xff1a;迷宮…

從C#中的MemberwiseClone()淺拷貝說起

MemberwiseClone() 是 C# 中的一個方法&#xff0c;用于創建當前對象的淺拷貝&#xff08;shallow copy&#xff09;。它屬于 System.Object 類&#xff0c;因此所有 C# 對象都可以調用該方法。 1. MemberwiseClone() 的含義 淺拷貝&#xff1a;MemberwiseClone() 會創建一個新…

筆記六:單鏈表鏈表介紹與模擬實現

在他一生中&#xff0c;從來沒有人能夠像你們這樣&#xff0c;以他的視角看待這個世界。 ---------《尋找天堂》 目錄 文章目錄 一、什么是鏈表&#xff1f; 二、為什么要使用鏈表&#xff1f; 三、 單鏈表介紹與使用 3.1 單鏈表 3.1.1 創建單鏈表節點 3.1.2 單鏈表的頭插、…

尚硅谷爬蟲note15n

1. 多條管道 多條管道開啟&#xff08;2步&#xff09;&#xff1a; (1)定義管道類 &#xff08;2&#xff09;在settings中開啟管道 在pipelines中&#xff1a; import urllib.request # 多條管道開啟 #(1)定義管道類 #&#xff08;2&#xff09;在setti…

oracle檢查字段為空

在Oracle數據庫中&#xff0c;檢查字段是否為空通常涉及到使用IS NULL條件。如果你想查詢某個表中的字段是否為空&#xff0c;你可以使用SELECT語句結合WHERE子句來實現。這里有一些基本示例來展示如何進行這樣的查詢。 示例1: 檢查單個字段是否為空 假設你有一個表employees…

虛幻基礎:動畫層接口

文章目錄 動畫層&#xff1a;動畫圖表中的函數接口&#xff1a;名字&#xff0c;沒有實現。動畫層接口&#xff1a;由動畫藍圖實現1.動畫層可直接調用實現功能2.動畫層接口必須安裝3.動畫層默認使用本身實現4.動畫層也可使用其他動畫藍圖實現&#xff0c;但必須在角色藍圖中關聯…

HarmonyOS學習第18天:多媒體功能全解析

一、開篇引入 在當今數字化時代&#xff0c;多媒體已經深度融入我們的日常生活。無論是在工作中通過視頻會議進行溝通協作&#xff0c;還是在學習時借助在線課程的音頻講解加深理解&#xff0c;亦或是在休閑時光用手機播放音樂放松身心、觀看視頻打發時間&#xff0c;多媒體功…

緒論數據結構基本概念(刷題筆記)

&#xff08;一&#xff09;單選題 1.與數據元素本身的形式、相對位置和個數無關的是&#xff08;B&#xff09;【廣東工業大學2019年829數據結構】 A.數據存儲結構 B.數據邏輯結構 C.算法 D.操作 2.在數據結構的討論中把數據結構從邏輯上分為&#xff08;C&#xff09;【中國…

GPTQ - 生成式預訓練 Transformer 的精確訓練后壓縮

GPTQ - 生成式預訓練 Transformer 的精確訓練后壓縮 flyfish 曾經是 https://github.com/AutoGPTQ/AutoGPTQ 現在是https://github.com/ModelCloud/GPTQModel 對應論文是 《Accurate Post-Training Quantization for Generative Pre-trained Transformers》 生成式預訓練Tr…

git的使用方法

文章目錄 前言git簡介GIT的基本操作克隆倉庫 (Clone)獲取最新代碼 (Pull)提交代碼到遠程倉庫查看當前分支查看提交代碼的日志git config 配置用戶信息 GIT的實操 前言 git是一種軟件版本管理工具&#xff0c;在多人團隊軟件開發中地方非常重要。 類似與SVN&#xff0c;git工具…

php虛擬站點提示No input file specified時的問題及權限處理方法

訪問站點&#xff0c;提示如下 No input file specified. 可能是文件權限有問題&#xff0c;也可能是“.user.ini”文件路徑沒有配置對&#xff0c;最簡單的辦法就是直接將它刪除掉&#xff0c;還有就是將它設置正確 #配置成自己服務器上正確的路徑 open_basedir/mnt/qiy/te…

使用Langflow和AstraDB構建AI助手:從架構設計到與NocoBase的集成

本文由 Leandro Martins 編寫&#xff0c;最初發布于 Building an AI Assistant with Langflow and AstraDB: From Architecture to Integration with NocoBase。 引言 本文的目標是演示如何創建一個集成了 NocoBase、LangFlow 和 VectorDB 工具的 AI 助手。作為基礎&#xf…

6.聊天室環境安裝 - Ubuntu22.04 - elasticsearch(es)的安裝和使用

目錄 介紹安裝安裝kibana安裝ES客戶端使用 介紹 Elasticsearch&#xff0c; 簡稱 ES&#xff0c;它是個開源分布式搜索引擎&#xff0c;它的特點有&#xff1a;分布式&#xff0c;零配置&#xff0c;自動發現&#xff0c;索引自動分片&#xff0c;索引副本機制&#xff0c;res…