1.为什么需要exec函数
(1)fork子进程是为了执行新的程序
(2)可以直接在子进程的if中写入新程序的代码,但是不够灵活,因为只能将子进程程序的源代码贴过来执行(必须知道源码,而且源码太长不好控制)比如要执行 ls -la命令就不行。
(3)使用exec运行新的可执行程序(可以把一个编译好的的可执行程序直接家在运行)
(4)我们有了exec族函数后,我们典型的父子进程就是这样的:子进程需要运行的程序被单独编写、单独编译成一个可执行文件(叫hello),项目是一个多进程项目,主程序叫父进程,fork创建了一个子进程后在进程中exec来执行hello,达到父子进程分别作不同的程序(宏观上)同时运行。
2.exec族的6个函数介绍
(1)execl和execv
int execv(const char *pathname, char *const argv[]);
int execl(const char *pathname, const char *arg, ...
/* (char *) NULL */);
这两个函数是最基本的两个exec,都可以用来执行一个程序,区别在于传参的格式不同。execl是把参数列表(本质上是多个字符串,必须以NULL结尾)依次排列而成(l就是list的缩写),execv是把参数列表事先放入一个字符串数组中,再把这个字符串数组传给execv函数。
(2)execlp和execle
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
int execvp(const char *file, char *const argv[]);
这两函数在上面两个函数的基础上加了个p,较上面两个函数相比,上面两个函数必须加上可执行文件的全路径(如果exec没有找到path这个文件则直接报错),而加了p传递的可以是file(也可以说是path,只不过兼容file。加了p这两个函数会首先去找file,如果找到会直接执行,如果没找到则会去环境变量PATH下去找,找到则执行没找到则报错)
(3)execvp和execvpe
int execvpe(const char *file, char *const argv[],
char *const envp[]);
int execle(const char *pathname, const char *arg, ...
/*, (char *) NULL, char *const envp[] */);
这两个函数较基本的exec来说加了e,函数的参数列表中多了个字符串数组envp形参,e就是environment环境变量的意思。区别就是可执行文件时会所传一个环境变量的字符串给待执行的程序
3.(1)使用execl运行ls -l -a
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
pid_t pid = -1;
pid = fork();
if(pid > 0)
{
printf("parent 子进程pid = %d\n",pid);
}
else if(pid == 0)
{
//int execl(const char *pathname, const char *arg, .../* (char *) NULL */);
//execl("/usr/bin/ls","ls","-a","-l",NULL);
// execlp("ls","ls","-a","-l",NULL);
//int execv(const char *pathname, char *const argv[]);
char *const arg[] = {"ls","-a","-l",NULL};
execv("/usr/bin/ls",arg);
execvp("ls",arg);
return 0;
}
else
{
perror("fork");
return -1;
}
return 0;
}
(2)使用execl和execv运行自己写的程序
(2.1)编写自己的程序
#include <stdio.h>
int main(int argc,char **argv)
{
int i =0;
printf("hello world\n");
printf("argc = %d\n",argc);
while(argv[i] != NULL)
{
printf("argv[%d] = %s\n",i,argv[i]);
i++;
}
return 0;
}
将这个文件编译成名为hello的可执行文件
(2.2)编写实现函数
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
pid_t pid = -1;
pid = fork();
if(pid > 0)
{
printf("parent 子进程pid = %d\n",pid);
}
else if(pid == 0)
{
execl("./hello","aaa","bbb",NULL);
return 0;
}
else
{
perror("fork");
return -1;
}
return 0;
}
4.execlp和execvp
(1)加p和不加p的区别就是:加p之后我们不需要包含该可执行文件的全路径名。
#include <stdio.h>
int main(int argc,char **argv,char **env) //第三个参数是环境变量
{
int i =0;
printf("hello world\n");
printf("argc = %d\n",argc);
while(argv[i] != NULL)
{
printf("argv[%d] = %s\n",i,argv[i]);
i++;
}
i=0;
while(NULL != env[i])
{
printf("env[%d] = %s\n",i,env[i]);
i++;
}
return 0;
}
注:如果用户在执行这个程序时没有传递第三个参数,则程序会自动从父进程继承一份环境变量(默认最早来源于OS中的环境变量),如果我们给这个envp传递了一份环境变量,这原来默认的那份环境变量就被这份环境变量替换了。
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
pid_t pid = -1;
pid = fork();
if(pid > 0)
{
printf("parent 子进程pid = %d\n",pid);
}
else if(pid == 0)
{
/int execle(const char *pathname, const char *arg, .../*, (char *) NULL, char *const envp[] */)
char *const env[] = {"AA = aaaa","XX = abcd",NULL};
execle("./hello","ls","-a","-l",NULL,env);
return 0;
}
else
{
perror("fork");
return -1;
}
return 0;
}