音乐类网站页面设计特点,铁法能源公司网站,号码之家官网,素材下载网站开发个人主页#xff1a;chian-ocean
文章专栏-Linux
前言#xff1a; 在Linux中#xff0c;进程替换#xff08;Process Substitution#xff09;是一个非常强大的特性#xff0c;它允许将一个进程的输出直接当作一个文件来处理。这种技术通常用于Shell脚本和命令行操作中…个人主页chian-ocean
文章专栏-Linux
前言 在Linux中进程替换Process Substitution是一个非常强大的特性它允许将一个进程的输出直接当作一个文件来处理。这种技术通常用于Shell脚本和命令行操作中。 进程替换原理
进程替换(Process Replacement)是操作系统用来用一个新程序完全替换当前进程用户态内容的机制其本质是清空当前进程的用户态内容并加载新程序同时保留内核态资源如 PID、文件描述符等。它通过 exec 系列系统调用实现以下是进程替换的详细原理。
进程替换的核心是
清空当前进程的用户态地址空间包括代码段、数据段、堆、栈等。加载新程序到当前进程的地址空间并切换到新程序的入口点执行。保留进程的内核态资源如 PID、打开的文件描述符、父子关系等。如果 exec 调用成功原进程的代码永远不会被执行。
#include iostream
#include unistd.h
#include sys/wait.h
#include cstdlib using namespace std;int main() {// 输出当前进程的 PID进程 ID和 PPID父进程 IDcout Im a process: PID: getpid() PPID: getppid() endl;// 调用 fork() 创建子进程pid_t id fork();// 子进程逻辑if (id 0) {cout Child PID: getpid() endl;//打印子进程的PID// 使用 execl() 替换当前子进程为 /usr/bin/ls 程序// 第一个参数是程序路径第二个参数是程序名称通常为 argv[0]后面是命令行参数execl(/usr/bin/ls, ls, -l, -a, NULL);// 如果 execl() 执行失败例如文件不存在会执行以下代码perror(execl failed); // 输出错误信息exit(1); // 子进程以退出码 1 结束}// 父进程逻辑// 使用 waitpid() 等待子进程结束int ret waitpid(id, NULL, 0); // 第二个参数为 NULL表示忽略子进程的退出状态if (ret 0) {// 如果 waitpid() 成功返回表示子进程已结束cout Father PID: getpid() Child PID: ret endl;return 0; // 父进程正常退出
}
执行流程
程序开始 父进程运行打印自己的 PID 和 PPID错误地显示 PPID 为自己的 PID。 创建子进程 fork 创建一个子进程。 子进程执行 execl 子进程替换为 /usr/bin/ls 程序并执行 ls -l -a 命令列出当前目录中所有文件包括隐藏文件的详细信息。如果 execl 成功子进程的地址空间完全被 ls 程序覆盖。如果 execl 失败执行 exit(1)子进程退出返回码为 1。 父进程等待子进程 父进程调用 waitpid阻塞等待子进程终止。当子进程完成后waitpid 返回子进程的 PID。 父进程打印结果 父进程输出自己的 PID 和已终止的子进程的 PID。 子进程的PID没有变化发成了进程替换。
exec系类函数
exec 系列函数是 UNIX/Linux 系统中用于进程替换的函数集合。通过 exec 系列函数当前进程的用户态内容如代码段、数据段、堆、栈等会被新程序替换而进程的内核态资源如 PID、打开的文件描述符等被保留。
exec 系列函数不创建新进程只是在当前进程中加载并运行一个新程序。
exec 系列函数的成员 L可以理解list
V可以理解Vector
execl
int execl(const char *path, const char *arg0, ..., NULL);参数说明
path 新程序的文件路径可以是绝对路径或相对路径。如 /bin/ls 或 ./myprogram。 arg0, ..., NULL 传递给新程序的参数列表按照顺序传递给新程序的 argv 数组。arg0 通常是程序名相当于 argv[0]。后续的参数是传递给新程序的命令行参数相当于 argv[1], argv[2], ...。参数列表必须以 NULL 结束。 示例
execl(/bin/ls, ls, -l, -a, NULL);execlp
int execlp(const char *file, const char *arg0, ..., NULL);参数说明
file 新程序的文件名。如果 file 不包含斜杠/execlp 会根据 PATH 环境变量搜索可执行文件。如果 file 包含斜杠则直接视为路径无需搜索 PATH。 arg0, ..., NULL 传递给新程序的参数列表必须以 NULL 结束。arg0 通常是程序名相当于 argv[0]。后续参数为程序的命令行参数相当于 argv[1]、argv[2] 等。 示例
execlp(ls, ls, -l, -a, NULL);execle
int execle(const char *path, const char *arg0, ..., NULL, char *const envp[]);参数说明
path
新程序的文件路径可以是绝对路径或相对路径。如 /bin/ls 或 ./myprogram。
arg0, ..., NULL
传递给新程序的参数列表必须以 NULL 结束。arg0 通常是程序名相当于 argv[0]。后续参数为程序的命令行参数相当于 argv[1], argv[2], ...。
envp
一个指向环境变量字符串数组的指针。每个环境变量字符串的格式为 keyvalue例如PATH/usr/bin。如果希望新程序继承当前进程的环境变量可以手动传递当前进程的 environ。
#includeiostream
#includeunistd.h
#includestdlib.h
#includesys/wait.h
#includesys/types.husing namespace std;int main()
{ cout Im a process: PID:getpid() PPID: getpid() endl; pid_t id fork(); char *envp[] { MY_VARHelloWorld, PATH/bin:/usr/bin, NULL }; if(id 0) { cout Child PID: getpid() endl; execle(/usr/bin/env,env,NULL,envp); exit(1); } int ret waitpid(id,NULL,0); if(ret 0) cout Father PID: getpid() Child PID: ret endl; return 0;
}execv
int execv(const char *path, char *const argv[]);参数说明
path: 指向可执行文件路径的字符串以 \0 结尾。argv: 一个字符串指针数组用于传递给新程序的参数列表。数组的第一个元素通常为程序名称argv[0]最后一个元素必须为 NULL以标记参数列表结束。
示例
#includeiostream
#includeunistd.h
#includestdlib.h
#includesys/wait.h 。
#includesys/types.h int main()
{// 输出当前进程的 PID进程 ID和 PPID父进程 IDstd::cout Im a process: PID: getpid() PPID: getppid() std::endl;// 创建子进程pid_t id fork();// 定义一个字符指针数组用于存储传递给 execv 的参数char *argv[] { ls, // argv[0]: 通常是程序名称-l, // argv[1]: 参数表示以长格式列出文件-a, // argv[2]: 参数显示隐藏文件NULL // 终止符必须为 NULL};if(id 0) // 子进程执行的代码块{ // 子进程输出自己的 PIDstd::cout Child PID: getpid() std::endl; // 用 execv 替换当前进程的执行映像execv(/usr/bin/ls, argv);// 如果 execv 返回说明执行失败exit(1); // 退出子进程返回非零值表示错误}// 父进程等待子进程完成int ret waitpid(id, NULL, 0);if(ret 0) // 如果 waitpid 成功返回std::cout Father PID: getpid() Child PID: ret std::endl;return 0;
}
逐步功能分析
主进程输出信息 使用 getpid() 和 getppid() 分别获取当前进程 ID 和父进程 ID并输出信息。创建子进程 使用 fork() 创建一个子进程 返回值 id 0表示当前是子进程。返回值 id 0表示当前是父进程id 为子进程的 PID。 子进程执行新程序 在子进程中调用 execv 替换当前进程映像为 /usr/bin/ls。参数数组 argv 指定了程序名称和选项。如果 execv 成功后续代码不会执行否则会继续执行并调用 exit(1) 终止子进程。 父进程等待子进程 父进程调用 waitpid 阻塞当前进程直到子进程终止。返回值 ret 是子进程的 PID。 父进程输出信息 输出父进程和子进程的 PID 信息。 execvp
int execvp(const char *file, char *const argv[]);参数说明
file 要执行的程序名称或路径。如果提供的是程序名称非路径execvp 会根据环境变量 PATH 中的目录列表查找该程序。 argv 一个字符串数组表示传递给新程序的参数。argv[0] 通常是程序名称最后一个元素必须为 NULL。
execvp 与 execv 的区别
execv 要求指定程序的完整路径且不会从环境变量 PATH 中查找。execvp 可以仅提供程序名称函数会自动从 PATH 中查找程序。
#includeiostream
#includeunistd.h
#includestdlib.h
#includesys/wait.h 。
#includesys/types.h int main()
{// 输出当前进程的 PID进程 ID和 PPID父进程 IDstd::cout Im a process: PID: getpid() PPID: getppid() std::endl;// 创建子进程pid_t id fork();// 定义一个字符指针数组用于存储传递给 execv 的参数char *argv[] { ls, // argv[0]: 通常是程序名称-l, // argv[1]: 参数表示以长格式列出文件-a, // argv[2]: 参数显示隐藏文件NULL // 终止符必须为 NULL};if(id 0) // 子进程执行的代码块{ // 子进程输出自己的 PIDstd::cout Child PID: getpid() std::endl; // 用 execvp 替换当前进程的执行映像execvp(ls, argv); // 区别于execv// 如果 execv 返回说明执行失败exit(1); // 退出子进程返回非零值表示错误}// 父进程等待子进程完成int ret waitpid(id, NULL, 0);if(ret 0) // 如果 waitpid 成功返回std::cout Father PID: getpid() Child PID: ret std::endl;return 0;
}ecexvpe
int execvpe(const char *file, char *const argv[], char *const envp[]);参数说明
file 要执行的程序名称或路径。如果提供的是程序名称execvpe 会根据环境变量 PATH 自动查找该程序。 argv 一个字符串数组用于传递给新程序的参数。argv[0] 通常是程序的名称最后一个元素必须是 NULL。 envp 一个字符串数组用于指定新程序的环境变量。每个字符串的格式为 KEYVALUE例如 PATH/usr/bin。最后一个元素必须为 NULL。
示例
#includeiostream
#includeunistd.h
#includestdlib.h
#includesys/wait.h
#includesys/types.husing namespace std;int main()
{ // 输出当前进程的 PID 和父进程 IDPPIDcout Im a process: PID: getpid() PPID: getpid() endl;// 创建子进程pid_t id fork(); // 自定义环境变量数组char *envp[] {MY_VARHelloWorld, // 自定义变量 MY_VAR值为 HelloWorldPATH/bin:/usr/bin, // 自定义 PATH确保能找到可执行文件NULL // 终止标志}; // 命令参数数组传递给 ls 命令char *argv[] {ls, // argv[0] 通常为程序名称-l, // 参数长格式输出-a, // 参数显示隐藏文件NULL // 终止标志}; if(id 0) // 子进程{cout Child PID: getpid() endl;// 使用 execvpe 执行 ls 命令传递自定义环境变量execvpe(ls, argv, envp);// 如果 execvpe 执行失败exit(1); // 退出子进程返回非零值表示错误} // 父进程等待子进程完成int ret waitpid(id, NULL, 0);if(ret 0) // 如果子进程正常退出cout Father PID: getpid() Child PID: ret endl;return 0功能分析 父进程输出信息 使用 getpid() 获取当前进程的 ID。使用 getpid() 显示父进程的 PPID此处写错正确用法应是 getppid()。 创建子进程 调用 fork()创建子进程 返回值 id 0表示当前是子进程。返回值 id 0表示当前是父进程id 为子进程的 PID。 定义环境变量和参数 envp是自定义的环境变量数组 包括 MY_VARHelloWorld 和 PATH/bin:/usr/bin。 argv是传递给 execvpe的参数列表 包括 ls 命令及其参数 -l 和 -a。 子进程执行新程序 子进程调用 execvpe(ls, argv, envp)替换当前子进程的映像为 ls 命令。使用自定义的环境变量。 如果 execvpe 失败子进程调用 exit(1) 退出。 父进程等待子进程完成 调用 waitpid 等待子进程完成。输出父进程和子进程的 PID 信息。 exec 系列函数总结
函数名称程序路径参数传递环境变量特点execl完整路径列表传参继承父进程环境手动传递每个参数易用但不适合动态参数数量。execlp搜索 PATH列表传参继承父进程环境在 PATH 中查找程序适合提供命令名称的情况。execle完整路径列表传参自定义环境与 execl 类似但支持自定义环境变量。execv完整路径数组传参继承父进程环境参数通过数组传递适合动态生成参数的情况。execvp搜索 PATH数组传参继承父进程环境在 PATH 中查找程序适合命令名称和动态参数。execve完整路径数组传参自定义环境底层实现函数用户可完全控制路径、参数和环境变量。execvpe搜索 PATH数组传参自定义环境GNU 扩展结合 execvp 和 execve 的优点。