目录
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 fileSYNOPSIS
#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 fileSYNOPSIS
#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。
感谢你的观看,期待我们下次再见!