news 2026/10/24 6:40:45

Linux 学习笔记(十九):进程程序替换与手写微型 Shell

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux 学习笔记(十九):进程程序替换与手写微型 Shell

一、 进程程序替换:exec 家族

如果我们想让子进程去执行一个全新的程序(比如系统的ls),而不是执行父进程的代码,就需要用到exec族函数。

它的工作原理是:加载新程序到当前进程的地址空间中,完全覆盖原有的代码和数据,进程 PID 保持不变。

特性:如果exec执行成功,它没有返回值(代码被覆盖了);只有失败才返回-1。

二、 exec 命名规律

  • l(list):命令行参数是列表形式(一个个写出来,以NULL结尾)。

  • v(vector):参数是一个数组。

  • p(path):带 p 的(如execlp),自动去PATH环境变量里找程序,不用写绝对路径。

  • e(env):允许传入自定义的环境变量。

三、 实战:手写微型 Shell

天天用的 Bash 终端,底层的核心逻辑是什么?就是一个死循环:

  1. 打印提示符,获取用户输入。

  2. 解析输入(把字符串按空格切分成命令和参数数组)。

  3. 调用fork()创建子进程。

  4. 子进程调用execvp()进行程序替换,执行命令。

  5. 父进程调用wait()阻塞等待子进程结束,防止僵尸进程。

  6. 循环往复。

四、 代码演示:exec 替换测试

printf("变身前的进程: %d\n", getpid()); // 替换执行 ls -l 命令 execl("/usr/bin/ls", "ls", "-l", NULL); perror("execl error"); // 如果能运行到这里,说明替换失败

总结

进程替换是 Shell 原理的基石。结合了fork、exec、wait和字符串处理,我们就能在 Linux 系统中实现一个最基础的自定义命令解释器。

自我实战操作

实战 1:体验一次“灵魂出窍”(exec 替换)
cd ~/linux_study/proc_test vim exec_test.c

(输入以下代码)

#include <stdio.h> #include <unistd.h> int main() { printf("我是原来的程序[%d],接下来我要变身了...\n", getpid()); // 用 execl 替换成系统的 ls 命令 // 参数1: 程序的绝对路径 // 参数2: 命令名称 // 参数3: 命令参数 (比如 -l) // 参数4: 必须以 NULL 结尾 execl("/usr/bin/ls", "ls", "-l", NULL); // 如果代码走到这里,说明 execl 执行失败了 perror("execl error"); return 1; }

编译并运行:

gcc exec_test.c -o exec_test ./exec_test

👉现象:会看到打印出“我是原来的程序...”,紧接着打印出了当前目录的详细列表。原来的程序“变”成了ls。
实战 2:手写一个“微型 Shell”

这其实是文档里那个长篇大论的简化版,把核心逻辑剥出来。

vim myshell.c

(输入以下代码)

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> #define MAX_ARGS 10 int main() { char buf[1024]; // 存用户输入 char *argv[MAX_ARGS]; // 存解析后的参数 while (1) { // 1. 打印提示符 printf("myshell> "); fflush(stdout); // 2. 获取用户输入 fgets(buf, sizeof(buf), stdin); buf[strcspn(buf, "\n")] = 0; // 去掉末尾的换行符 // 如果是 exit,直接退出 if (strcmp(buf, "exit") == 0) break; // 3. 解析命令 (按空格切分,比如 "ls -l" 切分成 "ls" 和 "-l") int argc = 0; argv[argc] = strtok(buf, " "); while (argv[argc] != NULL) { argc++; argv[argc] = strtok(NULL, " "); } if (argc == 0) continue; // 4. 创建子进程,替换并执行 pid_t pid = fork(); if (pid == 0) { // 子进程 execvp(argv[0], argv); // execvp 的 p 会自动去 PATH 里找,不需要写绝对路径 perror("exec error"); // 走到这里说明命令敲错了 exit(1); } else if (pid > 0) { // 父进程等待回收,防止僵尸进程 wait(NULL); } } return 0; }

编译并运行:

gcc myshell.c -o myshell ./myshell

👉现象:你会看到一个像模像样的myshell>提示符。你在里面敲ls、pwd、ping,它都能执行。但是如果你敲cd,你会发现没反应(这就要用到内建命令了,文档里有进阶讲解,咱们先跑通核心逻辑)。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/24 6:38:44

一年三个AI作品:从零到落地的产品路线图与避坑指南

1. 为什么我要给自己画一张 AI 产品路线图去年这个时候&#xff0c;我手里只有一个跑在本地的小脚本&#xff0c;功能很单一&#xff0c;就是帮我把一堆杂乱的文本素材做初步归类。那时候我根本没想过“产品”这两个字&#xff0c;觉得能跑通、能省点力气就不错了。但一年下来&…

作者头像 李华
网站建设 2026/10/24 6:30:19

Windows平台frpc客户端架构选择与性能优化指南

1. frpc客户端架构差异解析在Windows平台上部署frp内网穿透服务时&#xff0c;我们常会遇到两个不同架构的客户端版本&#xff1a;frpc_win_amd64和frpc_win_arm64。这两个版本的核心区别在于CPU指令集架构的兼容性设计。1.1 x86_64与ARM64架构对比AMD64&#xff08;又称x86_64…

作者头像 李华
网站建设 2026/10/24 6:29:05

文件管理与IO重定向实战:从目录结构到管道魔法

文件管理与IO重定向实战&#xff1a;从目录结构到管道魔法 发布日期&#xff1a;2026-10-16 | 标签&#xff1a;Linux文件管理、IO重定向、管道、inode、硬链接 目录导航 一、Linux文件系统目录结构二、文件类型识别三、文件操作命令实战四、通配符详解五、inode与硬软链接六、…

作者头像 李华
网站建设 2026/10/24 6:20:01

集体好奇心如何提升技术团队效能与创新

1. 集体好奇心的本质与团队效能集体好奇心不是个体好奇心的简单叠加&#xff0c;而是团队成员在协作过程中形成的知识探索共同体。当团队中存在健康的集体好奇心时&#xff0c;成员会主动分享疑问、共同拆解问题、协作寻找答案&#xff0c;这种动态过程能显著提升团队的知识获取…

作者头像 李华
网站建设 2026/10/24 6:17:08

黑洞与宇宙创世:元法则的数学框架与量子引力探索

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华