《深入理解Linux内核》 第二十章:深入理解 Linux 程序执行机制(Program Execution)

关键词:exec 系列系统调用、可执行文件格式(ELF)、用户地址空间、内存映射、动态链接、栈初始化、入口点、共享库、内核态与用户态切换


一、概述:程序是如何被执行的?

1.1 本质

Linux 中,“程序执行”是指某个已存在的进程调用 exec() 系列系统调用后,由内核将该进程的上下文替换为另一个程序的上下文,从而在相同 PID 下运行新的程序。

fork() 创建新进程不同,exec() 并不创建新进程,而是替换现有进程的地址空间。

1.2 系统调用家族

int execl(const char *path, const char *arg, ..., NULL);
int execv(const char *path, char *const argv[]);
int execle(const char *path, const char *arg, ..., NULL, char *const envp[]);
int execve(const char *filename, char *const argv[], char *const envp[]);

内核中的入口点为:

SYSCALL_DEFINE3(execve, const char __user *, filename,const char __user *const __user *, argv,const char __user *const __user *, envp)

二、从用户态到内核态

2.1 用户空间的 execve()

程序调用 exec 函数族(如 execv())最终都会转换为 execve() 系统调用。

其传入参数包括:

  • 程序路径 filename
  • 命令行参数 argv
  • 环境变量 envp

2.2 内核入口

系统调用处理流程:

  1. 用户态调用 execve;
  2. CPU 切换到内核态;
  3. 内核从系统调用表中查找 sys_execve()
  4. 调用 do_execve()do_execveat_common()
  5. 进入真正的程序替换逻辑。

三、execve 内核实现流程

程序执行的内核主干逻辑如下:

do_execveat_common()└── exec_binprm()└── search_binary_handler()└── load_elf_binary() 或其他格式

3.1 创建 binprm 结构

struct linux_binprm {char buf[BINPRM_BUF_SIZE];struct file *file;...
};
  • 包含传入参数、程序路径;
  • 读取前128字节,判断文件格式;
  • 设置执行权限、清除信号等。

3.2 判断可执行文件格式

