在UNIX系统中,系统为进程相关提供了一系列的控制原语,包括:进程fork,进程exit,进程exec,进程wait等服务。

该篇文章主要与进程exec服务有关,并记录了几个需要注意留意的点。

照例给出其头文件及函数原型如下:

 #include <unistd.h>

 int execl(const char *pathname, const char *arg0, ..., (char *));
int execv(const char *pathname, char *const argv[]);
int execle(const char *pathname, const char *arg0, ..., (char *), char *const envp[]);
int execve(const char *pathname, char *const argv[], char *const envp[]);
int execlp(const char *filename, const char *arg0, ..., (char *));
int execvp(const char *filename, char *const argv[]);
int fexecve(int fd, char *const argv[], char *const envp[]); // Linux
int execvpe(const char *file, char *const argv[], char *const envp[]);

上面总计 7+1=8 个函数,前面7个exec函数有些UNIX实现可能都会实现,也有可能只实现其中的几个,对于最后一个是GNU Linux系统的特有实现。

记忆方式为:exec开头 + l(list列表)/v(vector向量) + e(环境变量env传递)/p(环境变量path遍历)

上面的函数中都不应该有返回值,因为一旦exec( )函数执行成功,那么进程的内容,包括代码和数据会被全部替换掉,旧的进程的代码执行流程就不再存在,因此不该有返回值,但是exec( )函数们可能会执行失败,因此该exec( )函数是有返回值的,其返回值为固定的-1。因此,通常可以在exec函数后面进行错误的输出和程序终止,如下:

execl("/bin/your_program",argv_list);
cerr << "errno occurred, error number: " << errno << "\n";

如果exec返回,那么一定发生了错误,紧随其后的代码报告错误,并打印具体错误number至标准错误。

上面7个exec( )函数中前四个加载新程序的方式是通过绝对路径path来指定,这四个函数之间的区别是形参的传递形式,是以数组形式,还是以一个一个参数的形式传递。还有区别是否传递环境变量和形参是否以空指针结尾。

第五和第六个exec( )函数是通过环境变量 $PATH+file文件名来指定,而第七个则是通过已经打开的文件描述符来指定,通过文件描述符来指定可以避免欲加载的程序二进制文件被替换,从而阻止安全问题。

第一个注意点:前面给出的7个exec( )函数都是系统调用吗?

虽然各个UNIX实现可能提供了好几个exec( )函数,但是只有其中的execve( )函数是系统API,其他的exec( )函数都是在exece( )基础上进行包装的。

第二个注意点:exec( )函数给后续加载的程序传递参数时是否需要指定传入的argv的数组长度?

回忆UNIX系统中那些系统调用API,在很多形参中涉及指针的函数时,我们通常都要指定指针所指缓冲区字节数或者数组的长度,比如read函数要指定字节数,比如poll函数中要指定数组的元素个数。对于这个问题,回答肯定是不需要,因为我们无法指定数组长度,API接口形参列表中不接受数组长度。既然API接口不接受数组长度,那么exec( )函数怎么知道数组的长度呢?方法是靠参数格式约定,也即:传递给exec( )函数列表形式的形参最后一个参数是(char *)NULL,数组形式的形参argv[]最后一个元素是(char *)NULL。这样当exec( )函数遇到了(char *)NULL则表示数组已经遍历到结尾了。

如果不遵守上述行为,不同的实现可能会有不同的行为,比如CentOS会报错,而Mac OS X则会得到意外的形参,代码如下:

 char *const argv[] = {(char *) "ping", (char *) "-c", (char *) "", (char *) "www.baidu.com"};
char *const argv2[] = {(char *) "ping", (char *) "-c", (char *) "", (char *) "www.baidu.com"};
execv("/Users/mac/Develop/cpp/test", argv);

假设在调用execv( )之前先构造execv( )的形参argv,之后又构造了一个无用的argv2,然后调用execv( )函数,而execv( )函数中加载的test个人程序会一一打印传递给它的参数,在Mac OS X上会得到如下的结果:

从结果可以看到,由于第一个argv没有以null空指针结尾,因此exec会一直读argv,甚至把argv2的内容也读取到了,而同样的代码在CentOS上编译后运行,却能得到正常结果。虽然对于个人写的遍历打印参数的程序能正常工作,但对于ping这样的工具则会调用失败。因此这些行为是不确定的,不能对它们做出某种假设,要遵守null指针结尾的规则。

第三个注意点:exec( )函数中第一个形参参数名的意义是什么?

回忆main( )函数的参数,其函数原型为

int main(int argc, char const *argv[]);

其中的argv数组保存了传递给main函数的命令行参数,argv总是以这样的形式构成:

argv[0](main程序自身名) + argv[...](传递给main程序的形参) + argv[argv](null指针)

虽然argv最后以空指针结尾,但是该空指针并不计入argv大小中。

对于exec( )系列函数,当给其传递参数时,第一个参数按照惯例,总是传入该程序不带路径的纯名字,当然也可以为空,但不能省略。对于Mac OS X系统来说,会用该名字作为系统进程中的活跃进程名,而CentOS则不会。其意义其实就是一个约定。

