在 Windows 平台下,我们能够通过双击运行可运行程序,让这个可运行程序成为一个进程。而在 Linux 平台。我们能够通过 ./ 运行,让一个可运行程序成为一个进程。

可是。假设我们本来就执行着一个程序(进程)。我们怎样在这个进程内部启动一个外部程序,由内核将这个外部程序读入内存。使其执行起来成为一个进程呢?这里我们通过 exec
函数族
实现。

exec
函数族
。顾名思义,就是一簇函数,在 Linux 中,并不存在 exec() 函数,exec 指的是一组函数,一共同拥有 6 个:

  1. #include <unistd.h>
  2. int execl(const char *path, const char *arg, ...);
  3. int execlp(const char *file, const char *arg, ...);
  4. int execle(const char *path, const char *arg, ..., char * const envp[]);
  5. int execv(const char *path, char *const argv[]);
  6. int execvp(const char *file, char *const argv[]);
  7. int execve(const char *path, char *const argv[], char *const envp[]);

当中仅仅有 execve() 是真正意义上的系统调用。其他都是在此基础上经过包装的库函数。

exec 函数族提供了六种在进程中启动还有一个程序的方法。exec 函数族的作用是依据指定的文件名称或文件夹名找到可运行文件,并用它来代替调用进程的内容,换句话说,就是在调用进程内部运行一个可运行文件。

进程调用一种 exec 函数时,该进程全然由新程序替换,而新程序则从其 main 函数開始运行。由于调用
exec 并不创建新进程,所以前后的进程 ID (当然还有父进程号、进程组号、当前工作文件夹……)并未改变。exec 仅仅是用还有一个新程序替换了当前进程的正文、数据、堆和栈段(进程替换)。

exec
函数族
的 6 个函数看起来似乎非常复杂。但实际上不管是作用还是使用方法都非常相似,仅仅有非常微小的区别。

l(list):參数地址列表。以空指针结尾。

v(vector):存有各參数地址的指针数组的地址。

p(path):按 PATH 环境变量指定的文件夹搜索可运行文件。

e(environment):存有环境变量字符串地址的指针数组的地址。

exec 函数族装入并执行可执行程序 path/file,并将參数 arg0( arg1, arg2, argv[], envp[] ) 传递给此程序。

exec 函数族与一般的函数不同,exec 函数族中的函数运行成功后不会返回。并且,exec 函数族以下的代码运行不到

仅仅有调用失败了,它们才会返回 -1,失败后从原程序的调用点接着往下运行。


excel代码:

#include <stdio.h>
#include <unistd.h> int main(int argc, char *argv[])
{
printf("before exec\n\n"); /* /bin/ls:外部程序,这里是/bin文件夹的 ls 可运行程序,必须带上路径(相对或绝对)
ls:没有意义,假设需要给这个外部程序传參,这里必需要写上字符串,至于字符串内容随意
-a,-l,-h:给外部程序 ls 传的參数
NULL:这个必须写上,代表给外部程序 ls 传參结束
*/
execl("/bin/ls", "ls", "-a", "-l", "-h", NULL); // 假设 execl() 运行成功。以下运行不到,由于当前进程已经被运行的 ls 替换了
perror("execl");
printf("after exec\n\n"); return 0;
}

执行结果:


execv()演示样例代码:

execv() 和 execl() 的使用方法基本是一样的,无非将列表传參。改为用指针数组。

#include <stdio.h>
#include <unistd.h> int main(int argc, char *argv[])
{
// execv() 和 execl() 的使用方法基本是一样的,无非将列表传參,改为用指针数组
// execl("/bin/ls", "ls", "-a", "-l", "-h", NULL); /* 指针数组
ls:没有意义,假设需要给这个外部程序传參,这里必需要写上字符串,至于字符串内容随意
-a。-l,-h:给外部程序 ls 传的參数
NULL:这个必须写上,代表给外部程序 ls 传參结束
*/
char *arg[]={"ls", "-a", "-l", "-h", NULL}; // /bin/ls:外部程序。这里是/bin文件夹的 ls 可运行程序。必须带上路径(相对或绝对)
// arg: 上面定义的指针数组地址
execv("/bin/ls", arg); perror("execv"); return 0;
}

execlp() 或 execvp() 演示样例代码:

execlp() 和 execl() 的差别在于,execlp() 指定的可运行程序能够不带路径名,假设不带路径名的话,会在环境变量 PATH指定的文件夹里寻找这个可运行程序。而 execl() 指定的可运行程序,必须带上路径名。

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main(int argc, char *argv[])
  4. {
  5. // 第一个參数 "ls",没有带路径名。在环境变量 PATH 里寻找这个可运行程序
  6. // 其他參数使用方法和 execl() 一样
  7. execlp("ls", "ls", "-a", "-l", "-h", NULL);
  8. /*
  9. char *arg[]={"ls", "-a", "-l", "-h", NULL};
  10. execvp("ls", arg);
  11. */
  12. perror("execlp");
  13. return 0;
  14. }

execle() 或 execve() 演示样例代码:

execle() 和 execve() 改变的是 exec 启动的程序的环境变量(仅仅会改变进程的环境变量。不会影响系统的环境变量),其它四个函数启动的程序则使用默认系统环境变量。

execle()演示样例代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h> // getenv() int main(int argc, char *argv[])
{
// getenv() 获取指定环境变量的值
printf("before exec:USER=%s, HOME=%s\n", getenv("USER"), getenv("HOME")); // 指针数据
char *env[]={"USER=EDU", "HOME=/tmp", NULL}; /* ./edu:外部程序,当前路径的 edu 程序。通过 gcc edu.c -o edu 编译
edu:这里没有意义
NULL:给 edu 程序传參结束
env:改变 edu 程序的环境变量,正确来说,让 edu 程序仅仅保留 env 的环境变量
*/
execle("./edu", "edu", NULL, env); /*
char *arg[]={"edu", NULL};
execve("./edu", arg, env);
*/ perror("execle"); return 0;
}

