(八)Linux進程程序替換

1 進程替換

??進程替換是為了讓程序能在不創建新進程的情況下,讓父進程和子進程執行不同的代碼,以實現控制清晰、執行高效的程序調度機制。

1.1 先看效果

#include <stdio.h>
#include <unistd.h> int main()
{printf("before:I am a process, pid:%d,ppid:%d\n",getpid(),getppid()); // 標準寫法  execl("/usr/bin/ls", "ls", "-a", "-l", NULL);// 想要執行程序的路徑   怎么執行這個命令  最后必須NULL結尾printf("after:I am a process, pid:%d,ppid:%d\n",getpid(),getppid());return 0;
}

在這里插入圖片描述

??我們會發現這里并沒有if else但是子進程在execl后卻沒有執行父進程的代碼,這說明子進程所執行的代碼被替換了!! 這就是發生了進程替換!

1.2 進程替換的原理

??在使用 fork() 創建進程時,我們經常看到父進程和子進程執行的是不同的邏輯,但卻沒有使用 if/else 來區分函數入口。這是如何做到的?要理解這一點,就得先回答下面五個問題。

問題1:子進程執行了 ls 這個程序,是不是創建了一個新的子進程?

  • 答:不是的,子進程在執行 ls 的過程中,并不會再創建一個新的進程。相反,它會調用 exec 系列函數(如 execl、execvp 等),這類函數的作用是:將當前進程的代碼和數據空間完全替換為另一個可執行程序的內容(比如 ls 的代碼和數據)。這一過程由操作系統完成,原來的用戶態執行內容被新程序接管。雖然進程內容變了,但進程的 PID 不變,內核中的 PCB(進程控制塊)結構仍然保留,只是部分字段(如指令入口地址、頁表等)發生了更新。(就是寫時拷貝)

問題 2:既然子進程的內容被替換了,為什么父進程沒有受到影響?

  • 答:這是因為 Linux 在 fork() 創建進程時采用了寫時拷貝技術。簡單來說:起初父子進程共享相同的內存頁面(包括代碼段和數據段),但在其中一個進程嘗試修改內存時,操作系統才會為它單獨分配新頁面,實現真正的物理內存拷貝。當子進程執行 exec 系列函數時,它會重新加載目標程序的代碼和數據,觸發寫時拷貝,從而不影響父進程的內存空間。這樣,父進程可以繼續執行自己原來的代碼,而子進程則運行被替換的新程序。(就有點像你的第二人格出現,但是你已經不記得自己的第一人格做過什么或者說過什么)

問題 3:我們常說寫時拷貝作用于數據段,那代碼段也可以寫時拷貝嗎?

  • 答:可以,代碼段同樣可以觸發寫時拷貝,盡管它通常是只讀的。這是因為:操作系統不相信任何人,通常情況下,用戶程序無法修改代碼段,但 exec 是一種特殊情況,它是由操作系統內核完成的程序替換操作;因此操作系統有權限回收原有代碼段并重新加載新的代碼段(如 ls),實現了用一份全新的代碼段替換舊的的效果。這并不意味著代碼段可以隨意修改,而是說在 exec 的語義中,代碼段可以被替換。

問題 4:如果進程替換失敗了會怎樣?

  • 答:如果替換失敗了,就只能執行自己原先的代碼了!!所以exec系列的函數只有失敗的返回值而沒有成功的返回值,因為一但成功后跑的就是新的代碼和數據了,返回就沒有意義了。

問題 5:我們說 main 是程序入口,但它不一定寫在文件開頭,Linux 是如何找到它的?

  • 答: Linux中的可執行程序,是有自己的組織形式的,也就是有自己的格式的(有一張表),我們把這個格式叫做ELF ,ELF 文件中包含一個頭部結構體,里面記錄了程序的各個段的起始地址,其中就有一個字段叫做 e_entry,它指向程序的真實入口地址,操作系統加載可執行文件時,根據 e_entry 所指的地址啟動執行,從而精確找到程序入口,無需掃描整個文件內容。

1.3 探究各個程序替換的接口

在這里插入圖片描述

函數名參數類型是否搜索 PATH可否傳 envp使用方式
execlpath + 可變參數? 否? 否寫死路徑 + 手動寫參數
execlpfile + 可變參數? 是? 否像終端命令一樣寫
execlepath + 可變參數? 否? 是自定義環境變量
execvpath + argv[]? 否? 否參數數組(變量較多時)
execvpfile + argv[]? 是? 否動態命令調用
execvepath + argv[] + envp[]? 否? 是最底層、最靈活的調用

1.3.1 execl:路徑+變長參數(手動列出參數)

#include <unistd.h>
#include <stdio.h>// int execl(const char *path, const char *arg, ...);int main() 
{// 什么路徑下,執行什么程序// 使用完整路徑,執行 ls -l -a,最后一個一定是NULL結尾execl("/bin/ls", "ls", "-l", "-a", NULL);perror("execl failed");return 1;
}