执行 search_binary_handler()

  • 检查 ELF 标志(前4字节为 0x7f + ELF);
  • 若是脚本(以 #! 开头),调用 load_script()
  • 若是 ELF 文件,调用 load_elf_binary()

四、加载 ELF 可执行文件

4.1 ELF 文件结构

ELF(Executable and Linkable Format)是 Linux 下标准的可执行文件格式。

主要结构:

ELF Header
|
+-- Program Header Table (段表)
|
+-- Section Header Table (仅编译调试时使用)
|
+-- 数据段、代码段、堆、符号表等
  • ELF Header:描述整个文件;
  • Program Header Table:决定哪些段需要加载;
  • 每个段描述:起始地址、偏移、大小、属性(可执行、可写等)。

4.2 ELF 加载流程

load_elf_binary() 实现步骤:

  1. 验证 ELF 魔数;
  2. 解析 Program Header Table;
  3. 使用 do_mmap() 映射段到用户地址空间;
  4. 设置 mm->start_codestart_databrk
  5. 初始化用户栈;
  6. 设置 e_entry(程序入口点);
  7. 调用 start_thread() 设置寄存器(EIP / RIP);
  8. 切换到用户态开始执行。

五、构建新用户地址空间

5.1 mm_struct 的替换

每个进程都有一个 mm_struct

struct mm_struct {struct vm_area_struct *mmap;struct pgd_t *pgd;...
};

当调用 execve 时,原有的地址空间会被释放(mm_release()),新的 mm_struct 被创建并绑定。

5.2 do_mmap 的作用

调用 do_mmap() 将 ELF 段映射到用户空间:

  • .text 映射为可执行段;
  • .data 映射为可写段;
  • .bss 用于初始化堆;
  • 其他段如 .rodata 也被映射;

mmap 映射区域都以 vm_area_struct 记录,最终组成进程虚拟内存布局。


六、用户栈与参数传递

6.1 参数与环境变量的拷贝

exec 调用时会将 argvenvp 拷贝到内核缓冲区,再构建用户栈:

  • 栈顶:参数数量 argc;
  • 紧接着:argv 指针数组;
  • 然后:envp 指针数组;
  • 最后是 NULL terminator;

6.2 栈构建逻辑

setup_arg_pages()
create_elf_tables()
  • 将参数字符串数据拷贝到用户栈;
  • 设置 AT_PHDRAT_ENTRY 等 auxv;
  • 创建适配动态链接器的数据结构(如 ld.so);

七、动态链接与共享库加载

7.1 动态链接器的作用

如果 ELF 是动态链接的,其 PT_INTERP 段指定了动态链接器(如 /lib/ld-linux.so.2)。

流程:

  1. 加载主 ELF 文件;

  2. 加载动态链接器;

  3. 动态链接器运行在用户态,负责:

    • 加载所需 .so 文件;
    • 执行符号解析与重定位;
    • 最终跳转到 main() 函数。

7.2 预加载库

环境变量:

LD_PRELOAD=/lib/myhook.so ./app

可在程序启动前注入库,进行函数劫持等操作。


八、执行权限与文件检查

8.1 权限验证

  • 检查可执行文件是否有执行权限;
  • 检查是否可读取;
  • 判断 SUID/SGID 是否生效;
  • 对脚本文件,检查 #! 指向的解释器;

8.2 setuid 程序执行注意点

当可执行文件具有 SUID 权限时:

  • 进程会提升有效 UID 为目标用户;
  • 内核需清除大部分内存内容,防止信息泄漏;
  • 需要设置 secureexec 标志位,屏蔽某些危险变量(如 LD_PRELOAD);

九、执行结果与返回路径

9.1 执行完成前的最后一步

  • 在 execve 完成所有加载后,调用 start_thread() 设置 PC/SP;
  • 切换到用户态入口点开始执行;
  • 若失败,则返回错误码。

9.2 execve 成功永不返回

一旦 execve 成功,旧进程空间完全被新程序替换,除非失败,调用永不返回。


十、文件格式支持机制(binfmt)

Linux 支持多种可执行文件格式(ELF, a.out, scripts, Java 等):

struct linux_binfmt {int (*load_binary)(struct linux_binprm *);int (*load_shlib)(struct file *);...
};

常见格式注册:

  • register_binfmt(&elf_format);
  • register_binfmt(&script_format);

这些接口挂载在 search_binary_handler() 中被遍历查找处理程序。


十一、源码路径与调试技巧

路径说明
fs/exec.cexecve 核心逻辑
fs/binfmt_elf.cELF 加载实现
fs/binfmt_script.c脚本执行逻辑
arch/x86/kernel/process.c架构相关 start_thread() 实现
include/linux/binfmts.hbinfmt 接口定义
/proc/self/maps当前进程地址空间布局查看

调试工具:

  • strace ./a.out:追踪 execve 执行;
  • readelf -a ./a.out:查看 ELF 内容;
  • lsof -p PID:查看进程打开的文件;
  • gdb:调试执行流程与链接器行为。

十二、小结

  • execve 系统调用是 Linux 执行程序的核心;
  • 内核根据 ELF 或脚本格式加载目标程序;
  • 构建新的用户空间,包括栈、段映射、链接器加载;
  • 支持 SUID、环境变量注入、安全过滤;
  • 动态链接器负责完成后续 .so 加载与符号重定位;
  • 内核中设计清晰,分层合理,是内核与用户交互的关键桥梁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/87996.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/87996.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/87996.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

服务器如何配置防火墙规则以阻止恶意流量和DDoS攻击?

防火墙是保护服务器免受恶意流量和 DDoS 攻击的第一道防线。通过合理配置防火墙规则,可以有效阻止恶意访问、限制不必要的流量,并减少攻击对服务器的影响。以下是配置防火墙规则的全面指南,包括基础规则设置、防御 DDoS 攻击的高级策略和最佳…

持续性投入是成就自我价值的关键一环

概述 时间,的唯一公平之处就是给你我的长度是相同的,这也是它唯一公平,也是不公平的地方。 所谓的公平,就是不患寡而患不均中所说的平均。 所谓的不公平就是,相同时间内我们彼此对应的标价不同,延伸到后…

使用allegro在BoardGeometry的Silkscreen_Top层画出图案

目录 1. 图形及图形放置显示2. 绘制 1. 图形及图形放置显示 绘制完成图案: 导出后图案: 2. 绘制 图层选中; 画圆型; 半径3.5mm,原点生成; 在图案中挖空; 用指令走线: …

Kotlin 协程:Channel 与 Flow 深度对比及 Channel 使用指南

前言 在 Kotlin 协程的异步编程世界里,Channel 和 Flow 是处理数据流的重要工具,它们有着不同的设计理念与适用场景。本文将对比二者功能与应用场景,详细讲解 Channel 的使用步骤及注意事项 。 一、Channel 与 Flow 的特性对比 Channel 是协程…

MYsql主从复制部署

MySQL 主从复制是将主数据库的变更自动同步到从数据库的过程,常用语读写分离、高可用性和数据备份。 1.环境准备 确保主从服务器已安装相同版本的 MySQL,并能通过网络互相访问。 # 检查 MySQL 版本 mysql -V 2.配置主服务器 (1&#xff0…

安灯呼叫看板如何实现汽车生产异常秒级响应

在汽车零部件工厂的静置车间,传统生产管理依赖人工巡检与纸质记录,存在效率低、信息滞后、异常响应慢等问题。某汽车厂曾因物料静置时间未及时监控,导致批次混料,损失超10万元。而安灯呼叫看板系统的引入,通过实时状态…

构造函数注入在spring boot 中怎么使用详解

我们来详细讲解一下在 Spring Boot 中如何使用构造函数注入,并通过一个完整的、可运行的例子来演示。 构造函数注入是 Spring 官方最推荐的依赖注入方式,因为它能保证对象的不可变性和依赖的完整性。 核心理念 在 Spring Boot 中使用构造函数注入非常简单…

2025.6.30-2025.7.06第26周:第一次参加头马演讲俱乐部

现在是周一早上6:23,我开始写上周的周总结。 3件超出预期的事 参加头马俱乐部绝对是最超出预期的,使得这个周末格外的快乐简历的第一版终于改完了,花了好长的时间,其中有一天心情还很荡,因为,我想&#x…

2025使用VM虚拟机安装配置Macos苹果系统下Flutter开发环境保姆级教程--下篇

其实如何安装VM,如何安装MACOS网上的教程很多,我只是结合我的体验重新整理了一次,接下来才进入本教程最核心的部分,Flutter开发环境的配置部分。、一.配置前准备 主要是准备相应的工具包,以及其他虚拟机设置1.工具包 工具包的版本也可以自行配置,我这主要是我使用的是F…

QSPI、OSPI与FSMC的区别与内存映射分析

QSPI、OSPI与FSMC的区别与内存映射分析 基本概念与区别 1. FSMC (灵活静态存储控制器) 接口类型:并行接口,通常8/16位数据总线总线标准:传统并行总线协议速度:相对较低,通常最高约100MHz应用场景:SRAM、NOR…

系统思考与心智模式探索

成长的真正障碍,不是能力的不足,而是看待问题的局限。 在复杂多变的商业环境中,我们往往习惯于解决“眼前”的问题,却忽视了深藏背后的系统性障碍。我们看到的只是表面的“症状”,而真正的根源,却往往隐藏…

物联网技术的关键技术与区块链发展趋势的深度融合分析

一、物联网技术的核心架构与关键技术 物联网技术体系由感知层、网络层、平台层、应用层和安全层构成,各层技术协同工作,实现物理世界与数字世界的深度融合。 感知层:物联网的“感官” 传感器技术:包括环境传感器(温度…

针对Exhcnage Server的攻击防范措施

一、背景介绍最近,安全研究人员揭露了一个名为 NightEagle(又名 APT-Q-95) 的高级持续性威胁(APT)组织。这个组织被观察到利用 Microsoft Exchange 服务器中的零日漏洞链 进行攻击,其主要目标是中国政府、国…

编程基础:继承

能帮到你的话,就给个赞吧 😘 文章目录继承:使用基类成员:前提——派生类必须同样支持基类成员(组件和功能)示例:动物Animal 有 鳃 和 会飞。则 鸟Bird 不是 Animal,因为Bird虽会飞,却没有鳃。鱼…

TMC4361A 使用(未验证)

prompt 我用STM32F103C8T6 来控制 TMC4361A 运动控制芯片 ,我配置 STM32F103C8T6 的 SPI1 与 TMC4361A 进行通信,配置 PA4 作为片选线,配置 PA8 作为 RCC_MCO 输入时钟输入到 TMC4361A, 并将其连接到TMC4361A的CLK_EXT引脚。我想控制 TMC4361…

深度剖析:如何解决Node.js中mysqld_stmt_execute参数错误

在Node.js后端开发中,使用mysql2等数据库驱动与MySQL/MariaDB交互时,Incorrect arguments to mysqld_stmt_execute 是一个令人头疼的错误。它通常意味着你传递给SQL预处理语句的参数数量与SQL字符串中问号(?)占位符的数量不匹配。…

Vue3 学习教程,从入门到精通,Vue 3 安装指南及语法知识点详解(2)

Vue 3 安装指南及语法知识点详解 本文将详细介绍 Vue 3 的所有安装方式,并深入讲解 Vue 3 的语法知识点。此外,还将提供一些综合性案例,展示如何综合运用 Vue 3 的各项功能。一、安装 Vue 3 的所有方式 Vue 3 提供了多种安装方式,…

C++基础复习笔记

一、数组定义 在C中,数组初始化有多种方式,以下是常见的几种方法: 默认初始化 数组元素未显式初始化时,内置类型(如int、float)的元素值未定义(垃圾值),类类型调用默认构…

手机和PC远控安全深度测评:TeamViewer/ToDesk/向日葵安全防线对比

声明:本测试报告系作者基于个人兴趣及使用场景开展的非专业测评,测试过程中所涉及的方法、数据及结论均为个人观点,不代表任何官方立场或行业标准。 一、引言 当下远程控制技术已深度融入大众的工作与生活,无论是上班族在家操…

Windows 11的开始菜单调整为左下角布局

1.桌面右键个性化 2.个性化中任务栏 3.任务栏选择任务栏行为 4.任务栏行为中 任务栏对齐方式选择靠左即可