exec函数
exec函数是一组函数,主要功能是可以在程序代码中调用某些命令,该函数具体分类如下:
分类 | 函数 |
---|---|
字符串数组参数 | execv() execvp() execve() |
可变参数 | execl() execlp() execle() |
v
:表示向量
l
:表示可变参数
p
:表示文件名
e
:表示环境变量
字符串数组参数execv
我们定义一个数组char* const args[] = { "cat", "./test", NULL };
,声明的一个环境变量extern char** environ;
,该环境变量是在其他库中定义好的,以下的举例都相当于直接在终端执行cat test
。
execv()
使用格式
- execv(“执行文件地址”,字符串数组);
execv("/bin/cat",args);
execvp()
使用格式
- execvp(“文件名”,字符串数组);
execvp("cat", args);
execve()
使用格式
- execve(“执行文件地址”,字符串数组, 环境变量)
execve("/bin/cat", args, environ);
可变参数execl
execl()
使用格式
- execv(“执行文件地址”,“程序名”,“处理的文件地址”,NULL);
- 注:NULL表示所需要给的参数结束了
execl("/bin/cat","cat","./test",NULL);
execvp()
使用格式
- execvp(“文件名”,“程序名”,“处理的文件地址”,NULL));
execlp("cat","cat","./test",NULL);
execve()
使用格式
- execve(“执行文件地址”,“程序名”,“处理的文件地址”,NULL, 环境变量)
execle("/bin/cat","cat","./test",NULL, environ);
exec特点
1. exec函数运行结束后,将停止运行
我们执行如下代码:
#include <iostream>
#include <unistd.h>
using namespace std;
int main(){
cout << "abcde" << endl; // execl和execv后面无法使用cout
cout << "12345" << endl;
execl("/bin/cat","cat","./test",NULL);
}
运行结果如下:
但如果我们将cout
放在exec
函数之后,代码如下:
#include <iostream>
#include <unistd.h>
using namespace std;
int main(){
execl("/bin/cat","cat","./test",NULL);
cout << "abcde" << endl; // execl和execv后面无法使用cout
cout << "12345" << endl;
}
运行结果如下:
2. 一次调用,失败返回
返回值 | 含义 |
---|---|
-1 | 失败 |
不返回 | 成功 |
注意:该函数调用成功后将不会有返回值!
3. 取代原进程
我们先准备一个loop.cpp
作为程序中被执行的代码,其内容如下:
#include <iostream>
#include <unistd.h>
using namespace std;
int main(){
for(int i=0; i<5; ++i){
cout << getpid() << ":" << getppid() << endl;
}
}
然后使用exec
函数调用loop.cpp
形成的可执行文件loop
,命令行使用g++ loop.cpp -o loop
就生成loop
了,然后在exec.cpp
中执行如下代码:
#include <iostream>
#include <unistd.h>
using namespace std;
int main(){
cout << "main:" << getpid() << ":" << getppid() << endl;
execl("./loop", "loop",NULL);
}
执行结果如下:
我们发现我们当前主函数的进程ID和调用的loop
中的进程ID是相同的,这时因为exec
并不会生成一个新的进程,它只是做了一个替代操作,将源程序的虚拟内存换为exec
函数中调用的命令生成的虚拟内存,原来的代码区就消失了,这也是为什么我们运行结束exec
之后,将不会继续运行接下来的代码。
注意,在本质上
exec
是一个可以覆盖程序的函数。它不会改变PID,但该程序所在的地址空间的内容将会改变。