(1)exec函数说明

fork函数是用于创建一个子进程,该子进程几乎是父进程的副本,而有时我们希望子进程去执行另外的程序,exec函数族就提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换了。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行脚本文件。

(2)在Linux中使用exec函数族主要有以下两种情况

当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用任何exec 函数族让自己重生。

如果一个进程想执行另一个程序,那么它就可以调用fork函数新建一个进程,然后调用任何一个exec函数使子进程重生。

(3)exec函数族语法

实际上,在Linux中并没有exec函数,而是有6个以exec开头的函数族,下表列举了exec函数族的6个成员函数的语法。

所需头文件

#include <unistd.h>

函数说明

执行文件

函数原型

int execl(const char *pathname, const char *arg, ...)

int execv(const char *pathname, char *const argv[])

int execle(const char *pathname, const char *arg, ..., char *const envp[])

int execve(const char *pathname, char *const argv[], char *const envp[])

int execlp(const char *filename, const char *arg, ...)

int execvp(const char *filename, char *const argv[])

函数返回值

成功:函数不会返回

出错:返回-1,失败原因记录在error中

这6 个函数在函数名和使用语法的规则上都有细微的区别,下面就可执行文件查找方式、参数表传递方式及环境变量这几个方面进行比较说明。

①    查找方式:上表其中前4个函数的查找方式都是完整的文件目录路径(pathname),而最后2个函数(也就是以p结尾的两个函数)可以只给出文件名,系统就会自动从环境变量“$PATH”所指出的路径中进行查找。

前4个取路径名做参数,后两个则取文件名做参数。
当指定filename做参数时:
a. 如果filename中包含/,则将其视为路径名
b. 否则就按PATH环境变量搜索可执行文件。

②    参数传递方式:exec函数族的参数传递有两种方式,一种是逐个列举(l)的方式,而另一种则是将所有参数整体构造成指针数组(v)进行传递。

在这里参数传递方式是以函数名的第5位字母来区分的,字母为“l”(list)的表示逐个列举的方式,字母为“v”(vertor)的表示将所有参数整体构造成指针数组传递,然后将该数组的首地址当做参数传给它,数组中的最后一个指针要求是NULL。读者可以观察execl、execle、execlp的语法与execv、execve、execvp的区别。

③    环境变量:exec函数族使用了系统默认的环境变量,也可以传入指定的环境变量。这里以“e”(environment)结尾的两个函数execle、execve就可以在envp[]中指定当前进程所使用的环境变量替换掉该进程继承的所以环境变量。

(3)PATH环境变量说明

PATH环境变量包含了一张目录表,系统通过PATH环境变量定义的路径搜索执行码,PATH环境变量定义时目录之间需用用“:”分隔,以“.”号表示结束。PATH环境变量定义在用户的.profile或.bash_profile中,下面是PATH环境变量定义的样例,此PATH变量指定在“/bin”、“/usr/bin”和当前目录三个目录进行搜索执行码。

PATH=/bin:/usr/bin:.

export $PATH

(4)进程中的环境变量说明

在Linux中,Shell进程是所有执行码的父进程。当一个执行码执行时,Shell进程会fork子进程然后调用exec函数去执行执行码。Shell进程堆栈中存放着该用户下的所有环境变量,使用execl、execv、execlp、execvp函数使执行码重生时,Shell进程会将所有环境变量复制给生成的新进程;而使用execle、execve时新进程不继承任何Shell进程的环境变量,而由envp[]数组自行设置环境变量。

变量是计算机系统用于保存可变值的数据类型,我们可以直接通过变量名称来提取到对应的变量值。在 Linux 系统中,环境变量是用来定义系统运行环境的一些参数,比如每个用户不同的家目录(HOME)、邮件存放位置(MAIL)等。

值得一提的是,Linux 系统中环境变量的名称一般都是大写的,这是一种约定俗成的规范。

我们可以使用 env 命令来查看到 Linux 系统中所有的环境变量,执行命令如下:

[root@localhost ~]# env

ORBIT_SOCKETDIR=/tmp/orbit-root

HOSTNAME=livecd.centos

GIO_LAUNCHED_DESKTOP_FILE_PID=2065

TERM=xterm

SHELL=/bin/bash

......

Linux 系统能够正常运行并且为用户提供服务,需要数百个环境变量来协同工作,但是,我们没有必要逐一学习每个变量,这里给大家列举了 10 个非常重要的环境变量,如表 1 所示。

