Linux(9)——進程(控制篇——下)

目錄

三、進程等待

1)進程等待的必要性

2)獲取子進程的status

3)進程的等待方法

wait方法

waitpid方法?

多進程創建以及等待的代碼模型??

非阻塞的輪訓檢測

四、進程程序替換

1)替換原理

2)替換函數

3)函數解釋?

4)命名理解?


三、進程等待

1)進程等待的必要性

  1. 之前提過子進程退出,父進程如果不讀取子進程的退出信息,就可能造成“僵尸進程”的問題,從而造成內存泄漏的問題。
  2. 再者,一旦子進程進入了僵尸狀態,那就連kill -9都殺不亖他,因為沒有誰能夠殺亖一個死去的進程。
  3. 最后,父進程創建子進程是要獲取子進程的完成任務的情況的。
  4. 父進程需要通過等待的方式來回收子進程的資源,獲取子進程的退出信息。

2)獲取子進程的status

下面進程等待使用的兩個方法wait方法和waitpid方法都有一個status參數,這是一個輸出型參數(輸出型參數是函數中用于返回結果或修改調用者變量的參數,通常通過引用或指針實現。如void func(int *output)。),由操作系統進行填寫。

如果向status中傳遞的是NULL,那就表示用戶不關心子進程的退出狀態。否則,操作系統會根據該參數,將子進程的退出信息反饋給父進程。

status不能簡單的當作整形來看待,可以當作位圖來看待,具體細節如下圖(只研究status低16比特位):

我們從圖中可見,status的低16比特位中,高8位表示進程的退出狀態,即退出碼。當進程被信息殺亖時,則低7位表示終止信息,第8位時core dump標志。

我們可以通一系列的位操作來得出進程的退出碼和退出信號。

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

對于這兩個操作,系統提供了兩宏來獲取退出碼以及退出信號。分別是:

  • WIFEXITED(status):用于查看是否是正常退出,本質是檢查是否收到信號。
  • WEXITSTATUS(status):用于獲取進程的退出碼。
exitNormal = WIFEXITED(status); //是否正常退出exitCode = WEXITSTATUS(status); //獲取退出碼

敲黑板:

當一個進程是非正常退出的時候,那么該進程的退出碼將毫無意義。

3)進程的等待方法

wait方法

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

返回值:成功返回被等待進程pid,失敗返回-1。

參數:輸出型參數,獲取子進程退出狀態,不關心則可以設置成為NULL?

作用:等待任意子進程?

創建子進程后,父進程使用wait方法等待子進程,直到子進程的退出信息被讀取,我們可以寫個代碼驗證一下:

