`

exec 族函数介绍

阅读更多
    用 fork 函数创建子进程后,子进程往往要调用一种 exec 函数以执行另一个程序,exec 只是用磁盘上的一个新程序替换了当前进程的正文段、数据段、堆段和栈段,所以前后的进程 ID 并未改变。
    有 7 种不同的 exec 函数可供使用。
#include <unistd.h>
int execl(const char *pathname, const char *arg0, ... /* (char *)0 */);
int execv(const char *pathname, char *const argv[]);
int execle(const char *pathname, const char *arg0, ... /* (char *)0, char *const envp[] */ );
int execve(const char *pathname, char *const argv[], char *const envp[]);
int execlp(const char *filename, const char *arg0, ... /* (char *)0 */ );
int execvp(const char *filename, char *const argv[]);
int fexecve(int fd, char *const argv[], char *const envp[]);
                           /* 7 个函数返回值:若成功,不返回;否则,返回 -1 */

    其中,字母 p 表示取 filename 作为参数,并用 PATH 环境变量寻找可执行文件;字母 l 表示取一个参数表;v 表示取一个 argv[] 矢量,与字母 l 互斥;字母 e 表示取 envp[]
数组,而不使用当前环境,即可以传递一个指向环境字符串指针数组的指针,否则就使用调用进程中的 environ 变量为新程序复制现有的环境。
    当指定 filename 作为参数时,如果 filename 中包含“/”,则将其视为路径名,否则就在 PATH 环境变量指定的各目录中搜寻可执行文件。如果找到了一个可执行文件,但是该文件不是由连接编辑器产生的机器可执行文件,则就认为该文件是一个 shell 脚本,于是试着调用 /bin/sh,并以该 filename 作为 shell 的输入。
    fexecve 函数避免了寻找正确的可执行文件,而是依赖调用进程来完成这项工作。调用进程可以使用文件描述符验证所需要的文件并且无竞争地执行该文件。否则,拥有特权的恶意用户就可以在找到文件位置并且验证之后,但在调用进程执行之前替换可执行文件或可执行文件的部分路径。
    execl、execlp 和 execle 要求将新程序的每个命令行参数都说明为一个单独的参数,这种参数表以空指针结尾,execv、execvp、execve 和 fexecve 函数则以一个指向各命令行参数的指针数组的地址作为参数。
    在使用 ISO C 原型之前,对 execl、execle 和 execlp 三个函数表示命令行参数的一般方法是:
        char *arg0, char *arg1, ..., char *argn, (char *)0
    这种语法显示地说明了最后一个命令行参数之后跟了一个空指针(如果用 0 来表示一个空指针,则必须将它强制转换为一个指针。如果一个整型数的长度与 char * 的长度不同,那么 exec 的实际参数将出错)。
    通常,一个进程允许将其环境传播给其子进程,但有时进程也想为子进程指定某一个确定的环境。例如,在初始化一个新登录的 shell 时,login 程序通常创建一个只定义少数几个变量的特殊环境,而在登录时,可以通过 shell 启动文件,将其他变量添加到环境中。
    在使用 ISO C 原型之前,execle 的参数是:
        char *pathname, char *arg0, ..., char *argn, (char *)0, char *envp[]
    可见最后一个参数是指向环境字符串的各字符指针构成的数组的指针。而在 ISO C 中,所有命令行参数、空指针和 envp 指针都使用省略号表示。
    每个系统对参数表和环境表的总长度都有一个限制,该限制是由 ARG_MAX 给出的。在 POSIX.1 系统中,此值至少是 4096 字节。为摆脱对参数表长度的限制,可以使用 xargs(1) 命令,将长参数表断开成几部分。比如,为了寻找所用系统手册页中的 getrlimit,可用:
        find /usr/share/man -type f -print | xargs grep getrlimit
    如果系统手册页是压缩过的,则可使用:
        find /usr/share/man -type f -print | xargs bzgrep getrlimit
    这里使用 find 的“-type f”选项的原因是,grep 命令不能在目录中进行模式搜索,同时也可避免不必要的出错信息。
    执行 exec 后,新程序从调用进程继承了下列属性:
    * 进程 ID 和父进程 ID
    * 实际用户 ID 和实际组 ID
    * 附属组 ID
    * 进程组 ID
    * 会话 ID
    * 控制终端
    * 闹钟尚余留的时间
    * 当前工作目录
    * 根目录
    * 文件模式创建屏蔽字
    * 文件锁
    * 进程信号屏蔽
    * 未处理信号
    * 资源限制
    * nice 值
    * tms_utime、tms_stime、tms_cutime 以及 tms_cstime 值
    对打开文件的处理与每个描述符的执行时关闭(close-on-exec)FD_CLOEXEC 标志值有关:若设置了此标志,则在执行 exec 时关闭该描述符;否则该描述符仍打开。除非特地使用 fcntl 设置了此标志,否则系统的默认操作是在 exec 后仍保持这种描述符打开。
    POSIX.1 明确要求在 exec 时关闭打开目录流,这通常是由 opendir 函数实现的,它调用 fcntl 函数为对应于打开目录流的描述符设置执行时关闭。
    在 exec 前后实际用户 ID 和实际组 ID 保持不变,而有效 ID 是否改变则取决于所执行程序文件的设置用户 ID 位和设置组 ID 位是否设置:如果已设置,则有效用户 ID 变成程序文件所有者的 ID;否则有效用户 ID 不变。对组 ID 的处理方式类似。
    在很多 UNIX 系统中,只有 execve 是系统调用,另外 6 个是库函数,它们最终都要调用该系统调用。它们之间的关系如图所示。

    在这种安排中,库函数 execlp 和 execvp 使用 PATH 环境变量,查找第一个包含名为 filename 的可执行文件的路径名前缀,fexecve 使用 /proc 把文件描述符转换成路径名,execve 用该路径名去执行程序。这描述了在 FressBSD 8.0 和 Linux 3.2.0 中是如何实现 fexecve 的,其他系统采用的方法可能不同。例如,没有 /proc 和 /dev/fd 的系统可能把 fexecve 实现为系统调用,把文件描述符参数转换成 i 节点指针,把 execve 实现为系统调用,把路径名参数转换成 i 节点指针,然后把 execve 和 fexecve 中剩余的 exec 公共代码放到单独的函数中,调用该函数时传入执行文件的 i 节点指针。
    下面这个程序演示了 exec 函数。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

char *env_init[] = {"USER=unknown", "PATH=/tmp", NULL};

int main(void){
	pid_t	pid;
	if((pid=fork()) < 0){
		printf("fork 1 error\n");
		exit(2);
	}
	if(pid == 0){	// specify pathname, specify environment
		if(execle("./echoall.out", "echoall.out","myarg1","MY ARG2", (char *)0, env_init) < 0){
			printf("execle error\n");
			exit(2);
		}
	}
	if(waitpid(pid, NULL, 0) < 0){
		printf("waitpid 1 error\n");
		exit(2);
	}

	printf("==================================\n");

	if((pid=fork()) < 0){
		printf("fork 2 error\n");
		exit(2);
	}
	if(pid == 0){
		if(execlp("./echoall.out", "echoall.out", "only 1 arg", (char *)0) < 0){
			printf("execlp error\n");
			exit(2);
		}
	}
	if(waitpid(pid, NULL, 0) < 0){
		printf("waitpid 2 error\n");
		exit(2);
	}
	exit(0);
}

    在该程序中先调用 execle,它要求一个路径名和一个特定的环境。下一个调用的 execlp使用一个文件名,并将调用者的环境传送给新程序。另外,这里将新程序中的 argv[0] 参数设置为路径名的文件名分量,某些 shell 将此参数设置为完全的路径名,也可将其设置为任何字符串。当 login 命令执行 shell 时就是这样做的。在执行 shell 前,login 在 argv[0] 前加一个“/”作为前缀,这向 shell 指明它是作为登录 shell 被调用的。登录 shell 将执行启动配置文件命令,而非登录 shell 则不会执行这些命令。
    上面这个程序中执行的 echoall 程序会回显所有命令行参数及环境表,其代码如下。
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
	int		i;
	char	**ptr;
	extern char	**environ;

	for(i=0; i<argc; i++)	// echo all command-line args
		printf("argv[%d]: %s\n", i, argv[i]);
	
	for(ptr=environ; *ptr!=0; ptr++)	// echo all env strings
		printf("%s\n", *ptr);
	
	exit(0);
}

    exec 演示程序的执行结果如下。
$ ./execDemo.out 
argv[0]: echoall.out
argv[1]: myarg1
argv[2]: MY ARG2
USER=unknown
PATH=/tmp
==================================
argv[0]: echoall.out
argv[1]: only 1 arg
IMSETTINGS_INTEGRATE_DESKTOP=yes
TERM=xterm
SHELL=/bin/bash
HISTSIZE=1000
XDG_SESSION_COOKIE=c0ff14d609262918aa50a54a00000014-1493237231.38046-1794705392
WINDOWID=82012945
...             # 省略了多行输出

  • 大小: 7.2 KB
分享到:
评论

相关推荐

    Linux中使用exec函数族详解及示例代码 | 嵌入式Linux应用开发篇 – 03

    1.exec函数族 exec 为 execute(执行),exec 函数族用来替换调用进程所执行的程序,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行,替换前后进程的 PID 不会改变。 exec函数族中包括6个...

    Linux进程与线程实验,exec族函数、管道、消息队列、信号机制、共享内存等

    Linux系统提供了丰富的API来支持进程间的通信(IPC,Inter-Process Communication),包括exec族函数、管道、消息队列、信号机制以及共享内存等。本实验旨在让学生深入理解和掌握这些通信机制。 **1. exec族函数** ...

    进程替换:exec 函数族示例代码

    下面是对`exec`函数族主要成员的简要介绍: 1. `execl`:接受一串参数(字符串)作为参数列表,最后一个参数必须为NULL。例如:`execl("/bin/sh", "sh", "-c", "echo Hello", (char *)NULL);` 2. `execlp`:类似于...

    linux进程之exec函数族.zip

    在Linux操作系统中,进程执行是通过系统调用来完成的,其中`exec`函数族就是一组用于替换当前进程映像的函数。这些函数允许程序在不退出的情况下加载并执行新的程序。在本压缩包中,包含的`execl.c`和`helloexec.c`...

    exec函数族的使用

    作者:王姗姗,华清远见嵌入式学院... 下面来看下exec函数族:  #include  int execl(const char *path, const char *arg, ...);  int execlp(const char *file, const char *arg, ...);  int execle(cons

    linux进程和线程编程(exec)

    Linux进程和线程编程(exec) 在Linux操作系统中,进程和线程编程是非常重要的概念,exec函数族...exec函数族是Linux中非常重要的进程创建函数,了解exec函数族的使用和特点对我們编写高效、可靠的Linux程序非常重要。

    基于exec函数族分析Linux初始化进程运行环境的过程

    ### 基于exec函数族分析Linux初始化进程运行环境的过程 #### 一、Linux内存管理简介 Linux操作系统采用了一种高效的虚拟内存管理机制,其中主要包括分页管理技术。这一技术的核心在于将物理内存划分为固定大小的块...

    EDA/PLD中的exec函数族的使用

    在计算机科学领域,特别是在操作系统和进程管理中,`exec`函数族是一组重要的系统调用,用于在当前进程中加载并执行新的程序。这些函数在`&lt;unistd.h&gt;`头文件中定义,广泛应用于C语言编程中。`EDA/PLD`(电子设计自动...

    C函数库介绍

    本篇将详细介绍几个重要的C函数库及其相关函数,特别是与进程管理和文件操作相关的部分。 首先,我们来看stdlib.h库,它包含了大量基本的内存管理和程序控制函数。例如,`abort()`函数用于异常终止程序,它会发送一...

    system函数.docx

    在本实验中,我们主要关注如何使用`system`函数来完成特定任务,并通过比较`system`函数与其他`exec`族函数的区别来深入理解它们的工作原理。 首先,让我们详细讨论`system`函数。它的原型如下: ```c int system...

    \Linux解释器原理详细介绍

    首先,了解`exec`族函数是关键。这个家族包括`execl`、`execlp`、`execle`、`execv`和`execvp`,它们的功能都是替换当前进程的映像,执行一个新的程序。这些函数的差异在于参数的传递方式,但核心目标都是启动新的...

    Linux下生成进程换出进程杀死进程例子

    Linux下生成进程换出进程杀死进程例子 函数fork,exec族函数,exit函数例子

    Linux下Fork与Exec使用

    - **2.2.2 exec() 函数族**: - **作用**: 替换当前进程的映像为新的进程映像,即用另一个程序替换当前运行的程序。 - **常见函数**: - `execl()`: 用于替换当前进程的映像。 - `execle()`: 类似 `execl()`,但...

    系统编程中常用函数总结

    - **`exec`函数族** `exec`系列函数用于替换当前进程映像,执行新的程序。主要包括`execl`、`execlp`、`execle`、`execv`等。 - **`int execl(const char *path, const char *arg, ...)`:** 替换当前进程映像...

    Linux环境下的进程

    本文将深入探讨Linux环境下的进程,包括进程的创建、退出机制,以及与之相关的exec族函数、system函数和popen函数。 首先,我们来理解**进程的创建**。在Linux中,创建新进程通常使用`fork()`函数,它会复制当前...

    《linux软件工程师(C语言)实用教程》第6章.pptx

    本章还介绍了与进程相关的函数,特别是`exec`函数族。`exec`函数族包括`execve`、`execl`、`execlp`、`execle`、`execv`和`execvp`,它们用于在当前进程中替换并执行新的程序。例如,通过`execv()`可以创建一个新...

    【Linux】进程二 (PCB & fork/vfork & wait/waitpid & exit/_exit & exec函数族 & 环境变量)

    一、描述进程——PCB ·进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合 ·我们称为PCB,Linux操作系统下的PCB是:task struct 2、task_struct——PCB的一种 ·在Linux中描述进程的结构体...

    android apk获取root权限方法总结

    Android APK 获取 Root 权限方法总结 Android APK 获取 Root 权限是一个...但是,使用 exec 函数族和 Runtime.getRuntime().exec() 函数可以使 APK 获取 Root 权限,访问系统中需要 Root 权限才能执行的文件或程序。

    实验六 进程控制1

    `exec`函数族允许程序替换当前进程的映像来执行新的程序,如`execv()`函数。当`execv()`成功执行时,当前进程被新程序替换,且新进程的PID保持不变。若执行失败,函数返回-1,需要进行错误处理。在Linux中,`execv`...

Global site tag (gtag.js) - Google Analytics