我们在讲,文件I/O的时候,简单提到过 exec 函数,讲到 vfork 的时候,也有用到。下面我们来详细介绍下它。
一、exec 函数族概述
与 fork 或 vfork 函数不同,exec 函数不是创建调用进程的子进程,而是创建一个新的进程取代调用进程自身。新进程会用自己的全部地址空间,覆盖调用进程的地址空间,但进程的 PID 保持不变。exec 只是用磁盘上的一个新程序替换了当前进程的正文段、数据段、堆段和栈段。
exec 不是一个函数而是一堆函数,一般称为 exec 函数族。它们的功能是一样的,用法也很相近,只是参数的形式和数量略有不同。
exec函数族的作用:根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
-
#include <unistd.h>
-
extern
char **environ;
-
int execl(const char *path, const char *arg, ...);
-
int execlp(const char *file, const char *arg, ...);
-
int execle(const char *path, const char *arg, ..., char *const envp[]);
-
int execv(const char *path, char *const argv[]);
-
int execvp(const char *file, char *const argv[]);
-
int execve(const char *path, char *const argv[], char *const envp[]);
-
int fexecve(int fd, char *const argv[], char *const envp[]);
-
7 个函数返回值:若出错,返回
-1;若成功,不返回
这些函数之间的第一个区别:
4 个函数取路径名作为参数,2 个函数则取文件名作为参数,最后一个取文件描述符作为参数。当指定 file 作为参数时:如果 file 中包含 /,则就将其视为路径名。否则就按 PATH 环境变量,在它所指定的各目录中搜索可执行文件。
环境变量,我们之前专门讲过,参看:UNIX再学习 -- 环境变量
PATH 变量包含了一张目录表(称为路径前缀),目录之间用冒号(:)分隔。例如,下面 name = value 环境字符串指定在 4 个目录中进程搜索。
PATH=/bin:/usr/bin:/usr/local/bin:.
最后的路径前缀 . 表示当前目录。(零长前缀也表示当前目录。在 value 的开始处可用 : 表示,在行中间则要用 :: 表示,在行尾以 : 表示)。
第二个区别与参数表的传递有关(l 表示表 (list), v 表示矢量(vector))。
函数 execl、execlp 和 execle 要求将新程序的每个命令行参数都说明为一个单独的参数,这种参数以空指针结尾。对于另外 4 个函数 (execv、execvp、execve、fexecve),则应先构造一个指向各参数的指针数组,然后将该数组地址作为这 4 个函数的参数。
第三个区别与向新程序传递环境表相关。
以 e 结尾的 3 个函数(execle 和 execve 和 fexecve)可以传递一个指向字符串指针数组的指针。其他四个函数则使用调用进程中的 environ 变量为新程序复制现存的环境。
这些函数,它们的函数名都是在 exec 后面加上一到两个字符后缀,不同的字符后缀代表不同的含义。
-l: 即 list ,新进程的命令行参数以字符指针列表 (const char *arge, ...) 的形式传入,列表以空指针结束。
-p: 即 path,若第一个参数中不包含“/”,则将其视为文件名,并根据 PATH 环境变量搜索该文件。
-e: 即 environment,新进程的环境变量以字符指针数组 (cahr *const envp[]) 的形式传入,数组以空指针结束,不指定环境变量则从调用进程复制。
-v: 即 vector,新进程的命令行参数以字符指针数组 (char *const argv[]) 的形式传入,数组以空指针结束。

