(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. Linux中断驱动程序

    1.中断概念 中断时一种电信号,由硬件设备产生,然后再由中断控制器向处理器发送相应的信号.处理器一经检测到该信号,便中断自己当前正在处理的工作,转而去处理中断.此后,处理器会通知操作系统已经产生中断. ...

  2. Informatic 内些坑

    1. 工作流调用工作流(可实现无规则时间点自由调度) pmcmd startworkflow -sv 集成服务名称 -d 配置域名称 -u  Administrator -p Administrato ...

  3. MeteoInfoLab脚本示例:读取文本文件

    此例中的降水文本文件下载自"中国气象科学数据共享服务网"(http://cdc.nmic.cn/sksj.do?method=ssrjscp),其实是ESRI的文本格点数据格式.对 ...

  4. day28 Pyhton MRO和C3算法

    1.python多继承.一个类可以拥有多个父类 class ShenXian: # 神仙 def fei(self): print("神仙都会飞") class Monkey: # ...

  5. 解了这14道C语言谜题后,所有人都失声了!我来带你深入了解C!

    本文展示了14个C语言的迷题以及答案,代码应该是足够清楚的,而且有相当的一些例子可能是我们日常工作可能会见得到的.通过这些迷题,希望你能更了解C语言. 如果你不看答案,不知道是否有把握回答各个谜题?让 ...

  6. Spring Boot使用Mybatis实现增删改查

    java.com.wms.model.Admin.java 1 package com.wms.model; 2 3 import java.sql.Timestamp; 4 5 public cla ...

  7. scp远程上传

      scp -P 22 /Users/mac/Downloads/VBoxGuestAdditions_5.2.12.iso root@192.168.1.210:/usr/local/src   s ...

  8. python爬取知乎评论

    点击评论,出现异步加载的请求 import json import requests from lxml import etree from time import sleep url = " ...

  9. UML类图还不懂?来看看这版乡村爱情类图,一把学会!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.码场心得

  10. 【认知服务 Azure Cognitive Service】使用认知服务的密钥无法访问语音服务[ErrorCode=AuthenticationFailure] (2020-08时的遇见的问题,2020-09月已解决)

    问题情形 根据微软认知服务的文档介绍,创建认知服务(Cognitive Service)后,可以调用微软的影像(计算机视觉,人脸),语言(LUIS, 文本分析,文本翻译),语音(文本转语音,语音转文本 ...