前言
在我们需要使用多进程编程时,有两种比较常见的情况
- 一个父进程希望复制自己,使父、子进程同时执行不同的代码段。在网络服务进程中是常见的——父进程等待客户端得到服务请求。当这种请求到达时,父进程调用fork函数,使子进程处理此请求。父进程继续等待下一个服务请求到达、
- 一个进程哦要执行一个不同的程序。这对shell是常见的情况。在这种情况下,子进程fork返回后立即调用exec
<br/>
exec族函数的介绍:
在调用进程内部执行一个可执行文件。可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
函数族:
exec函数族分别是:execl, execlp, execle, execv, execvp, execvpe
函数原型:
我们可在Linux的终端中输入man exec
查看到函数信息如下:
#include <unistd.h>
extern char **environ;
int execl(const char *pathname, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *pathname, const char *arg, ...);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
返回值
man手册中是这么说的,翻译之后的大概意思为:Exec()函数仅在发生错误时返回。返回值为-1,并设置errno以指示错误。
<br/>
常用函数介绍
exec族函数,在函数名中结尾的字母大有玄机:
l:使用参数列表
p:使用文件名,从PATH环境中进行寻找可执行文件
v:要先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数
e:多了envp[]数组,使用新的环境变量代替调用进程的环境变量。
由于exec函数极难分辨和记忆,对于新手而言,我们就学会使用其中的几个就可以满足我们的需求了。
<br/>
execl函数
函数原型如下:int execl(const char *pathname, const char *arg, ...);
。其中参数pathname表示可执行文件的路径,arg是参数。在没有其他参数,末尾要写上NULL作为参数。
那接下来我们简单的使用一下这个函数吧。
首先我们先编写一个简单的c语言代码生成可执行文件用于测试execl函数的功能。
#include <stdio.h>
int main(int argc, char **argv)
{
int i;
for(i=0 ;i<argc ;i++)
{
printf("argv[%d] = %s\n",i,argv[i]);
}
return 0;
}
这段代码的作用是便利输出可执行文件的参数。我们使用 gcc xxx.c -o test
编译成我们想好的用户名。
接下来我们编写execl的代码。
#include <unistd.h>
#include <stdio.h>
int main()
{
printf("before execl\n");
if(execl("./test","text1","text2",NULL) == -1)
{
printf("execl error\n");
perror("why");
}
printf("after execl\n");
return 0;
}
我们可以通过perror输出错误信息。当出现错误时,会输出错误信息。
运行失败则结果如下:
before execl
execl error
why: Permission denied
after execl
如果运行成功,则可以看到结果:
before execl
argv[0] = text1
argv[1] = text2
通过上述代码效果,我们可以简单的知道如何使用execl函数,并且我们发现,当出现错误时,函数会继续回到出现错误的地方继续往下执行代码。
execl小练习
接下来我们做一个小练习
需求
使用execl函数实现以下获取现在的时间:
在Linux中,有一个命令date
可以获取到系统现在的时间。我们先使用whereis date
获取到date命令的位置。
hyx@hyx-virtual-machine:~/c_project/c_Process$ whereis date
date: /usr/bin/date /usr/share/man/man1/date.1.gz
在/usr/bin
目录下。接下来,我们使用execl实现一下:
int main()
{
printf("show now date:\n");
if(execl("/usr/bin/date","date",NULL) == -1)
{
printf("execl error\n");
perror("why");
}
printf("after execl\n");
return 0;
}
运行结果如下:
show now date:
2024年 03月 29日 星期五 00:00:54 CST
execlp函数
在我们学会execl函数之后,我们会有这种感受,就是当我们要调用这些Linux的命令时,我们每次都需要先whereis xxx
来获取到命令所在的位置。才能调用,让我们觉得比较麻烦。
那使用execlp
函数的优势就来了。我们修改一下上面的代码。
#include <unistd.h>
#include <stdio.h>
int main()
{
printf("show now date:\n");
if(execlp("date","date",NULL) == -1)
{
printf("execl error\n");
perror("why");
}
printf("after execl\n");
return 0;
}
我们将execl替换成execlp,execlp他能自动从环境变量中获取到位置,然后在其中进行寻找。那如果我们想要用execlp函数之间方便的执行我们自己生成的可执行文件。我们就只需要将我们写的文件位置加入到环境变量中就可以实现了。
在Linux系统中,我们可以使用export &PATH:/xxxx/xxx
就可以添加我们的文件位置到环境变量中。快去试试吧。
<br/>
execvp函数
execvp函数与execlp函数区别就是将这些参数放在一个数组中,然后在函数中将这个数组代替那些参数就好了。
#include <unistd.h>
#include <stdio.h>
int main()
{
char* arrgv[] = {"date",NULL};
printf("show now date:\n");
if(execvp("date",arrgv) == -1)
{
printf("execl error\n");
perror("why");
}
printf("after execl\n");
return 0;
}
至于execv函数则是类似的。就不过多赘述了。
总结
至此,execc族函数就差不多介绍完了,通过exec族函数,我们可以将复杂的任务拆分成多个小的、独立的程序,每个程序负责完成一个特定的任务。这样,我们可以更容易地编写、测试和维护代码,同时也可以避免代码重复。