news 2026/4/27 23:48:21

进程控制---进程程序替换

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
进程控制---进程程序替换

目录

1.进程程序替换

1.快速见见效果

2.进程替换的原理-fork

3.认识全部接口

1》execl

那么这个除了替换我们的指令之外,还可以替换我们的程序吗?

程序替换,本质不会创建新的进程,只是把代码进程替换了

2》execlp

3》execv

4》execvp

5》execvpe

6》execle

7》execve 系统调用


我们之前学过指令,这些指令跑起来其实也是一个进程

[user1@iZ5waahoxw3q2bZ 26-4-26]$ file /usr/bin/sleep /usr/bin/sleep: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=6e225bf5e1fbd30ee61bd7c94e0eccf32cc2b2ab, stripped [user1@iZ5waahoxw3q2bZ 26-4-26]$ sleep 10000 ls pwd

但是为什么我们在sleep里面输入指令没有反应呢?

sleep父进程是bash,sleep期间bash在阻塞等待,所以当然没反应。

1.进程程序替换

1.快速见见效果

exec 程序替换的接口

NAME
execl, execlp, execle, execv, execvp, execvpe - execute a file

SYNOPSIS
#include <unistd.h>

extern char **environ;

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[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

execvpe(): _GNU_SOURCE

[user1@iZ5waahoxw3q2bZ 26-4-27]$ cat proc.c #include<stdio.h> #include<errno.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> int main() { printf("我的程序要运行了!\n"); execl("/usr/bin/ls","ls","-l","-a",NULL); printf("我的程序运行完毕了"); return 0; }
[user1@iZ5waahoxw3q2bZ 26-4-27]$ make gcc -o proc proc.c [user1@iZ5waahoxw3q2bZ 26-4-27]$ ./proc 我的程序要运行了! total 28 drwxrwxr-x 2 user1 user1 4096 Apr 27 20:25 . drwxrwxr-x 14 user1 user1 4096 Apr 27 18:09 .. -rw-rw-r-- 1 user1 user1 59 Apr 27 19:40 Makefile -rwxrwxr-x 1 user1 user1 8544 Apr 27 20:25 proc -rw-rw-r-- 1 user1 user1 300 Apr 27 20:25 proc.c

我们这个程序竟然可以去执行系统中的命令。这种现象叫做程序替换

2.进程替换的原理-fork

进程=内核数据结构+代码和数据

替换原理

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

int main() { printf("我的程序要运行了!\n"); execl("/usr/bin/ls","ls","-l","-a",NULL); printf("我的程序运行完毕了"); return 0; }

代码段是main函数这里的,数据段就是内部定义的变量

进程一旦调用execl,会把指明的新的路径下的新的程序,把其代码重新覆盖式地写到代码中,覆盖式地写到数据段中。而整个替换过程只替换代码和数据。

在程序替换的过程中,并没有创建新的进程,只是:
把当前进程的代码和数据覆盖式地进行替换!

1》一旦程序执行成功,就去执行新代码,原始代码的后半部分,已经不存在了!
2》exec*函数,只有失败返回值,没有成功返回值
exec*系列的函数,不用对返回值进行判断,只要返回,就是失败!

execl

RETURN VALUE The exec() functions return only if an error has occurred. The return value is -1, and errno is set to indicate the error.

3.认识全部接口

NAME
execl, execlp, execle, execv, execvp, execvpe - execute a file

SYNOPSIS
#include <unistd.h>

extern char **environ;

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[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

execvpe(): _GNU_SOURCE


1》execl

int execl(const char *path, const char *arg, .../* (char *) NULL */); //"..."可变参数列表

第一个参数:要以路径+程序名
作用: execl("/usr/bin/ls","ls","-a","-l",NULL);
我要执行谁 [user1@iZ5waahoxw3q2bZ 26-4-27]$ ls -a -l

之后的参数:命令行怎么写,你就怎么传。以","为分割符。

我想怎么执行它

把参数一个一个传进来叫做list,list可变参数列表

最后一个参数必须以NULL结尾,表明参数传递成功

不想让其影响其他代码,可不可以不把自己的程序进行替换。让父进程安心做自己的,其他进程去执行。

[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #include<stdio.h> #include<errno.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> int main() { printf("我的程序要运行了!\n"); if(fork() == 0) { sleep(1); //child execl("/usr/bin/ls","ls","-l","-a",NULL); exit(1); } waitpid(-1,NULL,0); //execl("/usr/bin/top","top",NULL); printf("我的程序运行完毕了!\n"); //return 0; }
[user1@iZ5waahoxw3q2bZ 26-4-28]$ make gcc -o proc proc.c [user1@iZ5waahoxw3q2bZ 26-4-28]$ ./proc 我的程序要运行了! total 28 drwxrwxr-x 2 user1 user1 4096 Apr 27 21:06 . drwxrwxr-x 15 user1 user1 4096 Apr 27 20:49 .. -rw-rw-r-- 1 user1 user1 59 Apr 27 20:49 Makefile -rwxrwxr-x 1 user1 user1 8696 Apr 27 21:06 proc -rw-rw-r-- 1 user1 user1 465 Apr 27 21:06 proc.c 我的程序运行完毕了!


为什么没有影响父进程?
1.进程具有独立性
2.数据、代码发生写时拷贝

exec*接口---属于加载器的范畴

那么这个除了替换我们的指令之外,还可以替换我们的程序吗?

可以的

[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat other.cc #include<iostream> int main() { std::cout<<"hello C++"<<std::endl; return 0; } [user1@iZ5waahoxw3q2bZ 26-4-28]$ g++ other.cc -o other [user1@iZ5waahoxw3q2bZ 26-4-28]$ ll total 36 -rw-rw-r-- 1 user1 user1 59 Apr 27 20:49 Makefile -rwxrwxr-x 1 user1 user1 9056 Apr 27 21:12 other -rw-rw-r-- 1 user1 user1 88 Apr 27 21:12 other.cc -rwxrwxr-x 1 user1 user1 8696 Apr 27 21:06 proc -rw-rw-r-- 1 user1 user1 465 Apr 27 21:06 proc.c

用proc.c去调用other.cc

[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #include<stdio.h> #include<errno.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> int main() { printf("我的程序要运行了!\n"); if(fork() == 0) { sleep(1); //child //execl("/usr/bin/ls","ls","-l","-a",NULL); execl("./other","other",NULL); exit(1); } waitpid(-1,NULL,0); //execl("/usr/bin/top","top",NULL); printf("我的程序运行完毕了!\n"); //return 0; }

运行

[user1@iZ5waahoxw3q2bZ 26-4-28]$ make gcc -o proc proc.c [user1@iZ5waahoxw3q2bZ 26-4-28]$ ./proc 我的程序要运行了! hello C++ 我的程序运行完毕了!
程序替换,本质不会创建新的进程,只是把代码进程替换了
[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #include<stdio.h> #include<errno.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> int main() { printf("我的程序要运行了!\n"); if(fork() == 0) { printf("I am Child,My Pid Is:%d\n",getpid()); sleep(1); //child //execl("/usr/bin/ls","ls","-l","-a",NULL); execl("./other","other",NULL); exit(1); } waitpid(-1,NULL,0); //execl("/usr/bin/top","top",NULL); printf("我的程序运行完毕了!\n"); //return 0; } [user1@iZ5waahoxw3q2bZ 26-4-28]$ cat other.cc #include<iostream> #include<unistd.h> int main() { std::cout<<"hello C++,My Pid Is;"<<getpid()<<std::endl; return 0; }

运行

[user1@iZ5waahoxw3q2bZ 26-4-28]$ make gcc -o proc proc.c [user1@iZ5waahoxw3q2bZ 26-4-28]$ ./proc 我的程序要运行了! I am Child,My Pid Is:4500 hello C++,My Pid Is;4500 我的程序运行完毕了!


2》execlp

int execlp(const char *file, const char *arg, .../* (char *) NULL */);
p---path
第一个参数:只需要告诉要执行的文件名--因为execlp会自动在环境变量PATH中查找指定的命令

后面参数:同上,命令行怎么写,就怎么传。

execlp("ls","ls","-l","-a",NULL);

[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #include<stdio.h> #include<errno.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> int main() { printf("我的程序要运行了!\n"); if(fork() == 0) { printf("I am Child,My Pid Is:%d\n",getpid()); sleep(1); execlp("ls","ls","-l","-a",NULL); //child //execl("/usr/bin/ls","ls","-l","-a",NULL); execl("./other","other",NULL); exit(1); } waitpid(-1,NULL,0); //execl("/usr/bin/top","top",NULL); printf("我的程序运行完毕了!\n"); //return 0; }
[user1@iZ5waahoxw3q2bZ 26-4-28]$ make gcc -o proc proc.c [user1@iZ5waahoxw3q2bZ 26-4-28]$ ./proc 我的程序要运行了! I am Child,My Pid Is:4512 total 44 drwxrwxr-x 2 user1 user1 4096 Apr 27 21:25 . drwxrwxr-x 15 user1 user1 4096 Apr 27 20:49 .. -rw-rw-r-- 1 user1 user1 59 Apr 27 20:49 Makefile -rwxrwxr-x 1 user1 user1 9160 Apr 27 21:19 other -rw-rw-r-- 1 user1 user1 128 Apr 27 21:19 other.cc -rwxrwxr-x 1 user1 user1 8848 Apr 27 21:25 proc -rw-rw-r-- 1 user1 user1 603 Apr 27 21:25 proc.c 我的程序运行完毕了!

3》execv

int execv(const char *path, char *const argv[]);
v---vector

第一个参数:同上上 ,要以路径+程序名

第二个参数: 提供一个命令行参数表,其实就是一个指针数组!

char *const argv[] = {
(char*const)"ls",
(char*const)"-l",
(char*const)"-a",
NULL
};
execv("/usr/bin/ls",argv);

[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #include<stdio.h> #include<errno.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> int main() { printf("我的程序要运行了!\n"); if(fork() == 0) { printf("I am Child,My Pid Is:%d\n",getpid()); sleep(1); char *const argv[] = { (char*const)"ls", (char*const)"-l", (char*const)"-a", NULL }; execv("/usr/bin/ls",argv); execl("./other","other",NULL); exit(1); } waitpid(-1,NULL,0); printf("我的程序运行完毕了!\n"); }

运行

[user1@iZ5waahoxw3q2bZ 26-4-28]$ make gcc -o proc proc.c [user1@iZ5waahoxw3q2bZ 26-4-28]$ ./proc 我的程序要运行了! I am Child,My Pid Is:4557 total 44 drwxrwxr-x 2 user1 user1 4096 Apr 27 21:32 . drwxrwxr-x 15 user1 user1 4096 Apr 27 20:49 .. -rw-rw-r-- 1 user1 user1 59 Apr 27 20:49 Makefile -rwxrwxr-x 1 user1 user1 9160 Apr 27 21:19 other -rw-rw-r-- 1 user1 user1 128 Apr 27 21:19 other.cc -rwxrwxr-x 1 user1 user1 8848 Apr 27 21:32 proc -rw-rw-r-- 1 user1 user1 808 Apr 27 21:32 proc.c 我的程序运行完毕了!

所以我们原来系统的argv也是类似的方式去传的。父进程通过exec传的。

4》execvp

int execvp(const char *file, char *const argv[]);
第一个参数:同,只需要告诉要执行的文件名

第二个参数:同,提供一个命令行参数表。

char *const argv[] = {
(char*const)"ls",
(char*const)"-l",
(char*const)"-a",
NULL
};
execvp(argv[0],argv);

[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #include<stdio.h> #include<errno.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> int main() { printf("我的程序要运行了!\n"); if(fork() == 0) { printf("I am Child,My Pid Is:%d\n",getpid()); sleep(1); char *const argv[] = { (char*const)"ls", (char*const)"-l", (char*const)"-a", NULL }; execvp(argv[0],argv); execl("./other","other",NULL); exit(1); } waitpid(-1,NULL,0); printf("我的程序运行完毕了!\n"); }

运行

[user1@iZ5waahoxw3q2bZ 26-4-28]$ make gcc -o proc proc.c [user1@iZ5waahoxw3q2bZ 26-4-28]$ ./proc 我的程序要运行了! I am Child,My Pid Is:4576 total 44 drwxrwxr-x 2 user1 user1 4096 Apr 27 21:40 . drwxrwxr-x 15 user1 user1 4096 Apr 27 20:49 .. -rw-rw-r-- 1 user1 user1 59 Apr 27 20:49 Makefile -rwxrwxr-x 1 user1 user1 9160 Apr 27 21:19 other -rw-rw-r-- 1 user1 user1 128 Apr 27 21:19 other.cc -rwxrwxr-x 1 user1 user1 8848 Apr 27 21:40 proc -rw-rw-r-- 1 user1 user1 850 Apr 27 21:40 proc.c 我的程序运行完毕了!


5》execvpe

int execvpe(const char *file, char *const argv[],char *const envp[]);

第一个参数:同,只需要告诉要执行的文件名

第二个参数:同,提供一个命令行参数表。

第三个参数,环境变量

char *const argv[] = {
(char *const)"other",
(char *const)"-a",
(char *const)"-b",
(char *const)"-c",
(char *const)"-d",
NULL
};

char *const env[]= {
(char *const)"MYVAL=123456789",
NULL
};

execvpe("./other",argv,env);

[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat other.cc #include<iostream> #include<cstdio> #include<unistd.h> int main(int argc,char *argv[],char *env[]) { std::cout<<"hello C++,My Pid Is;"<<getpid()<<std::endl; for(int i=0;i<argc;i++) { printf("argv[%d]:%s\n",i,argv[i]); } printf("\n"); for(int i=0;env[i];i++) { printf("env[%d]:%s\n",i,env[i]); } return 0; } [user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #include<stdio.h> #include<errno.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> int main() { printf("我的程序要运行了!\n"); if(fork() == 0) { printf("I am Child,My Pid Is:%d\n",getpid()); sleep(1); char *const argv[] = { (char *const)"other", (char *const)"-a", (char *const)"-b", (char *const)"-c", (char *const)"-d", NULL }; char *const env[]= { (char *const)"MYVAL=123456789", NULL }; execvpe("./other",argv,env); execl("./other","other",NULL); exit(1); } waitpid(-1,NULL,0); printf("我的程序运行完毕了!\n"); }

运行

[user1@iZ5waahoxw3q2bZ 26-4-28]$ make gcc -o proc proc.c [user1@iZ5waahoxw3q2bZ 26-4-28]$ ./proc 我的程序要运行了! I am Child,My Pid Is:4615 hello C++,My Pid Is;4615 argv[0]:other argv[1]:-a argv[2]:-b argv[3]:-c argv[4]:-d env[0]:MYVAL=123456789 我的程序运行完毕了!

但是发现只剩我们传递的了,老的没有了。

所以execvpe第三个参数如果传环境变量,要求被替换的子进程使用全新的!

如果想以新增的方式交给子进程,该怎么做呢?

1.直接putenv

2.exec*e,putenv();environ;

1.替换成这个 execvp("./other",argv);

发现是可以的。为什么不传反而有呢?证明函数内部自己传了。即便不传参,也能获取这个环境变量
environ

2.putenv谁调用它,就在谁的环境变量表里面新增环境变量

SYNOPSIS
#include <stdlib.h>
int putenv(char *string);
新增环境变量

[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> char *newenv=(char*)"MYVAL=6666666"; int main() { printf("我的程序要运行了!\n"); if(fork() == 0) { printf("I am Child,My Pid Is:%d\n",getpid()); sleep(1); char *const argv[] = { (char *const)"other", (char *const)"-a", (char *const)"-b", (char *const)"-c", (char *const)"-d", NULL }; char *const env[]= { (char *const)"MYVAL=123456789", NULL }; putenv(newenv); execl("./other","other",NULL); exit(1); } waitpid(-1,NULL,0); printf("我的程序运行完毕了!\n"); }

但如果就是想用execvpe呢?

[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat Makefile proc:proc.c gcc -o $@ $^ -std=c99 .PHONY:clean clean: rm -f proc [user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> char *const addenv[]= { (char *const)"MYVAL=123456789", NULL }; int main() { printf("我的程序要运行了!\n"); if(fork() == 0) { printf("I am Child,My Pid Is:%d\n",getpid()); sleep(1); char *const argv[] = { (char *const)"other", (char *const)"-a", (char *const)"-b", (char *const)"-c", (char *const)"-d", NULL }; for(int i=0;addenv[i];i++) { putenv(addenv[i]); } extern char **environ; execvpe("./other",argv,environ); execl("./other","other",NULL); exit(1); } waitpid(-1,NULL,0); printf("我的程序运行完毕了!\n"); }

6》execle

int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);

除7外其他6个是语言封装!


7》execve 系统调用

NAME
execve - execute program
SYNOPSIS
#include <unistd.h>
int execve(const char *filename, char *const argv[],
char *const envp[]);

  • 参数

    • filename:可执行文件的路径(不支持PATH搜索)。

    • argv:传递给新程序的命令行参数数组,以NULL结尾。

    • envp:传递给新程序的环境变量数组,以NULL结尾。

  • 成功时:不会返回(当前进程被新程序替换)。

  • 失败时:返回-1,并设置errno

感谢你的观看,期待我们下次再见!

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

终极指南:如何用foo2zjs在Linux上实现专业级打印机兼容性

终极指南&#xff1a;如何用foo2zjs在Linux上实现专业级打印机兼容性 【免费下载链接】foo2zjs A linux printer driver for QPDL protocol - copy of http://foo2zjs.rkkda.com/ 项目地址: https://gitcode.com/gh_mirrors/fo/foo2zjs foo2zjs是一个开源Linux打印机驱动…

作者头像 李华
网站建设 2026/4/27 23:36:50

Tencent InstantCharacter跨平台AI角色生成工具解析

1. 项目概述Tencent InstantCharacter一键安装包是一个针对不同硬件平台优化的AI角色生成工具解决方案。这个项目最吸引人的地方在于它提供了跨平台的兼容性支持&#xff0c;从本地Windows环境到云端的RunPod和Massed Compute平台&#xff0c;甚至专门针对RTX 5000系列显卡进行…

作者头像 李华
网站建设 2026/4/27 23:36:03

终极解决方案:Android FLAG_SECURE 安全标志绕过完全指南

终极解决方案&#xff1a;Android FLAG_SECURE 安全标志绕过完全指南 【免费下载链接】DisableFlagSecure 项目地址: https://gitcode.com/gh_mirrors/dis/DisableFlagSecure 在Android开发和安全研究领域&#xff0c;FLAG_SECURE安全标志的限制一直是个棘手的问题。这…

作者头像 李华
网站建设 2026/4/27 23:35:55

Windows Cleaner终极指南:快速解决C盘爆红和电脑卡顿问题

Windows Cleaner终极指南&#xff1a;快速解决C盘爆红和电脑卡顿问题 【免费下载链接】WindowsCleaner Windows Cleaner——专治C盘爆红及各种不服&#xff01; 项目地址: https://gitcode.com/gh_mirrors/wi/WindowsCleaner Windows Cleaner是一款专为Windows用户设计的…

作者头像 李华