关于UNIX的exec函数
在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函数的更多相关文章
- UNIX高级环境编程(10)进程控制(Process Control)- 竞态条件,exec函数,解释器文件和system函数
本篇主要介绍一下几个内容: 竞态条件(race condition) exec系函数 解释器文件 1 竞态条件(Race Condition) 竞态条件:当多个进程共同操作一个数据,并且结果依赖 ...
- 进程控制之exec函数
用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序.当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行.因为调用exec并不创 ...
- fork和exec函数
#include<unistd.h> pid_t fork(void); 返回:在子进程中为0,在父进程中为子进程IO,若出错则为- fork最困难之处在于调用它一次,它却返回两次.它在调 ...
- Linux下多进程编程之exec函数语法及使用实例
exec函数族 1)exec函数族说明 fork()函数用于创建一个子进程,该子进程几乎复制了父进程的全部内容,但是,这个新创建的进程如何执行呢?exec函数族就提供了一个在进程中启动另一个程序执行的 ...
- 程序清单 8-8 exec函数实例,a.out是程序8-9产生的可执行程序
/* ============================================================================ Name : test.c Author ...
- JS中exec函数与match函数的区别与联系
总结: 正则规则的声明,两种方法: exec是RegExp类的匹配方法 match是字符串类的匹配方法 var reg = /aaa/g; var reg = new RegExp("aaa ...
- linux exec函数家族
1.exec家族一共有六个函数,分别是: (1)int execl(const char *path, const char *arg, ......); (2)int execle(const ch ...
- 让QMainWindow也表现出QDialog的exec函数的特征
前几天在做毕业设计项目的时候,使用的PyQt4,想实现这么样一个功能: 场景描述:主窗口a(QMainWindow类型)和主窗口b(QMainWindow),b是通过a窗口中某一个按钮弹出来的. 功能 ...
- 一、进程与信号之exec函数system函数
exec函数: 子进程调用exec函数执行另一个程序,exec函数进程完全由新程序代替,替换原有程序正文,数据,堆,栈段 #include <unistd.h> extern char * ...
随机推荐
- .net MVC4 来一个简单的分页代码
action VodeEntities db = new NewVode.Models.VodeEntities(); ; ) { var sql = "select * ,row_numb ...
- QQ分享-定制分享卡片
一般H5页面在进行分享的时候,都会生成一个分享卡片,但是这些卡片的生成是很多时候是我们是想要生成的卡片, 对于QQ,我们只需要在html页面里加如3个标签即可,如下: <meta itempro ...
- 016 Java中的动态代理
作者:nnngu GitHub:https://github.com/nnngu 博客园:http://www.cnblogs.com/nnngu 简书:https://www.jianshu.com ...
- python爬虫headers设置后无效解决方案
此次遇到的是一个函数使用不熟练造成的问题,但有了分析工具后可以很快定位到问题(此处推荐一个非常棒的抓包工具fiddler) 正文如下: 在爬取某个app数据时(app上的数据都是由http请求的),用 ...
- PHP-从零开始使用Solr搜索引擎服务(下)
前言: 原文地址: http://www.cnblogs.com/JimmyBright/p/7156085.html 前面在配置完成Solr服务之后,在浏览器上可以打开Solr的管理界面,这个界面几 ...
- 【bzoj3926】 Zjoi2015—诸神眷顾的幻想乡
http://www.lydsy.com/JudgeOnline/problem.php?id=3926 (题目链接) 题意 给出一棵树,每个节点有一个编号,范围在${[0,9]}$.一个序列是指树上 ...
- Linux内核设计第七周学习总结 Linux内核如何装载和启动一个可执行程序
陈巧然原创作品 转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-100002900 实验目的 使用gdb跟踪s ...
- 解题:USACO18FEB Taming the Herd
题面 从零开始的DP学习系列之贰(我的DP真的就这么烂TAT) 设DP状态的另一个技巧,考虑题目中有关答案的各种信息 然后这种和结尾有关系的$dp$可以考虑向前找结尾来转移 设$dp[i][j]$表示 ...
- 梯度提升树GBDT算法
转自https://zhuanlan.zhihu.com/p/29802325 本文对Boosting家族中一个重要的算法梯度提升树(Gradient Boosting Decison Tree, 简 ...
- 野指针(Wild pointer)和悬垂指针(dangling pointer)
详细参考如下: Dangling pointer(悬垂指针.迷途指针)和 Wild pointer(野指针) 迷途指针经常出现在混杂使用malloc() 和 free() 库调用: 当指针指向的内存释 ...