#include <stdio.h>    
#include <stdlib.h>    
#include <unistd.h>    
#include <sys/wait.h>    
#include <sys/types.h>    int main()    
{    pid_t id = fork();    if(id == 0){ //子進程    int count = 10;    while(count--)    {    printf("我是子進程,PID:%d, PPID:%d\n", getpid(), getppid());    sleep(1);    }    exit(0);    }    //父進程                                                                                                                                                               int status = 0;    pid_t ret = wait(&status);    if(ret > 0){    printf("等待成功...\n");    if(WIFEXITED(status)){    printf("退出碼:%d\n", WEXITSTATUS(status));    }    }    sleep(3);    return 0;    
}

然后我們可以在開一個會話用來監控進程的狀態:

while :; do ps axj | head -1 && ps axj | grep test | grep -v grep;echo "============================================================";sleep 1;done

?在下面這圖中我們可以看到,當子進程退出,父進程讀取到了子進程的退出信息時,子進程就不會變成僵尸狀態了。

waitpid方法?

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

返回值:

  1. 當正常返回的時候waitpid返回收集到的子進程的進程ID;
  2. 如果設置了選項WNOHANG(option),而調用中waitpid發現沒有已退出的子進程可收集,則返回0;
  3. 如果調用中出錯,則返回-1,這時errno會被設置成相應的值以指示錯誤所在;?

參數:

  1. pid:當pid=-1,等待任意一個子進程,與wait等效。當pid>0.等待其進程ID與pid相等的子進程。
  2. status:輸出型參數,用來獲取子進程的退出狀態,不關心可以設置成NULL。
  3. options:默認為0,表示阻塞等待;當設置為WNOHANG時,若pid指定的子進程沒有結束,則waitpid()函數返回0,不予以等待。若正常結束,則返回該子進程的ID。

返回值:等待任意子進程(可以指定)退出?

我們可以寫個代碼來驗證一下,創建子進程后,父進程可以使用waitpid函數一直等待子進程,直到子進程退出后讀取子進程的退出信息。

#include <stdio.h>    
#include <stdlib.h>    
#include <unistd.h>    
#include <sys/wait.h>    
#include <sys/types.h>    int main()    
{    pid_t id = fork();    if(id == 0){ //子進程    int count = 10;    while(count--)    {    printf("我是子進程,PID:%d, PPID:%d\n", getpid(), getppid());    sleep(1);    }    exit(0);    }    //父進程    int status = 0;    //pid_t ret = wait(&status);    pid_t ret = waitpid(id, &status, 0);    if(ret >= 0){    printf("等待成功...\n");    if(WIFEXITED(status)){    printf("退出碼:%d\n", WEXITSTATUS(status));    }else{    printf("被信號殺?:%d\n",status & 0x7F);    }                                                                            }    sleep(3);    return 0;    
}

在父進程運行過程中,我們可以使用kill -9命令來將子進程殺亖,這個時候父進程也能成功等待子進程。

敲黑板:

被信號殺亖的進程的退出碼是沒有意義的。

多進程創建以及等待的代碼模型??

上面演示的都是父進程的創建以及等待一個子進程,那么接下來我們可以同時創建多個子進程,然后讓父進程進程依次等待子進程退出。

下面我們可以同時創建10個子進程,將子進程的pid放到一個id數組中,并將這10個子進程的退出時的退出碼設置為該子進程pid對應數組中的下標,之后父進程使用waitpid等待這10個子進程。

#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){    printf("子進程創建成功:PID:%d\n", getpid());    sleep(3);    exit(i);    }    ids[i] = id;    }    for(int i = 0; i < 10; i++){    int status = 0;    pid_t ret = waitpid(ids[i], &status, 0);    if(ret >= 0){    printf("等待子進程成功:PID:%d\n", ids[i]);    if(WIFEXITED(status)){    printf("退出碼:%d\n", WEXITSTATUS(status));    }    else{    printf("被信號殺亖:%d\n", status & 0x7F);    }    }    }                                                                                                                                                                               return 0;    
} 

運行完代碼,我發現父進程同時創建了了多個子進程,當子進程退出后,父進程再以此讀取這些子進程的退出信息。?

殺亖進程,父進程依然可以獲取子進程的退出信息。

非阻塞的輪訓檢測

上面的方案,其實是有缺點的,那就是在父進程等待子進程的時候,父進程什么也干不了,這樣的等待就是阻塞等待。

而實際上,我們是可以讓我們的父進程在等待子進程退出的過程中做一些自己的事情的,這樣的等待就是非阻塞等待了。

其實想要實現非阻塞等待也很簡單,我們在上面說waitpid時就提過了,在想這個函數第三個參數傳入WNOHANG時就可以使得在等待子進程時,如果waitpid函數直接返回0,就不予等待,而等待的子進程若是正常結束,就返回該子進程的pid。

比如,父進程可以選擇地調用wait函數,若是等待的子進程還沒有退出,那么父進程就可以先去做一些其他的事情了,過一段時間在調用waitpid函數讀取子進程的退出信息。

#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){    int count = 3;    while(count--){    printf("子進程在運行:PID:%d, PPID:%d\n", getpid(), getppid());    sleep(3);    }    exit(0);    }    while(1){    int status = 0;    pid_t ret = waitpid(id, &status, WNOHANG);    if(ret > 0){    printf("等待子進程成功。\n");    printf("退出碼是:%d\n", WEXITSTATUS(status));    break;    }    else if(ret == 0){    printf("父進程做了其他事情。\n");    sleep(1);    }    else{    printf("等待錯誤。\n");    break;    }    }    return 0;                                                                          
}

代碼的運行結果就是,父進程每隔一段時間就去看看子進程是否退出,如果沒就去做自己的事情,知道子進程退出后讀取子進程退出的信息。

?

四、進程程序替換