外部程序,edu.c 演示样例代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> int main(int argc, char *argv[])
{
printf("\nin the edu fun, after exec: \n");
printf("USER=%s\n", getenv("USER"));
printf("HOME=%s\n", getenv("HOME")); return 0;
}

执行结果:






Linux系统编程——进程替换:exec 函数族的更多相关文章

  1. Linux系统编程@进程通信(一)

    进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统 ...

  2. linux系统编程-进程

    进程 现实生活中 在很多的场景中的事情都是同时进行的,比如开车的时候 手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的: 如下是一段视频,迈克杰克逊的一段视频: http://v.youku.com ...

  3. Linux系统编程@进程管理(一)

    课程目标: 构建一个基于主机系统的多客户即时通信/聊天室项目 涉及的理论知识 进程控制:僵尸进程/孤儿进程.进程控制.守护进程... 进程间通信:管道.命名管道.信号... 多线程编程: 锁.信号量. ...

  4. Linux多任务编程之三:exec函数族及其基础实验(转)

    来源:CSDN  作者:王文松  转自:Linux公社 exec函数族 函数族说明 fork() 函数用于创建一个新的子进程,该子进程几乎复制了父进程的全部内容,但是,这个新创建的子进程如何执行呢?e ...

  5. Linux系统编程@进程管理(二)

    1.创建守护进程(Deamon) 守护进程的概念与作用 后台服务程序 – 系统服务,进程名字往往以’d’结尾,生存周期比较长(系统装入时启动,关闭时候终止.系统装入两种启动方式:1从启动脚本.etc/ ...

  6. [linux] C语言Linux系统编程进程基本概念

    1.如果说文件是unix系统最重要的抽象概念,那么进程仅次于文件.进程是执行中的目标代码:活动的.生存的.运行的程序. 除了目标代码进程还包含数据.资源.状态以及虚拟化的计算机. 2.进程体系: 每一 ...

  7. Linux系统编程—进程间同步

    我们知道,线程间同步有多种方式,比如:信号量.互斥量.读写锁,等等.那进程间如何实现同步呢?本文介绍两种方式:互斥量和文件锁. 互斥量mutex 我们已经知道了互斥量可以用于在线程间同步,但实际上,互 ...

  8. Linux系统编程-----进程fork()

    在开始之前,我们先来了解一些基本的概念: 1. 程序, 没有在运行的可执行文件 进程, 运行中的程序 2. 进程调度的方法: 按时间片轮转 先来先服务 短时间优先 按优先级别 3. 进程的状态: 就绪 ...

  9. linux系统编程之进程(一)

    今天起,开始学习linux系统编程中的另一个新的知识点----进程,在学习进程之前,有很多关于进程的概念需要了解,但是,概念是很枯燥的,也是让人很容易迷糊的,所以,先抛开这些抽象的概念,以实际编码来熟 ...

随机推荐

  1. 二重积分的计算 —— 交换积分顺序(exchange the order of integration)

    交换积分顺序的诀窍在数形结合: 1. 几句顺口溜 后积先定限,限内穿条线,先交下限写,后交上限见 先积 x,画横线(平行于 x 轴),右减左: 先积 y,画竖线(平行于 y 轴),上减下: 2. 简单 ...

  2. [Javascript] 40个轻量级JavaScript脚本库

    诸如jQuery, MooTools, Prototype, Dojo和YUI等JavaScript脚本库,大家都已经很熟悉.但这些脚本库有利也有弊--比如说JavaScript文件过大的问题.有时你 ...

  3. strlen和mb_strlen

    在PHP中,strlen与mb_strlen是求字符串长度的函数,但是对于一些初学者来说,如果不看手册,也许不太清楚其中的区别.下面通过例子,讲解这两者之间的区别. 先看例子: <?php // ...

  4. nginx配置访问密码,让用户输入用户名密码才能访问

    如果我们在 nginx 下搭建了一些站点,但是由于站点内容或者流量的关系,我们并不想让所有人都能正常访问,那么我们可以设置访问认证.只有让用户输入正确的用户名和密码才能正常访问.效果如下: 在 ngi ...

  5. Python笔记(十一)——数据抓取例子

    上班时候想看股票行情怎么办?试试这个小例子,5分钟拉去一次股票价格,预警: #coding=utf-8 import re import urllib2 import time import thre ...

  6. uirecorder录制脚本

    安装步骤不再描述. 将手机通过数据线连接到mac 然后输入启动macaca的命令: macaca server --port 4444 --verbose & 在输入 命令:uirecorde ...

  7. TopN问题(分别使用Hadoop和Spark实现)

    简介 TopN算法是一个经典的算法,由于每个map都只是实现了本地的TopN算法,而假设map有M个,在归约的阶段只有M x N个,这个结果是可以接受的并不会造成性能瓶颈. 这个TopN算法在map阶 ...

  8. Entity Framework 的懒加载、预先加载、显示加载

    1.新建两个实体,一个班级有多个学生 public class Student { public int StudentId { get; set; } public string StudentNa ...

  9. jquery.reveal弹出框

     一款js弹出框,嵌入其它页面: 引用: <script src="../../js/jquery.reveal.js" type="text/javascript ...

  10. Python 之 基础知识(四)

    一.公共方法(列表.元组.字典以及字符串) 1.内置函数 cmp函数取消可以用比较运算符来代替,但是字典是无序的,故而不可以用比较运算符比较. 2.切片(列表.元组.字符串适用) 3.运算符 列表中直 ...