在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. python web调用docker-py

    在 /etc/init.d/docker的start()函数末尾加入:chmod 777 /var/run/docker.sock 否则web程序会没有权限去操作  

  2. 第218天:Angular---模块和控制器

    1.使用NG实现双边数据绑定 所有需要ng管理的代码必须被包裹在一个有ng-app指令的元素中ng-app是ng的入口,表示当前元素的所有指令都会被angular管理(对每一个指令进行分析和操作) & ...

  3. spring1 注册value与ref的区别

  4. 过滤器将获取到的内容注入到servlet的request中

    过滤器将获取到的内容注入到servlet的request中

  5. C#:system.collections.generic(泛型)

    1. array是一个固定长度的,如果要动态的存储的话就不行了,虽然 System.Collections.ArrayList(),是一个动态的存储的容器,但是没有对存储中的数据进行一个约束,所以非泛 ...

  6. python使用selenium、PhantomJS获得网站cookie信息#windows

    首先python安装selenium,命令行中输入 pip install selenium 在执行代码如下代码时出现错误 driver=webdriver.PhantomJS() 错误如下 sele ...

  7. 【bzoj4031】[HEOI2015]小Z的房间 解题报告

    [bzoj4031][HEOI2015]小Z的房间 Description 你突然有了一个大房子,房子里面有一些房间.事实上,你的房子可以看做是一个包含\(n*m\)个格子的格状矩形,每个格子是一个房 ...

  8. 洛谷 P4706 取石子 解题报告

    P4706 取石子 题目描述 现在 Yopilla 和 yww 要开始玩游戏! 他们在一条直线上标记了 \(n\) 个点,从左往右依次标号为 \(1, 2, ..., n\) .然后在每个点上放置一些 ...

  9. 【poj2406】 Power Strings

    http://poj.org/problem?id=2406 (题目链接) 题意 给定一个字符串 L,已知这个字符串是由某个字符串 S 重复 R 次而得到的, 求 R 的最大值. Solution 后 ...

  10. [网络流]小M的作物

    小\(M\)的作物(最小割) 做的第一道网络流,因为一个智障错误调了好久嘤嘤嘤 题目描述 小\(M\)在\(MC\)里开辟了两块巨大的耕地\(A\)和\(B\)(你可以认为容量是无穷),现在,小\(P ...