前言:Linux进程控制包含了进程终止,进程等待,进程程序替换。走到现在我们也只剩下进程程序替换没介绍了,那么让我们来看看进程程序替换到底是什么!
本篇主要内容:
替换原理
替换函数
实现简易shell
我们所创建的所有的子进程,执行的代码,都是父进程代码的一部分,如果我们想让子进程执行新的程序,执行全新的代码和访问全新的数据,不在和父进程有瓜葛这就要用到我们的进程替换了!!!
进程程序替换
- 1. 替换原理
- 2. 替换函数
- 2.1 execl类型函数
- 2.2 execv类型函数
- 3. 实现简易shell
- 4. 总结
1. 替换原理
我们先介绍一个exec函数,方便我们读下面的代码
execl:
我们直接来看代码分析:
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4 #include <wait.h>
5 int main()
6 {
7 pid_t id = fork();
8 if(id == 0)
9 {
10 printf("pid: %d, exec command begin\n", getpid());
11 sleep(2);
12 execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
13 printf("pid: %d, exec command end\n", getpid());
14 }
15 else{
16 pid_t rid = waitpid(-1, NULL, 0);
17 if(rid > 0)
18 {
19 printf("wait success, pid: %d\n", rid);
20 }
21 }
22 return 0;
23 }
我们用fork创建子进程后执行的是和父进程相同的程序,子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
- 操作系统在调用exec函数时,只要将整个进程的代码和数据用新程序的代码和数据进行替换。
- 子进程往往会共享父进程的代码和数据,因为有写时拷贝的技术,有新数据写入时,会重新开辟空间所以不会相互影响,保证父子进程的独立性。
但是我们仔细查看代码和执行结果后发现,竟然有一段代码没有执行!
13 printf("pid: %d, exec command end\n", getpid());
其实是调用exec程序替换,只要exec替换新程序成功了,子进程就回去执行新的程序,因此后面的代码将不会被执行
2. 替换函数
在进程程序替换中,替换函数必不可少,让我们来看看这类函数!
RETURN VALUE:
The exec() functions error has occurred return only if an The return value is -1 and errno to indicate the error
而exec开头的函数一共介绍6种,它们只会在出错时返回 -1!
我们以一下两点来理解:
- 必须先找到这个可执行程序怎么执行
- 必须告诉exec,怎么执行*
2.1 execl类型函数
execl我们在上面简单了解了,我们就介绍一下剩下两个
execlp:
int main()
{
{
printf("pid: %d, exec command begin\n", getpid());
sleep(2);
execlp("ls", "ls", "-a", "-l", NULL);
//第一个ls代表怎么找到这个程序
//第二个ls其实是命令的一部分,对应怎么执行
printf("pid: %d, exec command end\n", getpid());
}
return 0;
}
在使用execlp函数时,我们并没有加上路径,但是我们依然能够获得想要的结果,正是因为
p代表了PATH
他会自动去环境变量PATH中根据file来寻找可执行程序!!!
execlpe:
// mytest源代码
int main()
{
char *const myenv[] = {
"myenv1=2004",
"myenv2=2005",
"myenv3=2005",
"myenv4=2005",
NULL
};
printf("exec command begin\n");
execle("./myprocess", "myprocess", NULL, myenv);
// myprocess源代码
int main(int agrc, char *agrv[], char *env[])
{
for(int i = 0; env[i]; i++)
{
printf("[%d]: %s\n", i, env[i]);
}
return 0;
}
这是exec*函数程序替换,我们在下文会讲
2.2 execv类型函数
execv
其实就是在execl
的基础上多了一个指针数组,情况大差不差我们就不具体介绍了!具体关系如下图:
execv 和 execvp:
int main()
{
char *const argv[] = {
"ls",
"-a",
"-l",
NULL
};
printf("pid: %d, exec command begin\n", getpid());
sleep(2);
execv("/usr/bin/ls", argv);
//execvp("ls", argv);
printf("pid: %d, exec command end\n", getpid());
return 0;
}
其实exec这类函数命令有一定的规律:
- l(list) : 表示参数采用列表
- v(vector) : 参数用数组
- p(path) : 有p自动搜索环境变量PATH
- e(env) : 表示自己维护环境变量
3. 实现简易shell
我们用程序替换是可以用C语言把C++调用起来的
// test.c
int main()
{
printf("pid: %d, exec command begin\n", getpid());
execl("./myprocess", "myprocess", NULL);
return 0;
}
// test.cc
int main()
{
std::cout << "hello c++" << std::endl;
std::cout << "hello c++" << std::endl;
std::cout << "hello c++" << std::endl;
std::cout << "hello c++" << std::endl;
return 0;
}
Makefile:
.PHONY:all
all:mytest myprocess
myprocess:test.cc
g++ -o $@ $^ -std=c++11
mytest:test.c
gcc -o $@ $^ -std=c99
.PHONY:clean
clean:
rm -f mytest myprocess
因为我们要同时编译两个程序,所以我们要对Makefile进行一下调整
我们通过程序替换将C++用C语言调用起来了
那我们除了调用C++还能调用别的嘛?
- 显而易见当然是可以的!!!
// test.sh
// 会简单编写就好
#!/usr/bin/bash
echo "hello Linux"
echo "hello world"
touch code
// test.c
int main()
{
printf("pid: %d, exec command begin\n", getpid());
execl("/usr/bin/bash", "bash", "test.sh", NULL);
return 0;
}
当然除了我们讲的这两个,其实所有语言都能通过进程程序替换来调用,因为我们用任意语言写的程序都会变成进程,只要是进程就都可以用exec*来替换——系统大于一切
4. 总结
进程程序替换能够让我们只用C语言就可以调用其他任何语言去执行,极大程度上带来了便利,也可以帮助我们完成很多任务,好了,关于Linux进程控制我们先了解到这,下次我们将进入新的章节!
谢谢大家支持本篇到这里就结束了