1)替換原理

用fork創建子進程后執行的是和父進程相同的程序(但有可能執行不同的代碼分支),子進程往往要調用一種exec函數以執行另一個程序。當進程調用一種exec函數時,該進程的用戶空間代碼和數據完全被新程序替換,從新程序的啟動例程開始執行。調用exec并不創建新進程,所以調用exec前后該進程的id并未改變

?回答兩個關于進程程序替換的文題:

問題一:

當進行進程替換時,有沒有創建新的進程呢?

進程程序替換之后,該進程的PCB、進程的地址空間以及頁表等數據結構都沒有發生改變,而只是對原來在物理內存上的代碼和數據進行了替換,所有根本沒有創建新的進程,而且進程的程序替換前后其pid也是沒有改變的。

問題二:

子進程進行進程的程序替換時,是否會影響父進程的代碼和數據呢?

不會影響,一開始子進程被父進程創建時代碼和數據是和父進程共享的,但是一旦子進程要進行程序替換操作,就意味著子進程需要對代碼和數據進行寫入操作了,這時就需要對父進程的代碼和數據進行拷貝了(寫時拷貝),從這里開始子進程和父進程的代碼和數據就分離了,所有子進程進行進程的程序替換時不會影響父進程的代碼和數據。

2)替換函數

這里的替換函數都是以exec開頭的,所以稱之為exec函數,總共有六種:

1??int execl(const char *path,const char *arg,...);?

相關參數的說明:第一參數是要執行程序的路徑,第二個參數是可變參數列表,表示具體如何執行這個程序,同時以NULL結尾。

寫一個執行ls程序:

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

2??int execlp(const char *file, const char *arg,...);?

相關參數的說明:第一個參數是要執行程序的名字,第二個參數是可變參數列表,表示你要如何執行,也是以NULL結尾。

寫一個執行ls程序:

execle("ls", "ls", "-l", NULL); 

3??int execle(const char *path,?const char *arg,..., char *const envp[]);?

相關參數的說明:第一個參數是要執行的程序的路徑,第二個參數是可變參數列表,表示如何執行這個程序,也是以NULL結尾。第三個參數是自己設置的環境變量。

這里我們可以設置MYVAL的環境變量,在test中可以使用這個環境變量了。

char* myenp[] = {"MYVAL=2025", NULL};
execle("./test", "test", NULL, myenvp);

4??int execv(const char *path, char *const argv]);?

相關參數的說明:第一個參數是要執行程序的路徑,第二個參數是一個指針數組,數組中的就是你要如何執行這個程序,數組也是以NULL結尾的。

寫一個ls程序:

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

5??int execvp(const char *file,char *const argv[]);

相關參數的說明:第一個是要執行的程序的名字,第二個參數是一個指針數組,?數組中的就是你要如何執行這個程序,數組也是以NULL結尾的。

寫一個ls程序:

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

6??int execve(const char *path,? char *const argvl],? char *const envp[]);

相關參數的說明:?第一個參數是要執行程序的路徑,第二個參數是一個指針數組,數組中的就是你要如何執行這個程序,數組也是以NULL結尾的,第三個參數是自己設置的環境變量。

這里我們可以設置MYVAL的環境變量,在test中可以使用這個環境變量了。

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

3)函數解釋?

  1. 這些函數如果調用成功則加載新的程序從啟動代碼開始執行,不再返回。
  2. 如果調用出錯則返回-1

所以exec函數只有出錯的返回值而沒有成功的返回值。

4)命名理解?

其實我們仔細觀察就會發現這六個函數都是有規律的,只要掌握了規律是很好記的。

  1. l(list):表示參數采用列表的形式
  2. v(vector):參數用數組的形式
  3. p(path):有p自動搜索環境變量PATH
  4. e(env):表示自己維護環境變量
函數名參數格式是否帶路徑是否使用當前環境變量
execl列表不是
execlp列表
execle列表不是不是,須自己組裝環境變量
execv數組不是
execvp數組
execve數組不是不是,須自己組裝環境變量

事實上,我們打開man手冊就可以發現execve在man手冊第2節其它函數在man手冊第3節。也就是 只有execve是真正的系統調用,其它五個函數最終都調用execve。

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

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

相關文章