1.3.2 execlp:文件名+變長參數(自動按 PATH 搜索)

#include <unistd.h>
#include <stdio.h>// int execlp(const char *file, const char *arg, ...);int main() 
{// 自動在 PATH 中找 ls,等同于直接在終端輸入 ls -l -aexeclp("ls", "ls", "-l", "-a", NULL);perror("execlp failed");return 1;
}

1.3.3 execv:路徑+參數數組

#include <unistd.h>
#include <stdio.h>// int execv(const char *path, char *const argv[]);int main()
{char *args[] = {"ls", "-l", "-a", NULL};execv("/bin/ls", args);perror("execv failed");return 1;
}

1.3.4 execvp:文件名+參數數組(PATH 搜索)

#include <unistd.h>
#include <stdio.h>// int execvp(const char *file, char *const argv[]);int main()
{char *args[] = {"ls", "-l", "-a", NULL};execvp("ls", args);perror("execvp failed");return 1;
}

execle/execvpe:多個一個envp[ ] 意思就是我們可以自己用一套自己的環境變量,而不是用從父進程繼承下來的。 (后面補充!!!)

1.4 接口總結和加載器理解

??在 Linux 中,exec 系列函數雖然有多個變種,但它們的本質區別只在于參數的形式和功能的側重點不同。這些不同的接口設計,實際上是圍繞以下幾個核心問題展開的:

(1)程序在哪里?——執行目標的位置問題

  • 進程替換的第一步是定位目標程序的位置。為此,exec 系列提供了兩種方式:
    • 明確路徑:如 execl、execv 等函數要求你傳入程序的完整路徑(如 /bin/ls)。
    • 自動搜索:如 execlp、execvp 則只需傳入程序名,系統會自動從 PATH 環境變量中查找對應的可執行文件。

(2)參數如何傳?——執行參數的組織方式問題

  • 找到程序之后,下一個問題是:我們需要為它傳遞哪些參數?如何傳遞?為此,exec 系列函數支持兩種參數傳遞形式:
    • 可變參數列表(如 execl, execlp, execle):適合參數數量固定、較少的情況,手動列出每個參數,最后以 NULL 結尾。
    • 參數數組形式(如 execv, execvp, execve):適合動態構造參數列表,將參數統一組織在 char *argv[] 中傳入。

(3)是否使用自定義環境變量?——環境隔離問題

  • 最后一個核心問題是:新執行的程序是否必須繼承當前進程的環境變量?
    • 默認繼承:大部分 exec 函數(如 execl, execvp, execv 等)會沿用當前進程的環境變量。
    • 自定義傳入:而以 e 結尾的函數(如 execle, execve)允許你顯式傳入一組新的環境變量數組 envp[],實現更高的控制力和隔離性

??總結:exec 系列函數的多樣設計,其實是從“程序在哪、參數怎么傳、環境用誰的”這三個角度出發,對進程替換這一行為進行了細粒度的接口劃分,滿足了不同場景下的執行需求。

1.5 makefile一次生成兩個可執行文件

補充知識:.cc .cpp .cxx 都是C++中的文件后綴

test1.c文件

#include <stdio.h>int main()
{printf("hello HYQ\n");printf("hello HYQ\n");printf("hello HYQ\n");printf("hello HYQ\n");return 0;
}

test2.cpp文件

#include <iostream>using namespace std;int main()
{printf("Hello C++ Linux\n");printf("Hello C++ Linux\n");printf("Hello C++ Linux\n");printf("Hello C++ Linux\n");return 0;
}

makefile文件

test1:test1.cgcc -o $@ $^
test2:test2.cppg++ -o $@ $^
.PHONY:clean
clean:rm -f test1 test2

??上面的makefile文件只能生成一個可執行文件,因為它一旦掃描到一個推導鏈,就會立馬執行,執行一個推導鏈就結束了,不會去執行第二個。
??因此,如果我想生成兩個可執行文件,就必須讓這兩個程序有一個關系,才可能一次執行兩個文件。
??唯一的解決方法就是,誰都不要放前面,而是提前建立一個偽目標all放在前面,多一層推導關系,這樣兩個文件就會根據推導鏈的執行而被編譯了。

.PHONY:all
all:test1 test2  // 不能有縮進test1:test1.cgcc -o $@ $^   // 一定是tab縮進,空格會出問題
test2:test2.cppg++ -o $@ $^
.PHONY:clean
clean:rm -f test1 test2

一個可執行程序能不能調另一個可執行程序了???答案是:可以的!!!

int main()
{printf("hello HYQ\n");printf("hello HYQ\n");printf("hello HYQ\n");printf("hello HYQ\n");// 第一次參數:什么路徑執行什么東西// 第二個參數:怎么執行// 第三個參數:一定是NULL結尾execl("./test2","test2",NULL);printf("HYQ\n");return 0;
}