表 1 Linux系统中重要的10个环境变量
环境变量名称 作用
HOME 用户的主目录(也称家目录)
SHELL  用户使用的 Shell 解释器名称
PATH 定义命令行解释器搜索用户执行命令的路径
EDITOR 用户默认的文本解释器
RANDOM 生成一个随机数字
LANG 系统语言、语系名称
HISTSIZE 输出的历史命令记录条数
HISTFILESIZE 保存的历史命令记录条数
PS1 Bash解释器的提示符
MAIL 邮件保存路径

Linux 作为一个多用户多任务的操作系统,能够为每个用户提供独立的、合适的工作运行环境,因此,一个相同的环境变量会因为用户身份的不同而具有不同的值。



例如,使用下述命令来查看 HOME 变量在不同用户身份下都有哪些值:

[root@localhost ~]# echo $HOME

/root

[root@localhost ~]# su - user1  <--切换到 user1 用户身份

[user1@localhost ~]$ echo $HOME

/home/user1

这里的 su 命令可以临时切换用户身份。

(5)exec函数族关系

第4位

统一为:exec

第5位

l:参数传递为逐个列举方式

execl、execle、execlp

v:参数传递为构造指针数组方式

execv、execve、execvp

第6位

e:可传递新进程环境变量

execle、execve

p:可执行文件查找方式为文件名

execlp、execvp

事实上,这6个函数中真正的系统调用只有execve,其他5个都是库函数,它们最终都会调用execve这个系统调用,调用关系如下图12-11所示:

图12-11 exec函数族关系图

