execl族函数
int execl(const char * path,const char * arg,…);
int execle(const char * path,const char * arg,char * const envp[]);
int execlp(const char * file,const char * arg,…);
int execv(const char * path,char * const argv[]);
int execve(const char * path,char * const argv[],char * const envp[]);
int execvp(const char * file,char * const argv[]);
execl族函数除了execle()和execve()不常用以外,其他的都经常用。execl族函数的作用在于可以在进程或者代码中执行可执行文件,也就是以.exe结尾的文件。从而使执行的可执行文件变成一个新的进程。
#include <unistd.h>
这是execl族的头文件。
path:要执行的程序路径。可以是绝对路径或者是相对路径。在execv、execve、execl和execle这4个函数中,使用带路径名的文件名作为参数。
file:要执行的程序名称。如果该参数中包含“/”字符,则视为路径名直接执行;否则视为单独的文件名,系统将根据PATH环境变量指定的路径顺序搜索指定的文件。
argv:命令行参数的矢量数组。
envp:带有该参数的exec函数可以在调用时指定一个环境变量数组。其他不带该参数的exec函数则使用调用进程的环境变量。
arg:程序的第0个参数,即程序名自身。相当于argv[0]。
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
先写一个最简单的代码,然后编译成可执行文件hello。
CLC@Embed_Learn:~/test_11.7$ gcc hello.c -o hello
CLC@Embed_Learn:~/test_11.7$ ls
a.out hello hello.c test1.c
我们可以看到多了一个hello的可执行文件,这里看不出来,在linux系统下可执行文件会变成绿色。接下来使用一下exec()函数。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
if(execl("./hello",NULL,NULL) == -1)
{
printf("error\n");
}
printf("ok\n");
return 0;
}
execl()函数我这里有3个参数,第一个参数是文件路径,哦这里写的是当前路径下hello这个可执行文件,后面两个都是NULL。其实execl()函数可以有很多参数,但是最后一个参数必须是NULL,中间的参数是execl()函数执行的可执行文件的参数。看一下效果。
CLC@Embed_Learn:~/test_11.7$ gcc test2.c
CLC@Embed_Learn:~/test_11.7$ ./a.out
hello world
我们发现,他也打印了一个hello world。因为hello这个可执行文件里面的代码就是打印一个hello world。
之前说过,当execl()函数执行一个可执行文件之后,它就会变成一个新进程,我们也可以看到最后的一段打印ok的代码它并不会执行。如果执行失败会是什么效果?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
if(execl("./hello.c",NULL,NULL) == -1)
{
printf("error\n");
}
printf("ok\n");
return 0;
}
代码做了一点修改,把hello可执行文件改成了hello.c,但是我的目录下并不存在这个可执行文件,所有理论上会执行失败。
CLC@Embed_Learn:~/test_11.7$ gcc test2.c
CLC@Embed_Learn:~/test_11.7$ ./a.out
error
ok
CLC@Embed_Learn:~/test_11.7$
执行后,终端打印了error和ok,这就说明新的进程没有创建成功,代码会从头执行到尾,ok也被打印了出来。
execl()函数我目前使用了3个参数,第一个是可执行文件的路径,第三个也就是最后一个必须是NULL ,那么中间的参数怎么使用呢?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
if(execl("/bin/ls","ls","-l",NULL) == -1)
{
printf("error\n");
}
printf("ok\n");
return 0;
}
我这里中间给了2个参数,一个是ls,一个是-l。我们可执行文件是linux系统的bin目录下面的ls可执行文件。在我们使用linux系统时,输入ls它会列出当前目录下的文件,如果再加上-l选项,它会把每个文件的详细信息列出来。
我代码里的execl()函数演示的就是这个效果。可以对比一下。
CLC@Embed_Learn:~/test_11.7$ ls -l
total 40
-rwxr-xr-x 1 CLC book 8428 Nov 7 15:43 a.out
-rwxr-xr-x 1 CLC book 8377 Nov 7 14:50 hello
-rw-r--r-- 1 CLC book 74 Nov 7 14:49 hello.c
-rw-r--r-- 1 CLC book 363 Nov 7 14:52 test1.c
-rw-r--r-- 1 CLC book 172 Nov 7 15:38 test2.c
-rw-r--r-- 1 CLC book 175 Nov 7 15:49 test3.c
CLC@Embed_Learn:~/test_11.7$ gcc test3.c
CLC@Embed_Learn:~/test_11.7$ ./a.out
total 40
-rwxr-xr-x 1 CLC book 8428 Nov 7 15:49 a.out
-rwxr-xr-x 1 CLC book 8377 Nov 7 14:50 hello
-rw-r--r-- 1 CLC book 74 Nov 7 14:49 hello.c
-rw-r--r-- 1 CLC book 363 Nov 7 14:52 test1.c
-rw-r--r-- 1 CLC book 172 Nov 7 15:38 test2.c
-rw-r--r-- 1 CLC book 175 Nov 7 15:49 test3.c
CLC@Embed_Learn:~/test_11.7$
可以看到,效果一模一样。接下来使用一下execlp()函数。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
if(execlp("ls","ls","-l",NULL) == -1)
{
printf("error\n");
}
printf("ok\n");
return 0;
}
代码做了一些小调整,execlp()函数对比execl()函数来说,它的好处就是可以在整个linux系统中搜索你这个可执行文件,不需要加上目录,我们可以看看效果。
CLC@Embed_Learn:~/test_11.7$ gcc test4.c
CLC@Embed_Learn:~/test_11.7$ ./a.out
total 44
-rwxr-xr-x 1 CLC book 8429 Nov 7 15:54 a.out
-rwxr-xr-x 1 CLC book 8377 Nov 7 14:50 hello
-rw-r--r-- 1 CLC book 74 Nov 7 14:49 hello.c
-rw-r--r-- 1 CLC book 363 Nov 7 14:52 test1.c
-rw-r--r-- 1 CLC book 172 Nov 7 15:38 test2.c
-rw-r--r-- 1 CLC book 175 Nov 7 15:49 test3.c
-rw-r--r-- 1 CLC book 171 Nov 7 15:54 test4.c
CLC@Embed_Learn:~/test_11.7$
没什么区别。其他的族函数就不过多介绍了。
system函数
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>
int system(const char * cmdstring)
{
pid_t pid;
int status;
if(cmdstring == NULL){
return (1);
}
if((pid = fork())<0){
status = -1;
}else if(pid = 0){
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
-exit(127); //子进程正常执行则不会执行此语句
}else{
while(waitpid(pid, &status, 0) < 0){
if(errno != EINTER){
status = -1;
break;
}
}
}
return status;
}
上面这段代码是system()函数的源码,首先创建一个子进程,然后调用execl()函数来创建一个新进程取执行可执行文件。
我们可以看到execl()函数有5个参数,第五个参数(char*)0其实就是NULL的意思。第一个参数是可执行文件的目录,是linux系统bin目录下的sh可执行文件。第二和第三个参数分别是sh和-c,同时也是sh这个可执行文件的参数。
举个例子,平时我们编写代码先是用gcc命令把代码编译成可执行文件,然后再用./a.out来运行。这是我们通常的用法,但是你用sh -c指令也可以运行。
CLC@Embed_Learn:~/test_11.7$ gcc hello.c -o hello
CLC@Embed_Learn:~/test_11.7$ ./hello
hello world
CLC@Embed_Learn:~/test_11.7$ sh -c ./hello
hello world
接下来我们再带入这个execl()函数。
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
第二和第三个参数就相当于你在终端输入sh -c这个指令,cmdstring是system()函数的参数。sh -c ./hello这段命令就相当于system("./hello"),举个例子。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
if(system("./hello") == -1)
{
printf("error\n");
}
return 0;
}
CLC@Embed_Learn:~/test_11.7$ gcc test5.c
CLC@Embed_Learn:~/test_11.7$ ./a.out
hello world
CLC@Embed_Learn:~/test_11.7$
效果都是一样的,简单点来说,system()函数的底层就是execl()函数。