Datatable和實體集合互轉

1.使用已廢棄的 JavaScriptSerializer&#xff0c;且反序列化為弱類型 ArrayList。可用但不推薦。 using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Linq; using System.Reflection; using System.Web; using Sy…

阿里云服務器ECS詳解:云服務器是什么,云服務器優勢和應用場景及參考

云服務器ECS是阿里云眾多云產品中&#xff0c;最受用戶關注的產品&#xff0c;阿里云服務器提供多樣化的計算能力&#xff0c;支持x86、Arm架構&#xff0c;涵蓋CPU、GPU等多種服務器類型&#xff0c;滿足各種用戶需求。其便捷易用特性包括分鐘級交付、通用API和性能監控框架&a…

【Oracle】游標

個人主頁&#xff1a;Guiat 歸屬專欄&#xff1a;Oracle 文章目錄 1. 游標基礎概述1.1 游標的概念與作用1.2 游標的生命周期1.3 游標的分類 2. 顯式游標2.1 顯式游標的基本語法2.1.1 聲明游標2.1.2 帶參數的游標 2.2 游標的基本操作2.2.1 完整的游標操作示例 2.3 游標屬性2.3.1…

pikachu靶場通關筆記11 XSS關卡07-XSS之關鍵字過濾繞過(三種方法滲透)

