目录
[7》execve 系统调用](#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
[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。
感谢你的观看,期待我们下次再见!