当前位置: 首页 > news >正文

【Linux学习】Linux中的进程程序替换

大家好,我是程序员小青蛙,今天介绍进程程序替换。

一、什么是进程程序替换?

核心定义

fork()创建子进程后,子进程默认和父进程执行相同的程序。如果想让子进程执行一个全新的程序,就需要调用exec系列函数,完成进程程序替换

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

关键特点

  1. 不创建新进程:调用exec前后,进程的pid不会改变。
  2. 替换用户空间:新程序的代码段、数据段会完全覆盖原进程的用户空间,栈、堆也会被重置。
  3. 成功不返回exec调用成功后,原程序的后续代码不会再执行;调用失败时返回-1

二、exec 系列函数详解

Linux 提供了 6 个以exec开头的函数,统称为 exec 函数族。

1. 函数原型

#include <unistd.h> // l: list(可变参数列表) int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg, ..., char *const envp[]); // v: vector(参数数组) int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execve(const char *path, char *const argv[], char *const envp[]);

这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
如果调用出错则返回-1
所以exec函数只有出错的返回值而没有成功的返回值。

2. 命名规律(记忆技巧)

字母含义示例
llist:参数以可变列表形式传递,以NULL结尾execl("/bin/ls", "ls", "-l", NULL)
vvector:参数以数组形式传递,数组最后一个元素为NULLexecv("/bin/ls", argv)
ppath:自动搜索PATH环境变量,无需写全路径execlp("ls", "ls", "-l", NULL)
eenv:自定义环境变量,需手动传入envp数组execle("/bin/ls", "ls", "-l", NULL, envp)

3. 函数对比表

函数名参数格式是否带路径是否使用当前环境变量
execl列表必须写全路径
execlp列表可写程序名,自动搜PATH
execle列表必须写全路径否,需手动传入envp
execv数组必须写全路径
execvp数组可写程序名,自动搜PATH
execve数组必须写全路径否,需手动传入envp

4. 底层关系

只有execve是真正的系统调用,其他 5 个函数都是对execve的封装,以提供更灵活的调用方式。


三、exec 函数使用示例

1.execl示例(全路径 + 列表传参)

#include <unistd.h> int main() { // 执行 ls -l 命令,需要写全路径 execl("/bin/ls", "ls", "-l", NULL); // 只有调用失败才会执行这里 perror("execl"); return 1; }

2.execlp示例(自动搜 PATH + 列表传参)

#include <unistd.h> int main() { // 无需写全路径,自动在 PATH 中搜索 ls execlp("ls", "ls", "-l", NULL); perror("execlp"); return 1; }

3.execv示例(全路径 + 数组传参)

#include <unistd.h> int main() { char *argv[] = {"ls", "-l", NULL}; execv("/bin/ls", argv); perror("execv"); return 1; }
#include <unistd.h> int main() { char *const argv[] = {"ps", "-ef", NULL}; char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL}; execl("/bin/ps", "ps", "-ef", NULL); //带p的,可以使用环境变量PATH,无需写全路径 execlp("ps", "ps", "-ef", NULL); //带e的,需要自己组装环境变量 execle("ps", "ps", "-ef", NULL, envp); execv("/bin/ps", argv); //带p的,可以使用环境变量PATH,无需写全路径 execvp("ps", argv); //带e的,需要自己组装环境变量 execve("/bin/ps", argv, envp); exit(0); }

事实上,只有execve是真正的系统调用,其它五个函数最终都调用execve,所以execve在man手册第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示。


四、综合应用:简易 Shell 实现

fork+exec+wait是实现 shell 的核心逻辑。

核心流程

  1. 获取命令行:读取用户输入的命令。
  2. 解析命令:将命令字符串分割成argv数组。
  3. 创建子进程:调用fork()创建子进程。
  4. 程序替换:子进程调用execvp执行命令。
  5. 等待子进程:父进程调用waitpid等待子进程退出。

完整代码

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/wait.h> #define MAX_CMD 1024 char command[MAX_CMD]; // 1. 读取用户输入 int do_face() { memset(command, 0x00, MAX_CMD); printf("myshell$ "); fflush(stdout); if (scanf("%[^\n]%*c", command) == 0) { getchar(); return -1; } return 0; } // 2. 解析命令为 argv 数组 char **do_parse(char *buff) { int argc = 0; static char *argv[32]; char *ptr = buff; while (*ptr != '\0') { if (!isspace(*ptr)) { argv[argc++] = ptr; while (*ptr != '\0' && !isspace(*ptr)) ptr++; } else { while (*ptr != '\0' && isspace(*ptr)) ptr++; *ptr = '\0'; } } argv[argc] = NULL; return argv; } // 3. 执行命令 int do_exec(char *buff) { char **argv = do_parse(buff); if (argv[0] == NULL) return -1; pid_t pid = fork(); if (pid == 0) { // 子进程:程序替换 execvp(argv[0], argv); perror("execvp"); exit(1); } else { // 父进程:等待子进程退出 waitpid(pid, NULL, 0); } return 0; } int main() { while (1) { if (do_face() < 0) continue; do_exec(command); } return 0; }