目錄 一、源碼分析 1、進入靶場 2、代碼審計 3、攻擊思路 二、滲透實戰 1、探測過濾信息 2、注入Payload1 3、注入Payload2 4、注入Payload3 本系列為通過《pikachu靶場通關筆記》的XSS關卡(共10關&#xff09;滲透集合&#xff0c;通過對XSS關卡源碼的代碼審計找到安…

XML 元素:基礎、應用與優化

XML 元素:基礎、應用與優化 引言 XML(可擴展標記語言)作為一種數據交換的標準格式,廣泛應用于互聯網數據交換、數據存儲等領域。XML 元素是 XML 文檔的核心組成部分,本文將深入探討 XML 元素的概念、特性、應用以及優化方法。 一、XML 元素概述 1.1 XML 元素的定義 X…

【Axure高保真原型】交通事故大屏可視化分析案例

今天和大家分享交通事故大屏可視化分析案例的原型模板&#xff0c;包括餅圖分類分析、動態顯示發生數、柱狀圖趨勢分析、中部地圖展示最新事故發現地點和其他信息、右側列表記錄發生事故的信息…… 通過多種可視化圖表展示分析結果&#xff0c;具體效果可以點擊下方視頻觀看或…

HCIP(BGP基礎)

一、BGP 基礎概念 1. 網絡分類與協議定位 IGP&#xff08;內部網關協議&#xff09;&#xff1a;用于自治系統&#xff08;AS&#xff09;內部路由&#xff0c;如 RIP、OSPF、EIGRP&#xff0c;關注選路效率、收斂速度和資源占用。EGP&#xff08;外部網關協議&#xff09;&a…

【HarmonyOS 5】 ArkUI-X開發中的常見問題及解決方案

一、跨平臺編譯與適配問題 1. 平臺特定API不兼容 ?問題現象?&#xff1a;使用Router模塊的replaceUrl或startAbility等鴻蒙專屬API時&#xff0c;編譯跨平臺工程報錯cant support crossplatform application。 ?解決方案?&#xff1a; 改用ohos.router的跨平臺封裝API&a…

Matlab2018a---安裝教程

目錄 壹 | 引 言 貳 | 安裝環境 叁 | 安 裝 肆 | 結 語 壹 | 引 言 大家好&#xff0c;我是子正。 最近想學習一下DSP數字信號處理有關的知識&#xff0c;要用到Matlab進行數據處理&#xff0c;于是又重新把Matlab撿了回來; 記得上學那會兒用的還是Matlab2012a&#xff…

分布式流處理與消息傳遞——Kafka ISR(In-Sync Replicas)算法深度解析

Java Kafka ISR&#xff08;In-Sync Replicas&#xff09;算法深度解析 一、ISR核心原理 #mermaid-svg-OQtnaUGNQ9PMgbW0 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-OQtnaUGNQ9PMgbW0 .error-icon{fill:#55222…

ARM GIC V3概述

中斷類型 locality- specific peripheral interrupt&#xff08;LPI&#xff09;&#xff1a;LPI是一個有針對性的外設中斷&#xff0c;通過affinity路由到特定的PE。 為非安全group1中斷邊沿觸發可以通過its進行路由沒有active狀態&#xff0c;所以不需要明確的停用操作LPI總…

藍橋杯國賽訓練 day1

目錄 k倍區間 舞獅 交換瓶子 k倍區間 取模后算組合數就行 import java.util.HashMap; import java.util.Map; import java.util.Scanner;public class Main {static Scanner sc new Scanner(System.in);public static void main(String[] args) {solve();}public static vo…

安裝和配置 Nginx 和 Mysql —— 一步一步配置 Ubuntu Server 的 NodeJS 服務器詳細實錄6

前言 昨天更新了四篇博客&#xff0c;我們順利的 安裝了 ubuntu server 服務器&#xff0c;并且配置好了 ssh 免密登錄服務器&#xff0c;安裝好了 服務器常用軟件安裝, 配置好了 zsh 和 vim 以及 通過 NVM 安裝好Nodejs&#xff0c;還有PNPM包管理工具 。 作為服務器的運行…

鴻蒙版Taro 搭建開發環境

鴻蒙版Taro 搭建開發環境 一、配置鴻蒙環境 下載安裝 DevEco 建議使用最新版本的 IDE&#xff0c;當前為 5.0.5Release 版本。 二、創建鴻蒙項目 打開 DevEco&#xff0c;點擊右上角的 Create Project&#xff0c;在 Application 處選擇 Empty Ability&#xff0c;點擊 Ne…

Could not get unknown property ‘mUser‘ for Credentials [username: null]

最近遇到jekins打包報錯&#xff1a; Could not get unknown property mUser for Credentials [username: null] of type org.gradle.internal.credentials.DefaultPasswordCredentials_Decorated。 項目使用的是gradle&#xff0c;通過pipeline打docker包&#xff1b;因為ma…

Spring Boot + MyBatis-Plus 讀寫分離與多 Slave 負載均衡示例

Spring Boot + MyBatis-Plus 讀寫分離與多 Slave 負載均衡示例 一、項目結構 src/main/java/com/example/demo/ ├── config/ │ ├── DataSourceConfig.java # 數據源配置 │ ├── MyBatisPlusConfig.java # MyBatis-Plus配置 ├── constant/ │…

android binder(1)基本原理

一、IPC 進程間通信&#xff08;IPC&#xff0c;Inter-Process Communication&#xff09;機制&#xff0c;用于解決不同進程間的數據交互問題。 不同進程之間用戶地址空間的變量和函數是不能相互訪問的&#xff0c;但是不同進程的內核地址空間是相同和共享的&#xff0c;我們可…

高密爆炸警鐘長鳴:AI為化工安全戴上“智能護盾”

一、高密爆炸&#xff1a;一聲巨響&#xff0c;撕開化工安全“傷疤” 2025年5月27日&#xff0c;山東高密友道化學有限公司的車間爆炸聲&#xff0c;像一把利刃劃破了化工行業的平靜。劇烈的沖擊波將車間夷為平地&#xff0c;黑色蘑菇云騰空而起&#xff0c;刺鼻的化學氣味彌漫…

雙擎驅動:華為云數字人與DeepSeek大模型的智能交互升級方案

一、技術融合概述 華為云數字人 華為云數字人&#xff0c;全稱&#xff1a;數字內容生產線 MetaStudio。數字內容生產線&#xff0c;提供數字人視頻制作、視頻直播、智能交互、企業代言等多種服務能力&#xff0c;使能千行百業降本增效。另外&#xff0c;數字內容生產線&#…

Linux運維筆記:1010實驗室電腦資源規范使用指南

文章目錄 一. 檢查資源使用情況&#xff0c;避免沖突1. 檢查在線用戶2. 檢查 CPU 使用情況3. 檢查 GPU 使用情況4. 協作建議 二. 備份重要文件和數據三. 定期清理硬盤空間四. 退出 ThinLinc 時注銷&#xff0c;釋放內存五. 校外使用時配置 VPN注意事項 總結 實驗室的電腦配備了…