一、exec函数族简介
(1)fork子进程是为了执行新程序(fork创建了子进程后,子进程和父进程同时被OS调度执行,因此子进程可以单独的执行一个程序,这个程序宏观上将会和父进程程序同时进行)
(2)可以直接在子进程的if中写入新程序的代码。这样可以,但是不够灵活,因为我们只能把子进程程序的源代码贴过来执行(必须知道源代码,而且源代码太长了也不好控制),譬如说我们希望子进程来执行ls -la 命令就不行了(没有源代码,只有编译好的可执行程序),而exec族函数可以直接把一个编译好的可执行程序直接加载运行。
(3)我们有了exec族函数后,我们典型的父子进程程序是这样的:子进程需要运行的程序被单独编写、单独编译连接成一个可执行程序(叫hello),(项目是一个多进程项目)主程序为父进程,fork创建了子进程后在子进程中exec来执行hello,达到父子进程分别做不同程序同时(宏观上)运行的效果。
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...
/* (char *) NULL */);
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
int execle(const char *path, const char *arg, ...
/*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);
二、execl()函数
函数原型:
int execl(const char *path, const char *arg, ...
/* (char *) NULL */);
函数返回值:
The exec() functions return only if an error has occurred. The return value is -1, and errno is set to indicate the error.
成功则不返回值, 失败返回-1, 失败原因存于errno中,可通过perror()打印
execl代码示例:
实验过程:编译链接new.c生成new.o,在execl.c中使用execl函数调用new.o
/*
execl.c
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(void)
{
pid_t pid = -1;
pid_t ret = -1;
int status = -1;
pid = fork();
if (pid > 0)
{
// 父进程
printf("this is parent, 子进程id = %d.\n", pid);
printf("parent pid = %d.\n", getpid());
ret = wait(&status);
printf("子进程已经被回收,子进程pid = %d.\n", ret);
printf("子进程已经被回收,子进程status = %d.\n", status);
}
else if (pid == 0)
{
// 子进程
execl("./new", "./new", NULL); // ls -l -a 以NULL结尾表示完成 第一个参数是命令的名字,第二个才是参数
return 0;
}
else
{
perror("fork");
return -1;
}
return 0;
}
/*
new.c
*/
#include<stdio.h>
#include <unistd.h>
int main(void)
{
for(int i =0; i<3; i++)
{
sleep(1);
printf("this is child!\r\n");
}
return 0;
}
三、exec函数族使用细节
(1)execl和execv 这两个函数是最基本的exec,都可以用来执行一个程序,区别是传参的格式不同。execl是把参数列表(本质上是多个字符串,必须以NULL结尾)依次排列而成(l其实就是list的缩写),execv是把参数列表事先放入一个字符串数组中,再把这个字符串数组传给execv函数。
char *const ps_argv[] ={"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL};
execl("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execv("/bin/ps", ps_argv);
(2)execlp和execvp 这两个函数在上面2个基础上加了p,较上面2个来说,区别是:上面2个执行程序时必须指定可执行程序的全路径(如果exec没有找到path这个文件则直接报错),而加了p的传递的可以是file(也可以是path,只不过兼容了file。加了p的这两个函数会首先去找file,如果找到则执行执行,如果没找到则会去环境变量PATH所指定的目录下去找,如果找到则执行如果没找到则报错)(实验证明,会先到PATH指定的目录下面找)
char *const ps_argv[] ={"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL};
execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execvp("ps", ps_argv);
(3)execle和execvpe 这两个函数较基本exec来说加了e,函数的参数列表中也多了一个字符串数组envp形参,e就是environment环境变量的意思,和基本版本的exec的区别就是:执行可执行程序时会多传一个环境变量的字符串数组给待执行的程序。
char *const ps_envp[] ={"PATH=/bin:/usr/bin", "TERM=console", NULL};
execle("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL, ps_envp);
note:
main函数的原型其实不止是int main(int argc, char **argv),而可以是
int main(int argc, char **argv, char **env) 第三个参数是一个字符串数组,内容是环境变量。
如果用户在执行这个程序时没有传递第三个参数,则程序会自动从父进程继承一份环境变量(默认的,最早来源于OS中的环境变量);如果我们exec的时候使用execlp或者execvpe去给传一个envp数组,则程序中的实际环境变量是我们传递的这一份(取代了默认的从父进程继承来的那一份)
#include <stdio.h>
// env就是我们给main函数额外传递的环境变量字符串数组
int main(int argc, char **argv, char **env)
{
int i = 0;
printf("argc = %d.\n", argc);
while (NULL != argv[i])
{
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;
}
使用举例:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(void)
{
pid_t pid = -1;
pid_t ret = -1;
int status = -1;
pid = fork();
if (pid > 0)
{
// 父进程
printf("parent, 子进程id = %d.\n", pid);
}
else if (pid == 0)
{
// 子进程
char * const envp[] = {"AA=aaaa", "XX=abcd", NULL};
execle("hello", "hello", "-l", "-a", NULL, envp);
return 0;
}
else
{
perror("fork");
return -1;
}
return 0;
}
可见,使用execle和execvpe可以自己向执行进程传递环境变量,但不会继承Shell进程的环境变量,而其他四个exec函数则继承Shell进程的所有环境变量。
REF:
https://www.cnblogs.com/mickole/p/3187409.html
https://blog.csdn.net/amoscykl/article/details/80354052
朱有鹏课堂笔记