??所以語言和語言之間是可以相互調用的!!任何語言都有像exec這類的接口,語言可以互相調用的原因是無論什么語言寫的程序在操作系統看來都是進程。

??補充關于環境變量:環境變量是在子進程創建的時候就默認繼承了,即使沒有傳環境變變量參數,也可以在地址空間找到。所以進程替換中,環境變量信息不會被替換

1.6 總結進程替換系列的函數

  • 這些函數如果調用成功則加載新的程序從啟動代碼開始執行,不再返回。
  • 如果調用出錯則返回-1
  • 所以exec函數只有出錯的返回值而沒有成功的返回值
  • l(list) : 表示參數采用列表
  • v(vector) : 參數用數組
  • p(path) : 有p自動搜索環境變量PATH
  • e(env) : 表示自己維護環境變量

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

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

相關文章

支持 TDengine 的數據庫管理工具—qStudio

qStudio qStudio 是一款免費的多平臺 SQL 數據分析工具&#xff0c;可以輕松瀏覽數據庫中的表、變量、函數和配置設置。最新版本 qStudio 內嵌支持 TDengine。 前置條件? 使用 qStudio 連接 TDengine 需要以下幾方面的準備工作。 安裝 qStudio。qStudio 支持主流操作系統包…

破解 VMP+OLLVM 混淆:通過 Hook jstring 快速定位加密算法入口

版權歸作者所有&#xff0c;如有轉發&#xff0c;請注明文章出處&#xff1a;https://cyrus-studio.github.io/blog/ VMP 殼 OLLVM 的加密算法 某電商APP的加密算法經過dex脫殼分析&#xff0c;找到參數加密的方法在 DuHelper.doWork 中 package com.shizhuang.duapp.common…

Automatisch:開源的工作流自動化利器

在當今數字化的時代,企業和個人都在尋找高效的方式來自動化業務流程,減少手動操作帶來的時間和成本消耗。Automatisch 作為一款開源的 Zapier 替代方案,為我們提供了一個強大而靈活的工具,讓工作流自動化變得更加簡單和可控。 一、Automatisch 簡介 Automatisch 是一個商…

RAG應用效果評估框架與優化指南

1. 引言:為何RAG評估至關重要? 一個RAG系統通常包含多個可調參數和可替換組件(如不同的嵌入模型、向量數據庫、LLM、Prompt模板等)。沒有有效的評估機制,優化過程就像“盲人摸象”,難以判斷改動是否帶來了真正的提升。 RAG評估的核心目的: 量化系統性能:將RAG的“好壞…

豆包大模型應用場景

豆包作為通用大模型&#xff0c;應用場景其實覆蓋了個人和企業兩端。個人端要突出生活化功能——比如幫學生解題、幫上班族寫周報&#xff1b;企業端則要強調降本增效&#xff0c;比如客服自動化、代碼生成這些硬需求。用戶沒指定角度&#xff0c;那就都覆蓋吧。 注意到用戶用“…

OSITCP/IP

模型&協議 在互聯網發展的早期,不同的計算機廠商有不同的網絡傳輸協議,例如:IBM的SNA協議、蘋果的AppleTalk協議等,這些協議互不兼容,導致雖然不同的產商計算機在物理層面是鏈接的,但是在網絡上基本無法完成正常通信。這就導致一個用戶如果使用了某個廠商的某個網絡…

店匠科技閃耀“跨博會”,技術+生態打造靈活出海能力

2025年6月16日至18日&#xff0c;第八屆全球跨境電商節暨第十屆深圳國際跨境電商貿易博覽會&#xff08;簡稱“跨博會”&#xff09;在深圳會展中心舉行。作為全球跨境電商行業的年度盛會&#xff0c;本屆展會以“文化跨境、品牌出海、智量強國”為主題&#xff0c;匯聚近 1500…

selenium彈框元素定位-凍結界面

有些網站上面的元素&#xff0c;我們鼠標放在上面&#xff0c;會動態彈出一些內容。 但是當我們的鼠標從音樂圖標移開&#xff0c;這個欄目就整個消失了&#xff0c;就沒法查看其對應的HTML。 怎么辦&#xff1f;在開發者工具欄console里面執行如下js代碼 &#xff1a; setTi…

美學心得(第二百七十九集)羅國正

美學心得&#xff08;第二百七十九集&#xff09; 羅國正 &#xff08;2025年6月&#xff09; 3299、分清不同本體、主體及其之間的關系&#xff0c;是 正確的審美、判斷首先的關鍵 羅國正 &#xff08;2025年6月11日于廣州&#xff09; “人也按照美的規律來建造。”這句話…

云祺容災備份系統公有云備份與恢復實操-AWS

