社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
exec函数族用来执行一个程序(execute program).
下述中的引用内容如无特别说明, 均来自man page
一共有6个, 其函数原型为:
#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 *filename, char *const argv[], char *const envp[]);
其中execve为系统调用, 另外5个为c库函数.
The functions described in this manual page are front-ends for execve(2).
其中
l
的意思是list args
, 即列出参数p
的意思是使用环境变量PATH
v
的意思是argument vector
, 参数向量, 也就是参数用数组来表示(argv
, envp
)e
的意思传入环境变量(由envp
指定)exec函数族中的函数会用新进程的映像替换当前进程的映像.
The exec() family of functions replaces the current process image with a new process image.
所以exec**执行成功后, 后面的代码不会执行, 因为已经被新进程的代码替换了.
下面用这6个函数来实现ls -l
的功能.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(int argc, const char *argv[])
{
pid_t pid;
int ret;
pid = fork();
if (pid < 0) {
perror("fork() error");
exit(1);
}
if (pid == 0) { // child
char* const argv[] = {"ls", "-l", NULL}; // 1
char* const envp1[] = {"PATH=.", "XX=YY", NULL}; // 2
char* const envp2[] = {"PATH=/bin:/sbin", "XX=YY", NULL}; // 3
char* const envp3[] = {"AA=BB", "XX=YY", NULL}; // 4
// execlp("ls", "ls", "-l", NULL);
// execl("/bin/ls", "ls", "-l", NULL);
// execle("ls", "ls", "-l", NULL, envp);
// execvp("ls", argv);
// execv("/bin/ls", argv);
// https://stackoverflow.com/questions/33598869/execve-not-taking-environment-parameters
// execve("ls", argv, envp2); // 5 evnp is useless, why?
// execve("/bin/ls", argv, envp2); // 6
// execve("envp.out", argv, envp3); // 7
// 如果exec**执行失败, 才会到这来
perror("child exec error occurred");
exit(2);
} else { // parent
ret = wait(NULL); // wait for child change state
if (ret == -1) {
perror("wait() error");
}
}
return 0;
}
我们重点关注这个execve
, 因为它是系统调用, 其他几个都是调用这个函数实现的.
句子5(注释5对应的那一句)execve("ls", argv, envp2)
, 在envp2中指定了PATH环境变量,
看它能不能找到ls
, 然而执行会报错, 说是没有找到ls
在哪里.
然后SF上有人说, 这个envp不会起实质的作用, 参见链接: https://stackoverflow.com/questions/33598869/execve-not-taking-environment-parameters.
所以用execve执行系统命令, 还是要写绝对路径.
句子7execve("envp.out", argv, envp3)
, 这里执行了自己写的一个程序, 源码如下:
// envp.c
#include <stdio.h>
// https://stackoverflow.com/questions/10321435/is-char-envp-as-a-third-argument-to-main-portable
int main(int argc, char* argv[], char* env[])
{
int i = 0;
for (i = 0; i < argc; i++) {
printf("evnp.c, argv[%d], %sn", i, argv[i]);
}
for (i = 0; env[i]; i++) {
printf("evnp.c, env[%d], %sn", i, env[i]);
}
return 0;
}
envp.c文件只是为了打印传入的argv
和env
的值.
注意这个env不是POSIX标准的, 但是却被广泛使用. 参见: https://stackoverflow.com/questions/10321435/is-char-envp-as-a-third-argument-to-main-portable
打开语句7, 执行exec.out, 输出如下:
evnp.c, argv[0], ls
evnp.c, argv[1], -l
evnp.c, env[0], AA=BB
evnp.c, env[1], XX=YY
也就是说, envp.out在被执行时, 收到了传入的char* const argv[] = {"ls", "-l", NULL}
和char* const envp3[] = {"AA=BB", "XX=YY", NULL}
.
此时我们就明白了这个argv和envp是如何传入的了.
事实上, 这个execve
我们经常用到, 不过它是系统调用, 一般看不出来, 用strace命令可以打印出执行shell命令时产生的系统调用, 如ls
的产生的系统调用可以使用strace ls
来查看(只截取了部分):
发现, 执行ls
产生的第一个系统调用就是execve("/bin/ls", ["ls"], [/* 28 vars */])
, 看它传入的也是绝对路径/bin/ls
.
总结:
p: PATH
, l: list args
, e: env
, v: vector
, 这样好记.参考:
欢迎补充指正!
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!