(6) exec调用举例如下

  1. char *const ps_argv[] ={"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL};
  2.  
  3. char *const ps_envp[] ={"PATH=/bin:/usr/bin", "TERM=console", NULL};
  4.  
  5. execl("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
  6.  
  7. execv("/bin/ps", ps_argv);
  8.  
  9. execle("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL, ps_envp);
  10.  
  11. execve("/bin/ps", ps_argv, ps_envp);
  12.  
  13. execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
  14.  
  15. execvp("ps", ps_argv);

请注意exec函数族形参展开时的前两个参数,第一个参数是带路径的执行码(execlp、execvp函数第一个参数是无路径的,系统会根据PATH自动查找然后合成带路径的执行码),第二个是不带路径的执行码,执行码可以是二进制执行码和Shell脚本。

(7)exec函数族使用注意点

在使用exec函数族时,一定要加上错误判断语句。因为exec很容易执行失败,其中最常见的原因有:

①    找不到文件或路径,此时errno被设置为ENOENT。

②    数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT。

③    没有对应可执行文件的运行权限,此时errno被设置为EACCES。

下面分别是对6个exec家族函数的调用

execl

#include<stdio.h>
#include<unistd.h>
int main(int argc,char *argv[]){ printf("exercise execl\n");
int rc=fork();
if(rc==0){
if((execl("/bin/ls","ls","./",NULL))<0){
return -1;
} } return 0; }

execle

#include<stdio.h>
#include<unistd.h>
int main(int argc,char *argv[]){
char *envp1[]={"PATH=/bin:/bin/usr",NULL} ; //environment variable
printf("exercise execl\n");
int rc=fork();
if(rc==0){
if((execle("/bin/ls","ls","./",NULL,envp1))<0){
return -1;
} }else{
printf("output or not output");
}
return 0; }
[root@localhost codec5]# ./t4
exercise execl
output or not output[root@localhost codec5]# t1 t1.c t2 t2.c t3 t3.c t4 t4.c

execlp

#include<stdio.h>
#include<unistd.h>
int main(int argc,char *argv[]){
//char *envp1[]={"PATH=/bin:/bin/usr",NULL} ; //environment variable
printf("exercise execl\n");
int rc=fork();
if(rc==0){
if((execlp("ls","ls",NULL))<0){ //execlp 第五个p代表可以自己查找地
return -1; //址,只需要输入文件名子即可
} }else{
printf("output or not output");
}
return 0; } [root@localhost codec5]# ./t4
exercise execl
output or not output[root@localhost codec5]# t1 t1.c t2 t2.c t3 t3.c t4 t4.c

execv

#include<stdio.h>
#include<sys/wait.h>
#include<unistd.h>
int main(int argc,char *argv[]){ char *argv1[]={"ls","-l",NULL};
//char *envp1[]={"PATH=/bin:/bin/usr",NULL} ; //environment variable
printf("exercise execl\n");
int rc=fork();
if(rc==0){
if((execv("/bin/ls",argv1))<0){ //execv 第4个v代表传入字符串指针数组
return -1;
} }else{
wait(NULL);
printf("output or not output");
}
return 0; } 运行结果如下
[root@localhost codec5]# ./t4
exercise execl
总用量 48
-rwxr-xr-x. 1 root root 5416 10月 17 22:41 t1
-rw-r--r--. 1 root root 570 10月 17 22:41 t1.c
-rwxr-xr-x. 1 root root 5338 10月 17 23:53 t2
-rw-r--r--. 1 root root 443 10月 17 23:53 t2.c
-rwxr-xr-x. 1 root root 5083 10月 15 23:48 t3
-rw-r--r--. 1 root root 284 10月 15 23:48 t3.c
-rwxr-xr-x. 1 root root 5175 10月 18 20:09 t4
-rw-r--r--. 1 root root 456 10月 18 20:09 t4.c
output or not output[root@localhost codec5]#

execve

#include<stdio.h>
#include<sys/wait.h>
#include<unistd.h>
int main(int argc,char *argv[]){ char *argv1[]={"ls","-l",NULL};
char *envp1[]={"PATH=/bin:/bin/usr",NULL} ; //environment variable
printf("exercise execl\n");
int rc=fork();
if(rc==0){
if((execve("/bin/ls",argv1,envp1))<0){ //execve 第6个e代表可以改变环境变量
return -1;
} }else{
wait(NULL);
printf("output or not output");
}
return 0; } [root@localhost codec5]# ./t4
exercise execl
total 48
-rwxr-xr-x. 1 root root 5416 Oct 17 22:41 t1
-rw-r--r--. 1 root root 570 Oct 17 22:41 t1.c
-rwxr-xr-x. 1 root root 5338 Oct 17 23:53 t2
-rw-r--r--. 1 root root 443 Oct 17 23:53 t2.c
-rwxr-xr-x. 1 root root 5083 Oct 15 23:48 t3
-rw-r--r--. 1 root root 284 Oct 15 23:48 t3.c
-rwxr-xr-x. 1 root root 5212 Oct 18 20:14 t4
-rw-r--r--. 1 root root 459 Oct 18 20:14 t4.c
output or not output[root@localhost codec5]# cat t4.c

execvp

#include<stdio.h>
#include<unistd.h>
int main(int argc,char *argv[]){ char *argv1[]={"ls","-l",NULL};
char *envp1[]={"PATH=/bin:/bin/usr",NULL} ; //environment variable
printf("exercise execl\n");
int rc=fork();
if(rc==0){
if((execvp("ls",argv1))<0){ //execvp 第4个v代表传入字符串指针数组
return -1;
} }else{
printf("output or not output");
}
return 0; } [root@localhost codec5]# ./t4
exercise execl
output or not output[root@localhost codec5]# 总用量 48
-rwxr-xr-x. 1 root root 5416 10月 17 22:41 t1
-rw-r--r--. 1 root root 570 10月 17 22:41 t1.c
-rwxr-xr-x. 1 root root 5338 10月 17 23:53 t2
-rw-r--r--. 1 root root 443 10月 17 23:53 t2.c
-rwxr-xr-x. 1 root root 5083 10月 15 23:48 t3
-rw-r--r--. 1 root root 284 10月 15 23:48 t3.c
-rwxr-xr-x. 1 root root 5120 10月 18 20:06 t4
-rw-r--r--. 1 root root 418 10月 18 20:06 t4.c

参考的有一篇特别好的博客:https://blog.csdn.net/amoscykl/article/details/80354052

时间:  2020-10-18  20:33:50

exec 家族库函数以及系统调用(execl,execle,execlp and execv,execvp,execve)的更多相关文章

  1. 在进程中执行新代码 execl、execle、execlp、execv、execve和execvp函数

    摘要:本文主要讲述怎样在进程中执行新代码,以及exec系列函数的基本用法. 在进程中执行新代码 用函数fork创建子进程后,假设希望在当前子进程中运行新的程序,能够调用exec函数运行还有一个程序.当 ...

  2. linux系统编程之进程(五):exec系列函数(execl,execlp,execle,execv,execvp)使用

    本节目标: exec替换进程映像 exec关联函数组(execl.execlp.execle.execv.execvp) 一,exec替换进程映像 在进程的创建上Unix采用了一个独特的方法,它将进程 ...

  3. exec系列函数(execl,execlp,execle,execv,execvp)使用

    本节目标: exec替换进程映像 exec关联函数组(execl.execlp.execle.execv.execvp) 一,exec替换进程映像 在进程的创建上Unix采用了一个独特的方法,它将进程 ...

  4. Linux 库函数与系统调用的关系与区别

    上周总结了<C 标准库的基础 IO>,其实这些功能函数通过「系统调用」也能实现相应功能.这次文章并不是要详细介绍各系统调用接口的使用方法,而是要深入理解「库函数」与「系统」调用之间的关系和 ...

  5. 问题解决:补充安装c语言的库函数和系统调用man手册

    问题解决:补充安装c语言的库函数和系统调用man手册 ​ 今日份麻麻~上课时大家的Ubuntu都可以通过man查到关于stat的库函数,但是我的Kali查出来是这样: ​ 询问老师之后得知需要去安装相 ...

  6. execl, execlp, execle, execv, execvp - 执行某个文件

    总览 (SYNOPSIS) #include <unistd.h> extern char **environ; int execl( const char *path, const ch ...

  7. 转---python os.exec*()家族函数的用法

    execl(file, arg0,arg1,...) 用参数列表arg0, arg1 等等执行文件 execv(file, arglist) 除了使用参数向量列表,其他的和execl()相同 exec ...

  8. UC编程:通过fwrite()和write()比较标准库函数和系统调用的速度

    fwrte是C标准库中提供的函数,是对write函数的扩展与封装,write则是Unix系统提供的函数.按照常理来讲,系统调用肯定比使用库快的多,但是事实正好相反 Why?原因就在于缓冲的问题,fwi ...

  9. glibc库函数,系统调用API

    glibc封装了大部分系统API,我们一般都是使用glibc封装的接口进行系统调用,碰到一些没有封装的接口,可以通过这个 函数syscall 进行系统调用.   /* Invoke `system c ...

随机推荐

  1. 2014年 实验三 B2B模拟实验(一)

    [实验目的] ⑴.熟悉企业网络银行和电子证书的应用 ⑵.通过B2B模拟实验掌握B2B的交易过程 [实验条件] ⑴.个人计算机一台 ⑵.计算机通过局域网形式接入互联网 (3).电子商务模拟实验室软件包. ...

  2. Nuxt+Vue聊天室|nuxt仿微信App界面|nuxt.js聊天实例

    一.项目简述 nuxt-chatroom 基于Nuxt.js+Vue.js+Vuex+Vant+VPopup等技术构建开发的仿微信|探探App界面社交聊天室项目.实现了卡片式翻牌滑动.消息发送/emo ...

  3. EV加密播放器的分析过程+过虚拟机方法

    开启了OD载入播放器进行分析,发现如下问题:1.播放器会进行翻录检测2.防止虚拟机播放3.视频播放后,可直接对内存操作提取出源视频翻录检测:主要是对指定的文件名或进程名对比虚拟机检测:是针对虚拟机特征 ...

  4. CentOS 7的安装与部署 01

    01 虚拟软件的安装与配置 虚拟机(Virtual Machine)指通过软件模拟的具有完整硬件系统功能的.运行在一个完全隔离环境中的完整计算机系统.在实体计算机中能够完成的工作在虚拟机中都能够实现. ...

  5. 【Luogu】P1613 跑路

    [Luogu]P1613 跑路 一.题目 题目描述 小A的工作不仅繁琐,更有苛刻的规定,要求小A每天早上在6:00之前到达公司,否则这个月工资清零.可是小A偏偏又有赖床的坏毛病.于是为了保住自己的工资 ...

  6. docker-搭建 kafka+zookeeper集群

    拉取容器           docker pull wurstmeister/zookeeper           docker pull wurstmeister/kafka     这里演示使 ...

  7. cookie和session可以参考的文章

    cookie和session可以参考的文章 cookie:http://www.lemfix.com/topics/5session:https://www.cnblogs.com/nickjiang ...

  8. Linux命令之tab 键补全

    tab 键补全 tab 键可以实现命令及路径等补全,提高输入效率,避免出错 命令补全 用户给定的字符串只有一条惟一对应的命令,直接补全, 两次Tab会给出列表 内部命令: 外部命令:bash根据PAT ...

  9. 12天搞定Python,基础语法(上)

    不知你是否见过建楼房的过程,没有的话,找个时间去瞧一瞧,看一看.看过之后,你就会明白.建楼房,只有打好地基之后,才能在砌墙,建的楼层越高,打的地基就越深. 学编程也一样,要想得心应手的应用,得先打好地 ...

  10. Lambda表达式(三)

    public class Test04 { public static void main(String[] args) { /* * Java8中,有一个新的类:Stream类型,它代表一个数据加工 ...