到此,将 exec 函数族 7 个函数的区别和关系,简单讲了一下。下面我们就一一介绍:
二、execl 函数
-
#include <unistd.h>
-
extern
char **environ;
-
int execl(const char *path, const char *arg, ...);
-
若出错,返回
-1, 若成功,不返回
1、函数解析
2、示例说明
-
#include <stdio.h>
-
#include <unistd.h>
-
#include <stdlib.h>
-
-
int main (void)
-
{
-
printf (
"父进程开始执行\n");
-
-
pid_t pid = vfork ();
-
if (pid ==
-1)
-
perror (
"vfork"),
exit (
1);
-
if (pid ==
0)
-
{
-
printf (
"子进程开始执行\n");
-
if (execl (
"/bin/ls",
"ls",
"-l",
NULL) ==
-1)
-
perror (
"execl"), _exit (
1);
-
}
-
sleep (
1);
-
printf (
"父进程执行结束\n");
-
return
0;
-
}
-
输出结果:
-
父进程开始执行
-
子进程开始执行
-
总用量
16
-
-rwxr-xr-x
1 root root
7380 Apr
20
10:
22 a.out
-
-rw-r--r--
1 root root
383 Apr
20
10:
22 test.c
-
-rw-r--r--
1 root root
151 Apr
20
09:
56 test.c~
-
父进程执行结束
3、示例解析
-
#include <stdio.h>
-
#include <unistd.h>
-
#include <stdlib.h>
-
-
int main (void)
-
{
-
printf (
"父进程开始执行\n");
-
-
pid_t pid = vfork ();
-
if (pid ==
-1)
-
perror (
"vfork"),
exit (
1);
-
if (pid ==
0)
-
{
-
printf (
"子进程开始执行\n");
-
if (execl (
"/bin/ls",
"ls",
"-l",
0) ==
-1)
-
perror (
"execlp"), _exit (
1);
-
}
-
sleep (
1);
-
printf (
"父进程执行结束\n");
-
return
0;
-
}
-
编译:
-
警告: 函数调用中缺少哨兵 [-Wformat]
三、execlp 函数
-
#include <unistd.h>
-
int execlp(const char *file, const char *arg, ...);
-
若出错,返回
-1, 若成功,不返回
1、函数解析
2、示例说明
-
#include <stdio.h>
-
#include <unistd.h>
-
#include <stdlib.h>
-
-
int main (void)
-
{
-
printf (
"父进程开始执行\n");
-
-
pid_t pid = vfork ();
-
if (pid ==
-1)
-
perror (
"vfork"),
exit (
1);
-
if (pid ==
0)
-
{
-
printf (
"子进程开始执行\n");
-
if (execlp (
"ls",
"ls",
"-l",
NULL) ==
-1)
-
perror (
"execlp"), _exit (
1);
-
}
-
sleep (
1);
-
printf (
"父进程执行结束\n");
-
return
0;
-
}
-
输出结果:
-
父进程开始执行
-
子进程开始执行
-
总用量
16
-
-rwxr-xr-x
1 root root
7381 Apr
26
15:
02 a.out
-
-rw-r--r--
1 root root
480 Apr
26
15:
02 test.c
-
-rw-r--r--
1 root root
846 Apr
25
17:
01 test.c~
-
父进程执行结束
3、示例解析
开始就讲到,当指定 file 作为参数时,如果 file 中包含 /,则就将其视为路径名。否则就按 PATH 环境变量,在它所指定的各目录中搜索可执行文件。 这也就是 execlp 和 execl 的区别了。四、execle 函数
-
#include <unistd.h>
-
int execle(const char *path, const char *arg, ..., char * const envp[]);
-
若出错,返回
-1, 若成功,不返回
1、函数解析
2、示例说明
-
#include <stdio.h>
-
#include <unistd.h>
-
#include <stdlib.h>
-
-
int main (void)
-
{
-
printf (
"父进程开始执行\n");
-
-
pid_t pid = vfork ();
-
if (pid ==
-1)
-
perror (
"vfork"),
exit (
1);
-
if (pid ==
0)
-
{
-
printf (
"子进程开始执行\n");
-
if (execle (
"/bin/ls",
"ls",
"-l",
NULL,
NULL) ==
-1)
-
perror (
"execle"), _exit (
1);
-
}
-
sleep (
1);
-
printf (
"父进程执行结束\n");
-
return
0;
-
}
-
输出结果:
-
父进程开始执行
-
子进程开始执行
-
total
16
-
-rwxr-xr-x
1 root root
7381 Apr
26
15:
32 a.out
-
-rw-r--r--
1 root root
491 Apr
26
15:
32 test.c
-
-rw-r--r--
1 root root
488 Apr
26
15:
13 test.c~
-
父进程执行结束
3、示例解析
倒数第二个参数为,传递一个指向字符串指针数组的指针。五、execv 函数
-
#include <unistd.h>
-
int execv(const char *path, char *const argv[]);
-
若出错,返回
-1, 若成功,不返回
1、函数解析
2、示例说明
-
#include <stdio.h>
-
#include <unistd.h>
-
#include <stdlib.h>
-
-
int main (void)
-
{
-
char *arg[] = {
"ls",
"-l",
NULL};
-
printf (
"父进程开始执行\n");
-
-
pid_t pid = vfork ();
-
if (pid ==
-1)
-
perror (
"vfork"),
exit (
1);
-
if (pid ==
0)
-
{
-
printf (
"子进程开始执行\n");
-
if (execv (
"/bin/ls", arg) ==
-1)
-
perror (
"execv"), _exit (
1);
-
}
-
sleep (
1);
-
printf (
"父进程执行结束\n");
-
return
0;
-
}
-
输出结果:
-
父进程开始执行
-
子进程开始执行
-
总用量
16
-
-rwxr-xr-x
1 root root
7380 Apr
26
15:
48 a.out
-
-rw-r--r--
1 root root
505 Apr
26
15:
48 test.c
-
-rw-r--r--
1 root root
488 Apr
26
15:
13 test.c~
-
父进程执行结束
3、示例解析
六、execvp 函数
-
#include <unistd.h>
-
int execvp(const char *file, char *const argv[]);
-
若出错,返回
-1, 若成功,不返回
1、函数解析
2、示例说明
-
#include <stdio.h>
-
#include <unistd.h>
-
#include <stdlib.h>
-
-
int main (void)
-
{
-
char *arg[] = {
"ls",
"-l",
NULL};
-
printf (
"父进程开始执行\n");
-
-
pid_t pid = vfork ();
-
if (pid ==
-1)
-
perror (
"vfork"),
exit (
1);
-
if (pid ==
0)
-
{
-
printf (
"子进程开始执行\n");
-
if (execvp (
"ls", arg) ==
-1)
-
perror (
"execvp"), _exit (
1);
-
}
-
sleep (
1);
-
printf (
"父进程执行结束\n");
-
return
0;
-
}
-
输出结果:
-
父进程开始执行
-
子进程开始执行
-
总用量
16
-
-rwxr-xr-x
1 root root
7381 Apr
26
15:
54 a.out
-
-rw-r--r--
1 root root
502 Apr
26
15:
54 test.c
-
-rw-r--r--
1 root root
488 Apr
26
15:
13 test.c~
-
父进程执行结束
3、示例解析
当指定 file 作为参数时,如果 file 中包含 /,则就将其视为路径名。否则就按 PATH 环境变量,在它所指定的各目录中搜索可执行文件。这也就是 execvp 和 execv 的区别了。七、execve 函数
-
#include <unistd.h>
-
int execvpe(const char *file, char *const argv[], char *const envp[]);
-
若出错,返回
-1, 若成功,不返回
1、函数解析
execve() 用来执行参数 file 字符串所代表的文件路径,第二个参数是利用指针数组来传递给执行文件,并且需要以空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组。2、示例说明
-
#include <stdio.h>
-
#include <unistd.h>
-
#include <stdlib.h>
-
-
int main (void)
-
{
-
char *arg[] = {
"ls",
"-l",
NULL};
-
printf (
"父进程开始执行\n");
-
-
pid_t pid = vfork ();
-
if (pid ==
-1)
-
perror (
"vfork"),
exit (
1);
-
if (pid ==
0)
-
{
-
printf (
"子进程开始执行\n");
-
if (execve (
"/bin/ls", arg,
NULL) ==
-1)
-
perror (
"execve"), _exit (
1);
-
}
-
sleep (
1);
-
printf (
"父进程执行结束\n");
-
return
0;
-
}
-
输出结果:
-
父进程开始执行
-
子进程开始执行
-
total
16
-
-rwxr-xr-x
1 root root
7381 Apr
26
16:
12 a.out
-
-rw-r--r--
1 root root
513 Apr
26
16:
12 test.c
-
-rw-r--r--
1 root root
488 Apr
26
15:
13 test.c~
-
父进程执行结束
3、示例解析
第一个参数为 file,最后一个参数为 NULL,这没什么讲的了。八、fexecve 函数
-
#include <unistd.h>
-
int fexecve(int fd, char *const argv[], char *const envp[]);
-
若出错,返回
-1, 若成功,不返回
1、函数解析
九、find 和 xargs 命令相关内容
1、更改命令执行方式。
2、修改 ARG_MAX 的大小 (了解,系统不对实现不了)
(2)getconf ARG_MAX 查看ARG_MAX设置值大小,2097152
(3)调整ncargs占用字节:chdev -l sys0 -a ncargs=8 表示设置ncargs占用8字节,增加这个值就可以修改ARG_MAX参数的设置了
查找所有jpg文件,并重命名
-
root@zslf-
virtual-machine:/mnt/test
# ls
-
abc.txt def.txt ef.jpg hh.jpg images.tar.gz sd.jpg
-
root@zslf-
virtual-machine:/mnt/test
# find ./ -name "*.jpg" -type f | xargs -i cp {} {}.old
-
root@zslf-
virtual-machine:/mnt/test
# ls
-
abc.txt def.txt ef.jpg ef.jpg.old hh.jpg hh.jpg.old images.tar.gz sd.jpg sd.jpg.old
或使用 -exec
-
root@zslf-
virtual-machine:/mnt/test
# ls
-
abc.txt def.txt ef.jpg hh.jpg images.tar.gz sd.jpg
-
root@zslf-
virtual-machine:/mnt/test
# find ./ -name "*.jpg" -exec cp {} {}.old \;
-
root@zslf-
virtual-machine:/mnt/test
# ls
-
abc.txt def.txt ef.jpg ef.jpg.old hh.jpg hh.jpg.old images.tar.gz sd.jpg sd.jpg.old
十、最常见的错误
找不到文件或路径,此时errno被设置为ENOENT;
数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT;
没有对要执行文件的运行权限,此时errno被设置为EACCES。
- 点赞 1
- 收藏
- 分享