思考函数和进程之间的相似性

exec/exit就像call/return
一个C程序有很多函数组成。一个函数可以调用另外一个函数,同时传递给它一些参数。被调用的函数执行一定的操作,然后返回一个值。每个函数都有他的局部变量,不同的函数通过call/return系统进行通信。
这种通过参数和返回值在拥有私有数据的函数间通信的模式是结构化程序设计的基础。Linux鼓励将这种应用于程序之内的模式扩展到程序之间。如下图

一个C程序可以fork/exec另一个程序,并传给它一些参数。这个被调用的程序执行一定的操作,然后通过exit(n)来返回值。调用它的进程可以通过wait(&ret)来获取exit的返回值。


五、关键知识点总结

  1. 程序替换的本质:用新程序的代码和数据,覆盖进程原有的用户空间,进程pid不变。
  2. exec 函数的核心特点:成功不返回,失败返回-1
  3. 命名规律l/v表示参数传递方式,p表示自动搜PATHe表示自定义环境变量。
  4. 简易 shell 的核心逻辑fork创建子进程 →exec替换程序 →wait等待子进程。
http://www.gsyq.cn/news/1422746.html

相关文章:

  • 从图片到代码:Qwen3-VL-8B-Thinking视觉编码能力实战教程
  • Easypoi停更了?别慌!手把手教你无缝迁移到Apache Fesod(FastExcel)并保留模板功能
  • 从40G到400G:一文读懂Infiniband带宽演进与你的数据中心选型指南
  • League Akari:英雄联盟玩家的终极智能助手,告别繁琐操作提升游戏体验
  • 【计算机组成原理】 栈帧访问机制
  • AU‑60 全功能 AI 语音处理模组:工程师视角的一站式声学解决方案
  • 5分钟搞定三大音乐平台逐字歌词:ESLyric-LyricsSource终极使用指南
  • Arduino音频编程实战:从蜂鸣器驱动到旋律播放全解析
  • 行业首份Claude-3.5代码质量压测报告:10万行样本暴露的2个反直觉性能断层
  • mT5-small-sum-de-mit-v1:德国电信开源的MIT许可证德语摘要模型全面解析
  • 解锁Wallpaper Engine宝藏:5分钟掌握RePKG资源提取神器
  • 如何快速掌握Mem Reduct:面向新手的完整内存优化指南
  • 终极指南:Windows版微信QQ防撤回工具完整教程
  • Abaqus显式分析结果怎么读?手把手教你用Matlab调用Python脚本提取ODB数据(避坑指南)
  • GPT如何理解表情包情感?多模态评估与提示词工程实战
  • 如何轻松永久保存微信聊天记录:WeChatMsg完整使用指南
  • paraphrase-distilroberta-base-v2在中文场景下的应用:跨语言语义理解的实践指南
  • 炸鸡加盟品牌!徐小臣:草本薄浆炸鸡开创者,重构中式健康炸鸡新赛道 - 资讯纵览
  • Qwen2.5-32B-Instruct容器化部署终极指南:7个Docker配置与优化技巧
  • ETS2LA自动驾驶:让卡车模拟游戏实现真正的自动驾驶体验 [特殊字符]
  • 2026西安贵金属回收最新实测报告,5家综合推荐闪闪珠宝 - 西安闲转记
  • 希腊语AI模型安全指南:使用gpt2-finetuned-greek-small时的注意事项
  • 基于ESP32与SA818模块的DIY无线电收发器:从硬件到软件定义电台
  • Ubuntu开机卡在‘wait until snap is fully seeded’?别慌,试试这几招(附磁盘清理命令大全)
  • Windows热键冲突终极指南:用Hotkey Detective快速找回被占用的快捷键
  • AI科研绘图转矢量用什么工具最好?
  • 2026年四川木托盘厂家推荐:深耕工业包装,赋能西南制造 - 深度智识库
  • Gemma4-26B-A4B-PRISM-PRO-DQ-GGUF多模态能力实测:文本/图像/视频处理全场景应用指南
  • Mugen常见问题解决:从安装错误到图像质量问题的完整排错指南
  • NoFences:终极免费桌面整理工具,打造整洁高效工作空间