C++-linux系統編程 8.進程(二)exec函數族詳解

exec函數族詳解

在Unix/Linux系統中,fork()exec()函數族是進程控制的黃金組合:fork()創建新進程,exec()則讓新進程執行不同的程序。這種組合是實現shell命令執行、服務器進程動態加載任務等核心功能的基礎。本文將詳細解析exec函數族的原理、使用方法及最佳實踐。

一、exec函數族概述

核心功能

exec函數族的核心作用是程序替換:在當前進程中加載并執行新的程序,替換原有進程的用戶空間代碼、數據和堆棧,從新程序的main()函數開始執行,而進程 ID 保持不變,這意味著調用exec不會創建新進程,而是讓當前進程執行另一個程序。

與fork的協作關系

  • fork()創建子進程(父進程的副本)
  • 子進程通過exec()替換為新程序,實現"創建新進程并執行新任務"的完整流程
  • 替換后進程ID(PID)保持不變,僅程序內容被替換

二、exec函數族成員與原型

exec函數族包含6個核心函數,均以exec為前綴,后綴不同表示參數傳遞方式和功能特性的差異。

函數名函數原型核心特點
execlint execl(const char *path, const char *arg, ...);參數以列表(list)形式傳遞,需顯式指定程序路徑
execvint execv(const char *path, char *const argv[]);參數以數組(vector)形式傳遞,需顯式指定程序路徑
execleint execle(const char *path, const char *arg, ..., char *const envp[]);列表傳參+自定義環境變量,需顯式指定程序路徑
execveint execve(const char *path, char *const argv[], char *const envp[]);數組傳參+自定義環境變量,需顯式指定程序路徑(系統調用原型)
execlpint execlp(const char *file, const char *arg, ...);列表傳參,自動搜索PATH環境變量查找程序
execvpint execvp(const char *file, char *const argv[]);數組傳參,自動搜索PATH環境變量查找程序

三、命名規律與參數解析

exec函數族的命名后綴遵循嚴格規則,掌握這些規則能快速理解函數用法:

