Linux execve函数详解

2024-03-13 12:20
文章标签 linux 函数 详解 execve

本文主要是介绍Linux execve函数详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 1 基本介绍
    • 2 execve实例
      • 2.1 自定义argv和envp
      • 2.2 fork后再通过子进程执行execve

1 基本介绍

#include <unistd.h>
int execve(const char *pathname, char *const argv[], char *const envp[]);
  • 描述

    execve()执行由pathname引用的程序。这会导致当前由调用进程运行的程序被一个新程序替换,该新程序具有新初始化的堆栈、堆和(已初始化和未初始化)数据段。

    pathname必须是二进制可执行文件或以形式为#!interpreter [optional-arg]开头的脚本。

    argv是传递给新程序作为其命令行参数的字符串指针数组。按照惯例,这些字符串中第一个(即argv[0])应包含与正在执行文件相关联的文件名。argv数组必须以NULL指针结尾。(因此,在新程序中,argv[argc]将为NULL)。

    envp是传递给新程序环境变量的字符串指针数组。该数组包含了环境变量。每个环境变量都是一个 char* 指针,格式为 “name=value”。envp数组同样必须以NULL指针结尾。

    char *envp[] = {"PATH=/bin","HOME=/home/user","USER=user",NULL // 终止环境变量数组
    };
    

    argvenvp可以从新程序的主函数访问。例如我们编写的C程序,实际上是由操作系统通过execve()系统调用执行(这里是操作系统先执行fork系统调用,创建一个新的子进程,然后在新的子进程中,操作系统执行execve()系统调用),它会传递这些参数给新程序的主函数,即 main 函数。这些参数定义了新程序执行时的环境和命令行参数,在程序启动时由操作系统设置,并在整个程序执行期间保持不变。这使得程序能够根据传递给它的参数和环境变量来执行不同的任务或调整其行为。

  • 返回值

    成功时,execve() 不返回任何值,当 execve 成功替换当前进程的映像并开始执行新的程序时,原来的进程(即调用 execve 的进程)已经不再存在,因此无法返回任何值。

    在出错时返回 -1,并设置适当的 errno

  • 重点

    1. execve实际上就是将当前运行的状态机重置成另一个程序的初始状态
    2. 允许对新状态机设置参数 argv (v) 和环境变量 envp (e)
    3. 在程序启动时,操作系统首先执行 fork 系统调用,创建一个新的子进程。然后,操作系统在子进程中执行 execve 系统调用,以替换子进程的程序映像并开始执行新的程序。原来的父进程继续执行 fork 之后的代码。
    4. 在调用 execve 之前,确保释放所有不再需要的资源,如打开的文件描述符、锁等。
    5. 在调用 execve 之前,确保子进程已经处理了所有待处理的信号,除非你希望信号处理程序在新程序中执行。
    6. 如果 execve 失败,子进程通常应该终止。
    7. 在父进程中,通常会在 fork 之后立即调用 wait 或 waitpid 来等待子进程结束,以确保父进程不会过早退出,从而导致子进程的僵尸进程。

2 execve实例

2.1 自定义argv和envp

#include <unistd.h>
#include <stdio.h>int main() {char * const argv[] = {"/bin/bash", "-c", "env", NULL,};char * const envp[] = {"HELLO=WORLD", NULL,};execve(argv[0], argv, envp);printf("Hello, World!\n");return 0;
}

在这段代码中,我们显式的设置了argvenvp,其中参数 "/bin/bash", "-c", "env", NULL,这里的参数实际上是在告诉 bash 执行一个命令(由 -c 后面的字符串指定),在这个例子中是 env,它打印当前的环境变量。

我们运行代码,得到如下输出:

image-20240313091345219

如果我们不传 -c 参数和随后的命令,即只传入 "/bin/bash", NULL 作为参数,bash 会默认进入交互式模式。在这种模式下,它不会执行任何命令并立即退出,而是会等待用户输入,表现为进入了 shell 环境。

我们发现,打印的当前环境变量除了自定的envp,还有一些其他的输出。这是因为除了我们设定的环境变量外,还有一些系统或者 shell 默认的环境变量会被添加到新进程中,例如 PWD 表示当前工作目录,SHLVL 表示 shell 层级,_ 是上一个执行的命令。这就是为什么我们会看到额外的环境变量出现在输出中。

