如果,想要深入的学习Linux系统调用里面的execve函数与标准C库中的exec函数族,还是需要去自己阅读Linux系统中的帮助文档。
具体输入命令:
man 2 execve
man 3 exec
即可查阅到完整的资料信息。
在Linux系统调用中,execve()函数被用来加载并执行一个新的程序文件。其原型为:
int execve(const char *filename, char *const argv[], char *const envp[]);
它的三个参数分别为:
标准C库中的exec()函数族,都是源自于Linux系统调用中的execve()函数。其他exec()函数也基本上有着相同的功能,只不过使用的参数形式略有差异。
接下来让我们来介绍一下标准C库的exec函数族
在标准C库中,exec函数族是一组用于执行外部程序的系统调用。该函数族包括以下函数:
int execl(const char *path, const char *arg0, …, const char *argn, (char *) NULL); - 执行一个指定路径下的程序,并传递给它命令行参数列表。
int execv(const char *path, char *const argv[]); - 执行一个指定路径下的程序,并传递给它命令行参数列表。
int execle(const char *path, const char *arg0, …, const char *argn, (char *) NULL, char *const envp[]); - 执行一个指定路径下的程序,并传递给它命令行参数列表以及环境变量。
int execve(const char *path, char *const argv[], char *const envp[]); - 执行一个指定路径下的程序,并传递给它命令行参数列表以及环境变量。
int execlp(const char *file, const char *arg0, …, const char *argn, (char *) NULL); - 在当前的环境变量中查找指定的可执行文件,并运行它。
int execvp(const char *file, char *const argv[]); - 在当前的环境变量中查找指定的可执行文件,并运行它。
这些函数的返回值是:
如果成功,则不会返回;
如果失败,则返回 -1,并设置 errno 变量来表明错误类型。
这些exec函数族调用之后,操作系统会将其所属进程替换为新程序。如果新程序运行成功,那么整个进程就会被新程序所替代。
exec 函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。
exec 函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程 ID 等一些表面上的信息仍保持原样,颇有些神似“三十六计”中的“金蝉脱壳”。看上去还是旧的躯壳,却已经注入了新的灵魂。只有调用失败了,它们才会返回 -1,从原程序的调用点接着往下执行。
下面我们再具体介绍一下execl函数的参数与具体的代码示例
execl() 是 exec() 函数族的一个成员,用于加载新程序并且使用一个参数列表来替换当前进程。以下是关于此函数的详细介绍:
int execl(const char *path, const char *arg, ... /* (char *) NULL */);
path: 字符串类型的指针,指向需启动进程的路径,可以是相对路径或绝对路径。
arg: 指向传递给被执行程序的参数字符串。
如果成功,该函数不会返回,因为调用它后当前进程已经被替换了。如果出现错误,该进程将保持运行,并返回 -1。
execl() 函数从指定的文件路径加载新程序,并把当前进程替换成新的程序代码。传递的第二个参数是一个字符串常量,表示传递给新程序的命令行参数,一般是可执行程序的名称。注意,参数列表以 (char*)NULL 结尾。
当该函数调用成功时,控制权就被移交到新程序代码中,在新的进程环境下继续向下执行。新程序代码的主函数接收这些参数,并根据需要执行实际操作。
需要注意的是,新程序会完全替换当前进程,即包括该函数调用之前在内存中所执行过的过程都将被删除。如果任何资源(如打开的文件)没有进行正确的关闭,则可能会导致意外的行为。
//导入头文件
#include
#include
#include int main (){pid_t pid = fork();if(pid < 0){perror("fork");return -1;}if(pid > 0){printf("i am parent process,pid = %d\n",getpid());sleep(1);}else if(pid == 0){int ret = execl("hello","hello",NULL);if(ret == -1){perror("execl");return -1;}printf("i am child process,pid = %d\n",getpid());}for(int i = 0; i < 3; ++i){printf("i = %d, pid = %d\n",i,getpid());}return 0;
}
输出结果:
nowcoder@nowcoder:~/Linux/lession19$ ./execl
i am parent process,pid = 13039
hello, world
i = 0, pid = 13039
i = 1, pid = 13039
i = 2, pid = 13039
execlp() 和 execl() 非常类似,不过它是根据 PATH 环境变量来查找目标二进制文件。这就意味着你可以只传递文件名而不需要整个路径。
以下是函数原型:
int execlp(const char *file, const char *arg0, ..., const char *argn, (char *) NULL)
具体使用方法请看代码:
#include
#include int main() {// 创建一个子进程,在子进程中执行exec函数族中的函数pid_t pid = fork();if(pid > 0) {// 父进程printf("i am parent process, pid : %d\n",getpid());sleep(1);}else if(pid == 0) {// 子进程execlp("ls", "-l", NULL);printf("i am child process, pid : %d\n", getpid());}for(int i = 0; i < 3; i++) {printf("i = %d, pid = %d\n", i, getpid());}return 0;
}
输出内容:
nowcoder@nowcoder:~/Linux/lession19$ ./execlp
i am parent process, pid : 13354
execl execl1.c execl.c execlp execlp1.c execlp.c hello hello.c
i = 0, pid = 13354
i = 1, pid = 13354
i = 2, pid = 13354