在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. 【Django】用pycharm初学习使用Django

    开发框架流程 M V C(99%的开发都是这种流程.) 1.URL控制器 2.Views 视图 3.models 库   1.首先创建一个Django   2.创建成功后里面几个模块的功能   用它来 ...

  2. Mxnet Windows配置

    MXNET Windows 编译安装(Python) 本文只记录Mxnet在windows下的编译安装,更多环境配置请移步官方文档:http://mxnet.readthedocs.io/en/lat ...

  3. Window系统 安装TFLearn

    1. 确保成功安装了tensorflow 2. 查看当前tensorflow下的库文件,判断是否已经安装了h5py,scipy:conda list 3. 若没有安装,安装h5py,scipy.我的电 ...

  4. QProcess 进程调用

    1. 调用方的接口: void QProcess::start(const QString &program, const QStringList &arguments, OpenMo ...

  5. Qt——结合qt和python

    经常使用qt的童鞋一定有过这样的经历:百度或Google某个关于Qt的问题的时候,发现有的解答不是用的C++,而是包含很多py.__init__.self之类的词. 如果学过python,你会发现,这 ...

  6. BZOJ3162 独钓寒江雪(哈希+树形dp)

    数独立集显然是可以树形dp的,问题在于本质不同. 假设已经给树确立了一个根并且找到了所有等效(注意是等效而不是同构)子树,那么对转移稍加修改使用隔板法就行了. 关键在于找等效子树.首先将树的重心(若有 ...

  7. SpringBoot项目部署进阶

    一.war包部署 通过“云开发”平台初始化的SpringBoot项目默认采用jar形式打包,这也是我们推荐的方式.但是,因为某些原因,软件需求方特别要求用war形式打包,我们该怎么做? 1.项目尚未开 ...

  8. 【刷题】BZOJ 1061 [Noi2008]志愿者招募

    Description 申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者.经过估算,这个项目需要N 天才能完 ...

  9. mybatis plugin作为一款优秀的mybatis跳转插件

    阅读目录: 1. 简介2. 下载mybatis plugin插件3. 安装mybatis plugin插件4. 启动并验证5.说明1. 简介 mybatis plugin作为一款优秀的mybatis跳 ...

  10. 51nod 1293 球与切换器 | DP

    51nod 1293 球与切换器 | DP 题面 有N行M列的正方形盒子.每个盒子有三种状态0, -1, +1.球从盒子上边或左边进入盒子,从下边或右边离开盒子.规则: 如果盒子的模式是-1,则进入它 ...