2.2 fork后再通过子进程执行execve

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>int main() {pid_t pid = fork();if (pid == 0) {// 子进程char * const argv[] = {"/bin/echo", "Hello, World!", NULL};char * const envp[] = {NULL};execve("/bin/echo", argv, envp);} else if (pid > 0) {// 父进程wait(NULL); // 等待子进程结束printf("Child process finished.\n");} else {// fork失败perror("fork");return 1;}return 0;
}

这段代码演示了如何使用 fork() 系统调用创建一个新的子进程,然后在子进程中执行 execve() 系统调用。这是在 Unix-like 系统中常见的操作模式,因为 execve() 系统调用有一些关键的限制:

  • 一次机会:execve() 系统调用只能用于替换当前进程的映像一次。如果一个进程已经调用了 execve(),它就不能再调用 fork() 或再次执行 execve()
  • 无返回值:execve() 成功执行时,原来的进程映像被新程序映像替换,原来的进程不再存在,因此无法返回任何值。如果在 execve() 执行之前有任何返回值,那么这个返回值是在 fork() 调用之后,由父进程获得的。

因此,在实际应用中,我们通常会先使用 fork() 创建一个子进程,然后在子进程中调用 execve() 执行新的程序。父进程在 fork() 之后会继续执行,并通过调用 wait(NULL) 来等待子进程结束。这样,父进程可以知道子进程已经成功执行了 execve(),并且可以继续执行其他任务或退出。

在多线程程序中,如果一个线程执行了 fork() 并尝试在子进程中执行 execve(),那么其他线程将继续执行,不受 fork()execve() 的影响。只有调用 fork() 的线程会进入子进程,而其他线程则继续在父进程中运行。

得到的运行结果:

image-20240313102827423

这篇关于Linux execve函数详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/804861

相关文章

一文详解SpringBoot中控制器的动态注册与卸载

《一文详解SpringBoot中控制器的动态注册与卸载》在项目开发中,通过动态注册和卸载控制器功能,可以根据业务场景和项目需要实现功能的动态增加、删除,提高系统的灵活性和可扩展性,下面我们就来看看Sp... 目录项目结构1. 创建 Spring Boot 启动类2. 创建一个测试控制器3. 创建动态控制器注

C#读写文本文件的多种方式详解

《C#读写文本文件的多种方式详解》这篇文章主要为大家详细介绍了C#中各种常用的文件读写方式,包括文本文件,二进制文件、CSV文件、JSON文件等,有需要的小伙伴可以参考一下... 目录一、文本文件读写1. 使用 File 类的静态方法2. 使用 StreamReader 和 StreamWriter二、二进

在Linux中改变echo输出颜色的实现方法

《在Linux中改变echo输出颜色的实现方法》在Linux系统的命令行环境下,为了使输出信息更加清晰、突出,便于用户快速识别和区分不同类型的信息,常常需要改变echo命令的输出颜色,所以本文给大家介... 目python录在linux中改变echo输出颜色的方法技术背景实现步骤使用ANSI转义码使用tpu

Conda与Python venv虚拟环境的区别与使用方法详解

《Conda与Pythonvenv虚拟环境的区别与使用方法详解》随着Python社区的成长,虚拟环境的概念和技术也在不断发展,:本文主要介绍Conda与Pythonvenv虚拟环境的区别与使用... 目录前言一、Conda 与 python venv 的核心区别1. Conda 的特点2. Python v

Spring Boot中WebSocket常用使用方法详解

《SpringBoot中WebSocket常用使用方法详解》本文从WebSocket的基础概念出发,详细介绍了SpringBoot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消... 目录一、WebSocket基础概念1.1 什么是WebSocket1.2 WebSocket与HTTP

java中反射Reflection的4个作用详解

《java中反射Reflection的4个作用详解》反射Reflection是Java等编程语言中的一个重要特性,它允许程序在运行时进行自我检查和对内部成员(如字段、方法、类等)的操作,本文将详细介绍... 目录作用1、在运行时判断任意一个对象所属的类作用2、在运行时构造任意一个类的对象作用3、在运行时判断

linux hostname设置全过程

《linuxhostname设置全过程》:本文主要介绍linuxhostname设置全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录查询hostname设置步骤其它相关点hostid/etc/hostsEDChina编程A工具license破解注意事项总结以RHE

MySQL 中的 CAST 函数详解及常见用法

《MySQL中的CAST函数详解及常见用法》CAST函数是MySQL中用于数据类型转换的重要函数,它允许你将一个值从一种数据类型转换为另一种数据类型,本文给大家介绍MySQL中的CAST... 目录mysql 中的 CAST 函数详解一、基本语法二、支持的数据类型三、常见用法示例1. 字符串转数字2. 数字

SpringBoot中SM2公钥加密、私钥解密的实现示例详解

《SpringBoot中SM2公钥加密、私钥解密的实现示例详解》本文介绍了如何在SpringBoot项目中实现SM2公钥加密和私钥解密的功能,通过使用Hutool库和BouncyCastle依赖,简化... 目录一、前言1、加密信息(示例)2、加密结果(示例)二、实现代码1、yml文件配置2、创建SM2工具

MyBatis-Plus 中 nested() 与 and() 方法详解(最佳实践场景)

《MyBatis-Plus中nested()与and()方法详解(最佳实践场景)》在MyBatis-Plus的条件构造器中,nested()和and()都是用于构建复杂查询条件的关键方法,但... 目录MyBATis-Plus 中nested()与and()方法详解一、核心区别对比二、方法详解1.and()