进程控制---进程程序替换
目录1.进程程序替换1.快速见见效果2.进程替换的原理-fork3.认识全部接口1》execl那么这个除了替换我们的指令之外还可以替换我们的程序吗程序替换本质不会创建新的进程只是把代码进程替换了2》execlp3》execv4》execvp5》execvpe6》execle7》execve 系统调用我们之前学过指令这些指令跑起来其实也是一个进程[user1iZ5waahoxw3q2bZ 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 [user1iZ5waahoxw3q2bZ 26-4-26]$ sleep 10000 ls pwd但是为什么我们在sleep里面输入指令没有反应呢sleep父进程是bashsleep期间bash在阻塞等待所以当然没反应。1.进程程序替换1.快速见见效果exec 程序替换的接口NAMEexecl, execlp, execle, execv, execvp, execvpe - execute a fileSYNOPSIS#include unistd.hextern 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[user1iZ5waahoxw3q2bZ 26-4-27]$ cat proc.c #includestdio.h #includeerrno.h #includestring.h #includestdlib.h #includeunistd.h #includesys/types.h #includesys/wait.h int main() { printf(我的程序要运行了\n); execl(/usr/bin/ls,ls,-l,-a,NULL); printf(我的程序运行完毕了); return 0; }[user1iZ5waahoxw3q2bZ 26-4-27]$ make gcc -o proc proc.c [user1iZ5waahoxw3q2bZ 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*系列的函数不用对返回值进行判断只要返回就是失败execlRETURN 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.认识全部接口NAMEexecl, execlp, execle, execv, execvp, execvpe - execute a fileSYNOPSIS#include unistd.hextern 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_SOURCE1》execlint execl(const char *path, const char *arg, .../* (char *) NULL */); //...可变参数列表第一个参数要以路径程序名作用 execl(/usr/bin/ls,ls,-a,-l,NULL);我要执行谁 [user1iZ5waahoxw3q2bZ 26-4-27]$ ls -a -l之后的参数命令行怎么写你就怎么传。以,为分割符。我想怎么执行它把参数一个一个传进来叫做listlist可变参数列表最后一个参数必须以NULL结尾表明参数传递成功不想让其影响其他代码可不可以不把自己的程序进行替换。让父进程安心做自己的其他进程去执行。[user1iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #includestdio.h #includeerrno.h #includestring.h #includestdlib.h #includeunistd.h #includesys/types.h #includesys/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; }[user1iZ5waahoxw3q2bZ 26-4-28]$ make gcc -o proc proc.c [user1iZ5waahoxw3q2bZ 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*接口---属于加载器的范畴那么这个除了替换我们的指令之外还可以替换我们的程序吗可以的[user1iZ5waahoxw3q2bZ 26-4-28]$ cat other.cc #includeiostream int main() { std::couthello Cstd::endl; return 0; } [user1iZ5waahoxw3q2bZ 26-4-28]$ g other.cc -o other [user1iZ5waahoxw3q2bZ 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[user1iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #includestdio.h #includeerrno.h #includestring.h #includestdlib.h #includeunistd.h #includesys/types.h #includesys/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; }运行[user1iZ5waahoxw3q2bZ 26-4-28]$ make gcc -o proc proc.c [user1iZ5waahoxw3q2bZ 26-4-28]$ ./proc 我的程序要运行了 hello C 我的程序运行完毕了程序替换本质不会创建新的进程只是把代码进程替换了[user1iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #includestdio.h #includeerrno.h #includestring.h #includestdlib.h #includeunistd.h #includesys/types.h #includesys/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; } [user1iZ5waahoxw3q2bZ 26-4-28]$ cat other.cc #includeiostream #includeunistd.h int main() { std::couthello C,My Pid Is;getpid()std::endl; return 0; }运行[user1iZ5waahoxw3q2bZ 26-4-28]$ make gcc -o proc proc.c [user1iZ5waahoxw3q2bZ 26-4-28]$ ./proc 我的程序要运行了 I am Child,My Pid Is:4500 hello C,My Pid Is;4500 我的程序运行完毕了2》execlpint execlp(const char *file, const char *arg, .../* (char *) NULL */);p---path第一个参数只需要告诉要执行的文件名--因为execlp会自动在环境变量PATH中查找指定的命令后面参数同上命令行怎么写就怎么传。execlp(ls,ls,-l,-a,NULL);[user1iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #includestdio.h #includeerrno.h #includestring.h #includestdlib.h #includeunistd.h #includesys/types.h #includesys/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; }[user1iZ5waahoxw3q2bZ 26-4-28]$ make gcc -o proc proc.c [user1iZ5waahoxw3q2bZ 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》execvint 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);[user1iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #includestdio.h #includeerrno.h #includestring.h #includestdlib.h #includeunistd.h #includesys/types.h #includesys/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); }运行[user1iZ5waahoxw3q2bZ 26-4-28]$ make gcc -o proc proc.c [user1iZ5waahoxw3q2bZ 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》execvpint execvp(const char *file, char *const argv[]);第一个参数同只需要告诉要执行的文件名第二个参数同提供一个命令行参数表。char *const argv[] {(char*const)ls,(char*const)-l,(char*const)-a,NULL};execvp(argv[0],argv);[user1iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #includestdio.h #includeerrno.h #includestring.h #includestdlib.h #includeunistd.h #includesys/types.h #includesys/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); }运行[user1iZ5waahoxw3q2bZ 26-4-28]$ make gcc -o proc proc.c [user1iZ5waahoxw3q2bZ 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》execvpeint 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)MYVAL123456789,NULL};execvpe(./other,argv,env);[user1iZ5waahoxw3q2bZ 26-4-28]$ cat other.cc #includeiostream #includecstdio #includeunistd.h int main(int argc,char *argv[],char *env[]) { std::couthello C,My Pid Is;getpid()std::endl; for(int i0;iargc;i) { printf(argv[%d]:%s\n,i,argv[i]); } printf(\n); for(int i0;env[i];i) { printf(env[%d]:%s\n,i,env[i]); } return 0; } [user1iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #includestdio.h #includeerrno.h #includestring.h #includestdlib.h #includeunistd.h #includesys/types.h #includesys/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)MYVAL123456789, NULL }; execvpe(./other,argv,env); execl(./other,other,NULL); exit(1); } waitpid(-1,NULL,0); printf(我的程序运行完毕了\n); }运行[user1iZ5waahoxw3q2bZ 26-4-28]$ make gcc -o proc proc.c [user1iZ5waahoxw3q2bZ 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]:MYVAL123456789 我的程序运行完毕了但是发现只剩我们传递的了老的没有了。所以execvpe第三个参数如果传环境变量要求被替换的子进程使用全新的如果想以新增的方式交给子进程该怎么做呢1.直接putenv2.exec*e,putenv();environ;1.替换成这个 execvp(./other,argv);发现是可以的。为什么不传反而有呢证明函数内部自己传了。即便不传参也能获取这个环境变量environ2.putenv谁调用它就在谁的环境变量表里面新增环境变量SYNOPSIS#include stdlib.hint putenv(char *string);新增环境变量[user1iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #includestdio.h #includestdlib.h #includeerrno.h #includestring.h #includestdlib.h #includeunistd.h #includesys/types.h #includesys/wait.h char *newenv(char*)MYVAL6666666; 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)MYVAL123456789, NULL }; putenv(newenv); execl(./other,other,NULL); exit(1); } waitpid(-1,NULL,0); printf(我的程序运行完毕了\n); }但如果就是想用execvpe呢[user1iZ5waahoxw3q2bZ 26-4-28]$ cat Makefile proc:proc.c gcc -o $ $^ -stdc99 .PHONY:clean clean: rm -f proc [user1iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c #includestdio.h #includestdlib.h #includeerrno.h #includestring.h #includestdlib.h #includeunistd.h #includesys/types.h #includesys/wait.h char *const addenv[] { (char *const)MYVAL123456789, 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 i0;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》execleint execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);除7外其他6个是语言封装7》execve 系统调用NAMEexecve - execute programSYNOPSIS#include unistd.hint execve(const char *filename, char *const argv[],char *const envp[]);参数filename可执行文件的路径不支持PATH搜索。argv传递给新程序的命令行参数数组以NULL结尾。envp传递给新程序的环境变量数组以NULL结尾。成功时不会返回当前进程被新程序替换。失败时返回-1并设置errno。感谢你的观看期待我们下次再见