1、創建訪問密鑰 訪問并登錄AWS控制臺&#xff0c;點擊右上角用戶名、安全憑證&#xff0c;在我的安全憑證窗口中&#xff0c;下拉找到訪問密鑰&#xff0c;并點擊創建訪問密鑰&#xff0c;選擇其他&#xff0c;點擊下一步&#xff0c;即可獲得密鑰信息如圖1至圖6。 注意&…

windows內網穿透

內網穿透&#xff08;NAT穿透&#xff09;是一種通過技術手段將局域網&#xff08;內網&#xff09;中的服務暴露到公網&#xff08;外網&#xff09;的方法&#xff0c;使外部用戶能夠訪問內網資源。其核心是解決因NAT&#xff08;網絡地址轉換&#xff09;或防火墻限制導致的…

threejs 實現720°全景圖,;兩種方式:環境貼圖、CSS3DRenderer渲染

前提 有一個前提條件&#xff1a;六張大小一致的圖片&#xff0c;六個圖片分別對應的是720全景圖的六個面&#xff1a;上、下、左、右、前、后。 這個不是那種無人機拍攝的全景圖&#xff0c;是六個圖片拼起來的&#xff0c;這樣的取景方式要比無人機的要經濟一些。 ---…

老牌軟件 Ghost 備份還原操作基礎

一、Ghost 簡介 Symantec Ghost&#xff08;也稱為 Norton Ghost&#xff09; 是一款強大的磁盤克隆和備份還原工具&#xff0c;廣泛用于系統部署、數據恢復和災難恢復。其主要功能包括&#xff1a; 創建磁盤鏡像&#xff08;.GHO文件&#xff09;備份/還原分區或整個硬盤支持…

SSH連接服務器并同步本地文件

SSH連接服務器并同步本地文件 1. 復制本地公鑰 cat ~/.ssh/id_rsa.pub如果不確定本地是否有公鑰 ls ~/.ssh/id_rsa.pub# 如果出現如下&#xff0c;則說明你本地存在公鑰 # /Users/username/.ssh/id_rsa.pub若沒有公鑰&#xff0c;需生成 # 使用下面命令&#xff0c;然后一路回…

中英泰馬來語訂貨系統:助力東南亞批發貿易企業數字化轉型升級

隨著全球數字化轉型浪潮的推進&#xff0c;東南亞地區的批發貿易企業也正逐步邁向數字化發展道路。特別是在中英泰馬來語訂貨系統的推動下&#xff0c;東南亞的批發商和零售商能夠更高效、便捷地開展跨國貿易與供應鏈管理。這不僅幫助傳統企業提高了運營效率&#xff0c;還助力…

微信小程序獲取指定元素,滾動頁面到指定位置

微信小程序獲取指定元素&#xff0c;滾動頁面到指定位置 微信小程序獲取指定元素的寬高等信息,并滾動頁面到指定位置 微信小程序獲取指定元素的寬高等信息,并滾動頁面到指定位置 注&#xff1a;原生小程序開發&#xff1a; createSelectorQuery() 創建一個選擇器查詢實例。 sel…

LeetCode熱題100—— 118. 楊輝三角

https://leetcode.cn/problems/pascals-triangle/description/?envTypestudy-plan-v2&envIdtop-100-liked 題解 代碼 public List<List<Integer>> generate(int numRows) {List<List<Integer>> datatList new ArrayList<>();for(int i …

Python函數/Lambda/nested function/decorator/kwargs:全面教程

目錄 函數簡介基本函數語法函數參數返回值高級函數概念列表推導式與Lambda函數實用示例 函數簡介 函數是可重用的代碼塊&#xff0c;用于執行特定任務。它們有助于組織代碼&#xff0c;促進復用&#xff0c;并使程序更易于維護。可以將函數視為程序中的小型程序。 基本函數…

UG NX二次開發(C++)-創建草圖(基于平面、X軸和參考點)

文章目錄 1、前言2、在UG NX中的操作3、代碼實現3.1 添加頭文件3.2 在項目中聲明一個創建草圖的函數3.3 創建草圖函數的實現代碼3.4 函數調用3.5 實現效果1、前言 作為一款大型的CAD/CAM軟件,UG NX在建模中草圖的作用非常重要,功能也非常強大,所以在UG NX中學會草圖的二次開…

計算機視覺課程筆記-機器學習中典型的有監督與無監督學習方法的詳細分類、標簽空間性質、解釋說明,并以表格形式進行總結

? 一、有監督學習&#xff08;Supervised Learning&#xff09; 定義&#xff1a;有監督學習中&#xff0c;模型訓練依賴于已標注的樣本&#xff0c;即輸入和輸出&#xff08;標簽&#xff09;成對出現。 標簽空間可能是&#xff1a; 離散型&#xff08;Discrete&#xff09…