关于UNIX的exec函数的更多相关文章

  1. UNIX高级环境编程(10)进程控制(Process Control)- 竞态条件,exec函数,解释器文件和system函数

    本篇主要介绍一下几个内容: 竞态条件(race condition) exec系函数 解释器文件    1 竞态条件(Race Condition) 竞态条件:当多个进程共同操作一个数据,并且结果依赖 ...

  2. 进程控制之exec函数

    用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序.当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行.因为调用exec并不创 ...

  3. fork和exec函数

    #include<unistd.h> pid_t fork(void); 返回:在子进程中为0,在父进程中为子进程IO,若出错则为- fork最困难之处在于调用它一次,它却返回两次.它在调 ...

  4. Linux下多进程编程之exec函数语法及使用实例

    exec函数族 1)exec函数族说明 fork()函数用于创建一个子进程,该子进程几乎复制了父进程的全部内容,但是,这个新创建的进程如何执行呢?exec函数族就提供了一个在进程中启动另一个程序执行的 ...

  5. 程序清单 8-8 exec函数实例,a.out是程序8-9产生的可执行程序

    /* ============================================================================ Name : test.c Author ...

  6. JS中exec函数与match函数的区别与联系

    总结: 正则规则的声明,两种方法: exec是RegExp类的匹配方法 match是字符串类的匹配方法 var reg = /aaa/g; var reg = new RegExp("aaa ...

  7. linux exec函数家族

    1.exec家族一共有六个函数,分别是: (1)int execl(const char *path, const char *arg, ......); (2)int execle(const ch ...

  8. 让QMainWindow也表现出QDialog的exec函数的特征

    前几天在做毕业设计项目的时候,使用的PyQt4,想实现这么样一个功能: 场景描述:主窗口a(QMainWindow类型)和主窗口b(QMainWindow),b是通过a窗口中某一个按钮弹出来的. 功能 ...

  9. 一、进程与信号之exec函数system函数

    exec函数: 子进程调用exec函数执行另一个程序,exec函数进程完全由新程序代替,替换原有程序正文,数据,堆,栈段 #include <unistd.h> extern char * ...

随机推荐

  1. 【设计模式】—— 原型模式Prototype

    前言:[模式总览]——————————by xingoo 模式意图 由于有些时候,需要在运行时指定对象时哪个类的实例,此时用工厂模式就有些力不从心了.通过原型模式就可以通过拷贝函数clone一个原有的 ...

  2. DB2 数据库常用操作【持续更新】

    好久没写博客了. 上次还是两个月前. 1. 连接数据库 db2 connect to dbName user userName using password 2. 查看表结构 db2 "de ...

  3. delphi开发学习四:TClientDataSet与TDataSetProvider控件使用实例

    1.TClientDataSet控件 通过TClientDataSet控件可以建立瘦客户端的应用程序,且数据执行效率较高,但它不能和数据库自动连接,程序中必须制定它如何获取数据.一般情况下,TClie ...

  4. 学习Spring Boot:(七)集成Mybatis

    前面都是用的是spring data JPA,现在学习下Mybatis,而且现在Mybatis也像JPA那样支持注解形式了,也非常方便,学习一下. 数据库 mysql 5.7 添加依赖 在pom文件中 ...

  5. BZOJ5305 [Haoi2018]苹果树 【组合数学】

    题目链接 BZOJ5305 题解 妙啊 要求的是所有可能的树形的所有点对距离和 直接考虑点的贡献肯定想不出,这样的所有点对距离问题通常转化为边的贡献 考虑一条边会产生多少贡献 我们枚举\(i\)节点的 ...

  6. 洛谷 P1939 【模板】矩阵加速(数列) 解题报告

    P1939 [模板]矩阵加速(数列) 题目描述 a[1]=a[2]=a[3]=1 a[x]=a[x-3]+a[x-1] (x>3) 求a数列的第n项对1000000007(10^9+7)取余的值 ...

  7. 使用mshta.exe绕过应用程序白名单(多种方法)

      0x00 简介 很长一段时间以来,HTA文件一直被web攻击或在野恶意软件下载程序用作恶意程序的一部分.HTA文件在网络安全领域内广为人知,从红队和蓝队的角度来看,它是绕过应用程序白名单有价值的“ ...

  8. python模块之 paramiko

    paramiko模块提供了ssh及sft进行远程登录服务器执行命令和上传下载文件的功能.这是一个第三方的软件包,使用之前需要安装. 1 基于用户名和密码的 sshclient 方式登录 # 建立一个s ...

  9. [SCOI2016] 背单词 (Trie树)

    $pdf\space solution$      link #include<iostream> #include<algorithm> #include<cstrin ...

  10. c++优先队列(堆)

    1.最小堆.最大堆 priority_queue<int,vector<int>,greater<int> > f; //最小堆(后面的数逐渐greater) pr ...