linux 之进程基础 (四)、进程API之进程执行exec函数簇
4. 进程API之进程执行exec函数簇
int execl(const char *path, const char *arg, ...
/* (char *) NULL */);
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
int execle(const char *path, const char *arg, ...
/*, (char *) NULL, 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[]);
// 函数返回值: -1 为出错
4.1 exec()函数簇说明
4.1.1
可执行文件查找方式:
- 带p:对文件查找方式为p的(以p结尾的函数)可以只给出文件名,系统会自动从环境变量“$PATH”所包含的路径中进行查找。
- 不带p:需要提供完整的路径名。
向新程序传递环境表的方式:
- 带e:接受一个附加的环境参数列表,参数格式为NULL结尾的字符串数组,且字符串的格式为“VARIABLE=value”。
- 不带e: 不能接受附加的环境变量。
向新程序传递参数表的方式 :
- 带l:以函数名的第五位字母来区分,字母为“l”(list)的表示:逐个列举的方式;
- 带v:字母为“v”(vertor)的表示将所有参数构造成指针数组传递,其语法为char *const argv[]
4.1.2 exec 函数簇功能
提供了一种在一个进程中启动另一个程序的方法。另一个程序可以是一个二进制文件,也可以是一个脚本。
4.2 使用exec函数簇的函数
4.2.1 什么时候使用exec函数簇
- 当当前进程已经不能再为系统和用户做事情时,它就可以调用exec函数簇,去执行新的程序。(当fork的子进程没用的时候调用exec)
- 当某个进程想要执行另一个程序,它就可以在fork一个子进程后,在子进程中直接调用exec函数簇去执行想要执行的程序(执行fork 后马上调用exec)
4.2.2 这样做的好处
当一个进程不再起作用时,按照正常的逻辑应该结束该进程,然后回收其资源。但是我们可以不让他结束,而是利用它的task_struct空壳取装载另外一个程序。(对应上述第一种情况)
就是使用fork创建一个进程后,原本进程中的task_struct 结构体是复制父进程而来的。 每当进程调用一种exec函数时,该进程完全由新程序代换。也就是将指定的程序的 堆栈、数据段等内容,覆盖原本的task_struct结构体。新程序从main函数开始执行。exec并不创建新进程,所以前后进程ID也不会变,也就是task_struct中的pid 字段等保持不变。exec只是用另一个新程序替换了当前进程的正文、数据、堆和栈段。(对应上述第二种情况)
相当于更加快速的执行一个程序。 例如exec函数执行的程序是a.out。 如果使用./a.out执行这个程序的话,需要内核为其开辟空间等。但是如果使用exec函数簇的话,就直接将该函数的堆栈以及代码段数据用用新程序填充了。因此省了许多内核的操作,效率提高了很多。
4.3 使用exec函数簇的注意事项
在使用exec函数族时,一定要加上错误判断语句:
常见的错误原因有:
1)找不到文件或路径,此时errno被设置为ENOENT
2)数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT
3)没有对应可执行文件的运行权限,此时errno被设置为EACCESS
4)这6个函数中只用execve是系统调用,其他5个都是库函数,它们只是在execve基础之上进行封装,最终还是会调用该函数。
4.4 环境表
每个程序都接收到一张环境变量表。与参数表一样,环境表也是一个字符指针数组,其中每个指针包含一个以NULL结束的字符串的地址。全局变量environ则包含了该指针数组的地址。
实际上main 函数的真正形式 最后一个参数 是环境变量表。