后綴含義示例
l(list)參數以可變參數列表形式傳遞,最后必須以(char*)NULL結尾execl("/bin/ls", "ls", "-l", NULL);
v(vector)參數以字符串數組形式傳遞,數組最后一個元素必須是NULLchar* args[] = {"ls", "-l", NULL}; execv("/bin/ls", args);
p(path)自動搜索環境變量PATH查找程序,無需顯式指定完整路徑execlp("ls", "ls", "-l", NULL);(無需寫/bin/ls
e(environment)允許通過參數自定義環境變量,其他函數使用當前進程的環境變量char* env[] = {"PATH=/bin", NULL}; execle("/bin/ls", "ls", NULL, env);

四、返回值與錯誤處理

exec函數族的返回值特性與普通函數不同,需特別注意:

  • 成功執行:函數不會返回(新程序替換當前進程,原有代碼被覆蓋)。
  • 執行失敗:返回-1,并設置errno指示錯誤原因(如程序不存在、權限不足等)。

錯誤處理示例

if (execvp("ls", args) == -1) {perror("exec failed");  // 輸出錯誤原因(如"exec failed: No such file or directory")exit(EXIT_FAILURE);    // 必須退出,否則子進程會繼續執行原有代碼
}

常見錯誤碼:

  • ENOENT:找不到指定的程序文件
  • EACCES:程序文件無執行權限
  • EINVAL:參數格式錯誤(如參數列表未以NULL結尾)

五、典型應用場景:fork+exec組合

exec函數族極少單獨使用,通常與fork()配合,實現"創建新進程并執行新程序"的經典流程:

流程解析

  1. 父進程調用fork()創建子進程(復制自身)
  2. 子進程調用exec函數替換為新程序
  3. 父進程通過wait()waitpid()等待子進程結束

代碼示例:執行ls -l命令

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>int main() {pid_t pid = fork();if (pid < 0) {perror("fork failed");exit(EXIT_FAILURE);} else if (pid == 0) {// 子進程:替換為ls程序char *args[] = {"ls", "-l", NULL};  // 參數數組必須以NULL結尾// 使用execvp:自動搜索PATH,數組傳參if (execvp("ls", args) == -1) {perror("exec failed");exit(EXIT_FAILURE);  // 若exec失敗,子進程需退出}// 注意:exec成功后,以下代碼永遠不會執行printf("This line will never be printed.\n");} else {// 父進程:等待子進程結束int status;waitpid(pid, &status, 0);  // 等待指定子進程printf("Child process (PID=%d) completed.\n", pid);}return 0;
}

執行流程說明

  1. 父進程創建子進程(PID不變)
  2. 子進程調用execvp("ls", args)
    • PATH中搜索ls程序(通常位于/bin/ls
    • ls程序的代碼、數據替換子進程的內存空間
    • ls程序的main()函數開始執行,參數為{"ls", "-l"}
  3. ls程序執行完畢后退出,父進程通過waitpid()回收資源

六、程序替換的核心特性

exec函數族的本質是程序替換,理解以下特性是掌握exec的關鍵:

  1. 進程ID不變:替換前后進程的PID、PPID保持不變,操作系統仍認為是同一個進程。
  2. 內存空間完全替換
    • 代碼段、數據段、堆、棧被新程序覆蓋
    • 原有全局變量、局部變量、函數定義均失效
  3. 保留部分系統資源
    • 未被新程序關閉的文件描述符(繼承自父進程的打開文件)
    • 進程的信號掩碼、當前工作目錄、環境變量(除非使用e后綴函數自定義)
  4. 原子操作:程序替換是原子性的,要么完全成功(新程序運行),要么完全失敗(原有程序繼續執行)。

七、注意事項與最佳實踐

  1. 參數列表必須以NULL結尾
    無論是l后綴的可變參數還是v后綴的數組,都必須以NULL結束,否則會導致未定義行為:

    // 錯誤示例:參數列表未以NULL結尾
    execl("ls", "ls", "-l");  // 可能崩潰或執行異常// 正確示例
    execl("ls", "ls", "-l", (char*)NULL);  // 顯式添加NULL結尾
    
  2. 帶p后綴的函數路徑處理
    當文件名包含斜杠(/)時,p后綴函數會直接使用路徑而非搜索PATH

    execlp("./myprog", "myprog", NULL);  // 直接執行當前目錄的myprog,不搜索PATH
    
  3. 環境變量傳遞
    若需自定義環境變量,使用execleexecve;否則默認繼承父進程的environ變量:

    // 自定義環境變量示例
    char* my_env[] = {"PATH=/usr/local/bin","USER=custom",NULL  // 環境變量數組必須以NULL結尾
    };
    execle("/bin/echo", "echo", "$USER", NULL, my_env);  // 輸出"custom"
    
  4. 避免僵尸進程
    父進程必須通過wait()waitpid()回收調用exec的子進程,即使exec失敗也不例外。

  5. 優先使用帶p后綴的函數執行系統命令
    對于lsdate等系統命令,使用execlpexecvp更簡潔(無需硬編碼完整路徑):

    // 推薦:依賴PATH自動查找
    execvp("date", (char*[]){"date", "+%Y-%m-%d", NULL});// 不推薦:硬編碼路徑(不同系統可能安裝在不同位置)
    execl("/bin/date", "date", "+%Y-%m-%d", NULL);
    

總結

exec函數族是Linux進程編程的核心工具,通過程序替換機制,實現了在保持進程ID不變的情況下執行新程序的功能。其與fork()的組合("創建-替換"模式)是shell、服務器等多任務系統的基礎。掌握exec函數族的命名規律、參數傳遞方式及替換特性,能有效提升進程控制能力,為實現復雜系統功能奠定基礎。

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

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

相關文章

PTL亮燈揀選系統提升倉庫運營效率的方案

隨著電商、零售、制造等行業的快速發展&#xff0c;倉庫的作業效率成為企業競爭力的關鍵因素之一。傳統的揀選方式多依賴人工尋找與確認&#xff0c;不僅耗費時間&#xff0c;還容易出錯&#xff0c;嚴重制約倉庫整體運營效率。為了應對日益增長的訂單需求與提高揀選準確率&…

LVS三種模式實戰

IPVS基本上是一種高效的Layer-4交換機&#xff0c;它提供負載平衡的功能。當一個TCP連接的初始SYN報文到達時&#xff0c;IPVS就選擇一臺服務器&#xff0c;將報文轉發給它。此后通過查看報文的IP和TCP報文頭地址&#xff0c;保證此連接的后繼報文被轉發到相同的服務器。這樣&a…

HCIA第二次綜合實驗:OSPF

HCIA第二次綜合實驗&#xff1a;OSPF一、實驗拓撲二、實驗需求 1、R1-R3為區域0&#xff0c;R3-R4為區域1&#xff1b;其中R3在環回地址在區域1&#xff1b; 2、R1、R2各有一個環回口&#xff1b; 3、R1-R3中&#xff0c;R3為DR設備&#xff0c;沒有BDR&#xff1b; 4、R4環回地…

深入解析環境變量:從基礎概念到系統級應用

目錄 一、基本概念及其核心作用 1、基本概念 2、核心作用 二、常見環境變量 三、查看環境變量方法 四、測試PATH 1、對比執行&#xff1a;./project和直接執行project的區別 2、思考&#xff1a;為何某些命令可直接執行而無需路徑&#xff0c;但我們的二進制程序卻需要…

Spring Boot:DTO 字段 cPlanId 無法反序列化的奇葩問題

本文記錄一次在 Spring Boot 項目中&#xff0c;DTO 字段明明有值&#xff0c;反序列化后卻是 null 的問題。最終發現并不是常見的 JSON 工具庫 Bug&#xff0c;而是隱藏在 setter 命名大小寫規則中的坑。&#x1f4bb; 背景介紹技術棧如下&#xff1a;Spring Boot&#xff1a;…

文本生成視頻的主要開源模型

AI文本到視頻生成技術發展迅速&#xff0c;這些模型的“快速”通常指相對于傳統視頻制作的效率&#xff08;生成時間從幾秒到幾分鐘&#xff0c;取決于硬件&#xff09;&#xff0c;但實際速度取決于您的計算資源&#xff08;如GPU&#xff09;。這些模型大多依賴于深度學習框架…

vscode里面怎么配置ssh步驟

01.ubuntu里面下載幾個插件還需要下載插件net-tools02.vscode里面下載插件會生成下面類似電視機的插件(room6)

【人工智能99問】激活函數有哪些,如何選擇使用哪個激活函數?(5/99)

文章目錄激活函數一、激活函數的分類1. 按“是否線性”分類2. 按“是否飽和”分類&#xff08;針對非線性激活函數&#xff09;3. 按“適用層”分類二、常見激活函數及特點&#xff08;一&#xff09;非線性激活函數&#xff08;主要用于隱藏層&#xff09;1. 飽和激活函數&…

代數——第4章——線性算子(算符)(Michael Artin)

第 4 章 線性算子(Linear Operators) That confusions of thought and errors of reasoning still darken the beginnings of Algebra, is the earnest and just complaint of sober and thoughtful men. (思維混亂和推理錯誤 仍然使代數的開端變得模糊不清&#xff0c; …

Neo4j Python 驅動庫完整教程(帶輸入輸出示例)

Neo4j Python 驅動庫完整教程&#xff08;帶輸入輸出示例&#xff09; 1. 基礎連接示例 輸入代碼 from neo4j import GraphDatabase# 連接配置 URI "bolt://localhost:7687" USER "neo4j" PASSWORD "password123" # 替換為你的實際密碼def t…

Axios 和 Promise 區別對比

Axios 和 Promise 是前端開發中兩個不同的概念&#xff0c;盡管 Axios 基于 Promise 實現&#xff0c;但它們的核心定位和功能有顯著區別。以下是對比分析&#xff1a; 1. 核心定位與功能Promise 定義&#xff1a;Promise 是 JavaScript 的異步編程方案&#xff0c;用于處理異步…

Git分支管理與工作流詳解

前言 分支管理是Git最強大的功能之一&#xff0c;它允許開發者在不影響主代碼庫的情況下創建獨立的工作空間。本文將詳細介紹Git分支的操作和常見工作流策略&#xff0c;幫助團隊更高效地協作開發。 1. Git分支的基本概念 1.1 什么是分支 在Git中&#xff0c;分支本質上是指…

【flutter】flutter網易云信令 + im + 聲網rtm從0實現通話視頻文字聊天的踩坑

接了一個國外的項目,項目采用網易云im 網易云信令聲網rtm遇到的一些問題這個項目只對接口,給的工期是兩周,延了工期,問題還是比較多的 需要全局監聽rtm信息,收到監聽內容,引起視頻通話網易云給的文檔太爛,所有的類型推策只能文檔一點點推聲網的rtm配置網易云的信令,坑太多,比如…

hive/spark sql中unix_timestamp 函數的坑以及時間戳相關的轉換

我用的是hive版本是3.1.3&#xff0c;spark版本是3.3.1&#xff0c;它們的unix_timestamp 函數在同樣的語句下轉換出來的時間戳是完全不同的&#xff0c;如下試驗所示1.unix_timestamp 函數的坑上圖試驗中我同樣的計算 2025-07-11 10:00:00 時間點對應的時間戳&#xff0c;但是…

MyBatis專欄介紹

專欄導讀 在當今的軟件開發領域&#xff0c;持久層框架的選擇對于提高開發效率和數據處理能力至關重要。MyBatis作為一個半自動化的ORM框架&#xff0c;因其靈活、高效的特點&#xff0c;在眾多開發者中廣受好評。本專欄《MyBatis實戰》旨在通過深入淺出的方式&#xff0c;幫助…

HarmonyOS從入門到精通:自定義組件開發指南(七):自定義事件與回調

HarmonyOS從入門到精通&#xff1a;自定義組件開發指南&#xff08;七&#xff09;&#xff1a;自定義事件與回調 在HarmonyOS應用開發中&#xff0c;組件化架構是構建復雜界面的基礎&#xff0c;而組件間的高效通信則是實現業務邏輯的核心。自定義事件與回調機制作為組件交互的…

C++編程學習(第七天)

基于過程的程序設計C既可以用來進行基于過程的程序設計&#xff0c;又可以用來進行面向對象的程序設計。基于過程的程序設計又稱為過程化的程序設計&#xff0c;它的特點是&#xff1a;程序必須告訴計算機應當具體怎么做&#xff0c;也就是要給出計算機全部操作的具體過程&…

ubuntu透網方案

場景&#xff1a;兩個linux/Ubuntu系統&#xff0c;一個可以上網&#xff0c;一個不能&#xff0c;讓不能上網的,讓能上網的共享網絡 步驟 1&#xff1a;修改 /etc/sysctl.conf sudo nano /etc/sysctl.conf 找到或添加以下行&#xff1a; net.ipv4.ip_forward1 按 CtrlO 保存&a…

基于Python的物聯網崗位爬取與可視化系統的設計與實現【海量數據、全網崗位可換】

文章目錄有需要本項目的代碼或文檔以及全部資源&#xff0c;或者部署調試可以私信博主項目介紹數據采集數據預處理系統展示總結每文一語有需要本項目的代碼或文檔以及全部資源&#xff0c;或者部署調試可以私信博主 項目介紹 隨著物聯網技術的迅速發展&#xff0c;物聯網行業…

線性回歸原理推導與應用(十):邏輯回歸多分類實戰

本篇文章將利用sklearn中內置的鳶尾花數據進行邏輯回歸建模并對鳶尾花進行分類。對于邏輯回歸和線性回歸的相關原理&#xff0c;可以查看之前的文章 數據導入 鳶尾花數據是機器學習里的常用數據&#xff0c;首先導入一些基礎庫并從sklearn中導入數據集 #導入用到的一些pytho…