本篇博客经参考资料1整理、修改而成。
1 exec 函数执行时,是用一个全新的程序代替了当前进程的正文、数据、堆栈段,但是它并没有改变进程ID,执行后进程ID依旧是之前的进程ID。其函数原型如下:
#include <unistd.h>
extern char **environ;
int execl(const char *pathname, const char *arg, ...
/* (char *) NULL */);
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
int execle(const char *pathname, const char *arg, ...
/*, (char *) NULL, char *const envp[] */);
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[]);
此6个函数按照exec后面的参数可以分为以下3类:
(1)带‘l’的。l是list,是指函数的参数以list的形式出现的,参数形式如下:arg0,arg1...argn,最后一个参数须以(char *)NULL结束。主要函数有execl(), execlp(), execle()。通常情况下,第一个参数是路径名,如果是完整路径名,则执行完整路径名,如果只是文件名,则从进程当前工作目录寻找。
程序1:execl.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(int argc, char *argv[])
{
printf("before execl\n");
if(execl("/bin/ls","ls","-l",(char *)0)==-1)
{
fprintf(stderr,"execl error ,error is %d,msg is %s\n",errno,strerror(errno));
}
printf("after execl\n");
exit(0);
}
程序执行后,会用全新的程序替代当前进程,执行结果如下:
可以看出,程序执行结束后,"after execl" 没有打印出来,因为进
程上下文已被切换成新的,原程序不再执行。如果pathname 使用文件名,当从当前工作目录查找,如果查找不到,便会出错。
修改execl.c源文件中的execl行为(execl2.c):
if(execl("ls","ls","-l",(char *)0)==-1)
显示找不到文件,后main继续执行,打印出了“after execl”。如果更改工作目录为/bin,因为/bin目录有ls的执行文件,所以程序执行结果一样成功,类似第一个结果截图。
(2)带‘v’的,v 是vector,代表参数是以vector的形式出现,参数个数总数为2个,第二个参数为指针数组char *const argv[],代表了后面的参数以指数组的形式出现,并且以空指针NULL结束。主要函数有execv(), execvp(), execvpe()。第一个参数pathname的意义与execl的第一个参数pathname 相同。
源程序execv.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(void)
{
printf("before execl\n");
char *argv[]={"ls","-l",(char *)0};
if(execv("/bin/ls",argv)==-1)
{
fprintf(stderr,"execv error ,error is %d,msg is %s\n",errno,strerror(errno));
}
printf("after execl\n");
exit(0);
}
执行结果如下:
(3)带‘p’的,p 代表“PATH”,是指程序的第一个参数filename ,是从当前环境变量PATH值寻找,即如果第一个参数不是完整路径的话,将从PATH寻找可执行文件,也包括当前工作目录。如果第一个参数中有‘/’,则PATH寻找过程被忽略。主要函数有:execlp(), execvp(), execvpe()。
源程序execlp.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(int argc, char *argv[])
{
printf("before execl\n");
if(execlp("ls","ls","-l",(char *)0)==-1)
{
fprintf(stderr,"execlp error ,error is %d,msg is %s\n",errno,strerror(errno));
}
printf("after execl\n");
exit(0);
}
执行结果如下
因为新建的文件越来越多,所以输出结果会相应的增加。
(4)带‘e’的。'e'代表environment,是指程序将当前的环境变量替换为参数char *const envp[]指向的环境表,并执行程序。这样的函数都有带有一个参数char *const envp[],出现在函数参数表中的位置均为最后一个位置。主要的函数有两个execle(), execvpe(),就是其它四个函数是用调用进程的环境表environ来执行,而这两个函数是用envp 代替environ 来执行,envp 是一个指针数组,以NULL 结束。
以下程序先打印当前环境表envrion的值,然后创建新环境表,并打印出来。
源程序:printenv.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
extern char **environ;
int main(void)
{
char **p=environ;
while(*p)
{
printf("%s\n",*p);
p++;
}
exit(0);
}
输出执行文件,gcc printenv.c -o printenv ,
源程序execl3.c,先打印当前进程的环境表
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(int argc, char *argv[])
{
printf("before execl\n");
if(execl("printenv","printenv",(char *)0)==-1)
{
fprintf(stderr,"execl error ,error is %d,msg is %s\n",errno,strerror(errno));
}
printf("after execl\n");
exit(0);
}
输出结果是进程中当前环境表的值
然后利用execle 函数打印新环境表的值
源程序execle.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(int argc, char *argv[])
{
printf("before execl\n");
char * envp[]={"AA=11","BB=22","cc=33",NULL};
if(execle("printenv","printenv",(char *)0,envp)==-1)
{
fprintf(stderr,"execle error ,error is %d,msg is %s\n",errno,strerror(errno));
}
printf("after execl\n");
exit(0);
}
输出结果如下:
可以看到环境表已经替换。
2 函数返回值。
上述6个函数,没有正确返回值,只有出错返回值,出错时,函数返回-1,并赋值errno,常见的错误码如下:
(1)E2BIG:envp,arg0等参数表太大
(2)EACCES :第一个参数执行权限不允许
(3)ENOENT:第一个参数文件或路径不存在
(4)ENOEXE 执行文件出错
3 补充
exec函数执行成功时,执行新程序的进程除了进程ID 不变外,还有下面项保持不变。
(1)进程ID 和父进程ID
(2)实际用户ID和实际组ID
(3)附加组ID
(4)进程组ID
(5)会话ID
(6)控制终端
(7)闹钟尚预留时间
(8)当前工作目录
(9)根目录
(10)文件模式创建屏蔽字
(11)文件锁
(12)进程信号屏蔽
(13)未处理信号
(14)资源限制
(15)tms_utime、tsm_stime、tms_cutime、tms_cstime
(16)environ(带‘e’函数的除外)。
参考资料:
(1)https://www.cnblogs.com/mickole/p/3187409.html
(2)https://man7.org/linux/man-pages/man3/execl.3.html
(3)https://man7.org/linux/man-pages/man2/execve.2.html