跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C算法

Linux 底层核心精讲:环境变量、命令行参数与程序地址空间

环境变量是操作系统运行环境的隐形配置,通过 PATH、HOME 等变量管理全局状态。命令行参数为程序提供外部输入,区分常规命令与内建命令的执行机制。程序地址空间划分了代码、数据、堆栈区域,虚拟地址通过页表映射至物理内存,配合写时拷贝机制实现进程隔离与优化。

颠三倒四发布于 2026/1/30更新于 2026/10/899 浏览
Linux 底层核心精讲:环境变量、命令行参数与程序地址空间

Linux 底层核心精讲:环境变量、命令行参数与程序地址空间

环境变量是程序运行的'隐形配置',程序地址空间则是进程访问内存的'底层规则'。理解这两者,不仅能解释'为什么命令能直接运行'、'进程如何安全访问内存'等核心问题,更能为编写灵活的命令行工具、排查内存相关问题提供底层支撑。

本文从环境变量的概念、常用指令与系统调用入手,逐步延伸到 main 函数的命令行参数用法、常规命令与内建命令的区别,最终深入程序地址空间的布局、虚拟地址与物理地址的映射原理(含页表、写时拷贝等核心机制)。每个知识点都配套具体指令示例,文末还附上高频习题及解析,帮助读者夯实基础。

环境变量

环境变量一般指在操作系统中用来指定运行环境的一些参数。不同的环境变量有不同的用途,通常具有全局属性——这里的全局属性意味着它们可以被子进程继承。

Bash 本身在启动的时候,会从操作系统的配置文件中读取环境变量信息。每次启动,环境变量都会被重置;父进程的环境变量变了,子进程会跟着变。

常见的环境变量

  • PATH:指定命令的搜索路径。
  • HOME:指定用户的主工作目录。
  • SHELL:当前 Shell,其值通常是 /bin/bash。
  • HISTSIZE:history 能保存之前使用指令的最大条数。
  • SSH_TTY:终端设备的路径(SSH 远程登录的话会有)。
  • USER 和 LOGNAME:代表当前登录的用户是谁(这俩没啥区别)。
  • PWD:表示当前的路径(修改这个不会改变现在在哪个路径哈)。
  • OLDPWD:通过 cd 命令切换目录时,标记上一次的路径。

和环境变量相关的指令

  • echo:显示某个环境变量值。
  • export:设置一个新的环境变量。
  • env:显示所有环境变量(想只看一个的话就配合 grep 过滤)。
  • unset:清除环境变量。
  • set:显示所有本地变量和环境变量。

修改环境变量示例:

PATH=$PATH:/home/renshen

引申一下:本地变量只在当前 Bash 进程里有效。将本地变量转变成环境变量只需执行 export 变量名(不用加=值)。

通过系统调用获取或设置环境变量

系统调用包括 getenv 和 putenv。

另外,chdir 这个系统调用可以改路径。

main 函数的命令行参数

其实 main 函数原型是 int main(int argc, char *argv[], char *env[])。

第一个参数是接受到的命令行参数的总个数(包括程序名)。 第二个参数是命令行参数(最后一个位置会放一个 NULL),命令行的指令当字符串传过来的。 第三个参数是环境变量数组。

在进程进行时,都会被传入两张核心向量表——命令行参数表和环境变量表。所以,不要 char *env[] 也行,直接在代码里 extern char **environ。env 怎么用,这个也怎么用,例如 environ[0]。这个全局变量指向环境变量表(不用头文件)。

main 函数这样设置的目的,是为指令、工具、软件等提供命令行选项的支持。

常规命令和内建命令

  • 常规命令:通过 Bash 创建子进程执行。
  • 内建命令:Bash 不创建子进程,自己亲自执行,类似 Bash 调用系统提供的函数,比如 echo。

程序地址空间

进程地址空间

进程地址空间分配示意图

这是进程地址空间的分配图。最下面地址是 0x00000000,最上面是 0xFFFFFFFF(32 位的话)。

Linux 里面这个叫做线性地址或者虚拟地址(线性地址和虚拟地址不是一个东西哈)。

  • 堆里面存放的是动态开辟的空间。
  • 栈里面存放的是局部变量等。
  • 未初始化全局变量和已初始化全局变量共同组成了全局数据区。
  • 栈区的上方还有命令行参数环境变量区(这个是低地址向高地址长的)。

引申:static 修饰的局部变量在编译的时候会被编译到全局数据区。

这里的存放并非真实的存放哈,只是存的地址映射,真正的东西存放在物理内存里面。在调试的时候查看内存就是看的这个虚拟内存的地址;物理地址用户是看不到的。

关于进程地址空间的大小(32 位计算机的话):操作系统给每个进程都假装许诺给他 4GB 的内存,但是其实是进程需要多少空间,操作系统才给多少(其实进程全部加起来一共只有 4GB 能用)。

几个关键问题:

  1. 什么是地址空间? 本质是一个描述进程可视范围的大小的内核的一个数据结构对象,跟 PCB 差不多(也是先描述再组织)。

  2. 如何理解地址空间上的区域划分? 其实就是搞了个结构体,里面存这个地址范围的 begin 和 end;对空间的区域增大减少其实也就是把 begin 和 end 改了。在这个地址范围内,这个连续的空间中的每一个最小单位都可以有地址,这个地址可以被这部分直接使用。

  3. 32 位地址总线为什么能支持最大 4GB 内存寻址? 32 位地址总线一共 32 根,每根的状态只有 0 或 1,所以就是 $2^{32}$,然后一个地址对应着 1 个字节的空间,所以就是 4GB。所以操作系统给进程地址空间画饼的大小是 4GB 的内存。

进程地址空间的意义:

  1. 让进程以统一的视角看待内存。
  2. 有了进程地址空间可以让我们在访问内存时,增加一个转换的过程。在这个转换过程中,可以对寻址请求进行审查,所以进行异常访问的话,就会被直接拦截,这个请求就不会到物理内存那里,保护了物理内存的安全。
  3. 有地址空间和页表的存在,将进程管理模块和内存管理模块进行了解耦合(就是降低依赖关系),这也是进程具有独立性的原因。

虚拟地址和物理地址间的关系

虚拟地址与物理地址映射示意图

每一个进程都有自己的一个进程地址空间。父进程和子进程的进程地址空间也是分开的,但是页表部分是一样的。但是如果进行了修改操作的话,页表的虚拟地址和物理地址的对应关系就变了(这其实也是父子进程代码可以共享的原因)。

在父进程没有分出子进程时,页表里面可读可写的部分会在分出子进程之后全部变成只读部分。如果父进程如果想改值,也会触发写时拷贝。

问题:进程怎么找到自己的页表?

CPU 里面有个 CR3 寄存器会存储当前进程的页表地址(操作系统写入的,这里存的是物理地址哈)。上一个进程的页表可以通过进程控制块的 mm_struct 找到。

所以对进程的认知进一步加深了:

  1. 进程的内核数据结构包括 task_struct 和 mm_struct 和页表。
  2. 进程在被创建的时候是先创建的内核数据结构,再加载对应的可执行程序。

知识巩固

Q1: 不属于冯诺依曼体系结构必要组成部分是: A. CPU // 运算器与控制器 B. Cache // 缓存(一种技术)// 是为了缓解 CPU 与内存之间的速度差异,属于体系结构的优化补充,而非必须的核心组成部分 C. RAM // 属于存储器 D. ROM // 属于存储器

答案:B

Q2: 冯诺依曼体系结构计算机的基本原理是: A. 信息存储 B. 存储智能 C. 数字控制 D. 存储程序和程序控制

答案:D

Q3: 系统调用函数的执行过程应该是在用户态。

答案:错。应该是系统调用的运行过程是在内核态完成的。

Q4: 系统感知进程的唯一实体是: A. 进程 id B. 进程控制块 C. 进程管理器 D. 进程名

答案:B

Q5: 在抢占式多任务处理中,进程被抢占时,哪些运行环境需要被保存下来?(多选) A. 所有 cpu 寄存器的内容 // cpu 上正在处理的数据 B. 全局变量 C. 页表指针 // 程序切换时会将页表起始地址加载到寄存器中 D. 程序计数器

答案:ACD

Q6: 关于僵尸进程,以下描述正确的有: A. 僵尸进程必须使用 waitpid/wait 接口进行等待 B. 僵尸进程最终会自动退出 C. 僵尸进程可以被 kill 命令杀死 D. 僵尸进程是因为父进程先于子进程退出而产生的

答案:A

Q7: 以下关于孤儿进程的描述正确的有(多选): A. 父进程先于子进程退出,则子进程成为孤儿进程 B. 孤儿进程会产生资源泄漏 C. 孤儿进程运行在系统后台 D. 孤儿进程没有父进程

答案:AC

Q8: 以下描述错误的是: A. 守护进程:运行在后台的一种特殊进程,独立于控制终端并周期性地执行某些任务。 B. 僵尸进程:一个进程 fork 子进程,子进程退出,而父进程没有 wait/waitpid 子进程,那么子进程的进程描述符仍保存在系统中,这样的进程称为僵尸进程。 C. 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,这些子进程称为孤儿进程。(孤儿进程将由 init 进程收养并对它们完成状态收集工作) D. 精灵进程:精灵进程退出后会成为僵尸进程

答案:D。引申:守护进程和精灵进程其实是一个东西。

Q9: 使用 shell 时,默认的环境变量放在哪里? A. ~/.bash_profile B. ~/.bash C. /etc/profile.d D. ~/bash

答案:A

Q10: 以下描述正确的有(多选): A. 子进程默认会复制拥有与父进程相同的环境变量 B. 环境变量使 shell 运行环境配置变的更加复杂 // 可以使运行环境的配置更加灵活简单 C. 环境变量可以使用 export 命令设置 D. 删除一个环境变量可以使用 unset 和 rm 命令

答案:AC

Q11: 在 CPU 和物理内存之间进行地址转换时,()将地址从虚拟(逻辑)地址空间映射到物理地址空间。 A. TCB // 线程控制块 B. MMU // 内存管理单元 C. CACHE // 高速缓冲存储器 D. DMA // 直接内存访问 (不经过 CPU)

答案:B

目录

  1. Linux 底层核心精讲:环境变量、命令行参数与程序地址空间
  2. 环境变量
  3. 常见的环境变量
  4. 和环境变量相关的指令
  5. 通过系统调用获取或设置环境变量
  6. main 函数的命令行参数
  7. 常规命令和内建命令
  8. 程序地址空间
  9. 进程地址空间
  10. 虚拟地址和物理地址间的关系
  11. 知识巩固

更多推荐文章

查看全部
  • Coze扣子「百套AI工作流」模板合集
  • Java Set 集合并非完全无序:LinkedHashSet 与 TreeSet 详解
  • C++ 继承机制详解:从概念到转换规则
  • Linux 内核 list_for_each_entry 链表遍历详解
  • OpenTiny 前端智能化实战:Renderless 架构下 DialogBox 可缩放实现
  • OpenHarmony 中使用 web_socket 实现跨平台 WebSocket 通信
  • Android 面试核心知识点汇总:32 个模块技术问答解析
  • Easy R1 训练环境搭建与配置实战
  • 美妆企业自建专属大模型:开源模型与训练方法汇总
  • C++ 多线程进阶:互斥锁与竞态条件解析
  • 基于 Llama-Factory 构建弹幕互动机器人
  • FPGA 实现 CIC 抽取滤波器
  • Java 音视频场景面试实战:内存模型、Spring Boot 与 Kafka
  • 如何在 VS Code 中为 GitHub Copilot 配置 Agent Skill
  • Neo4j 图数据库安装与基础使用指南
  • 基于 AI 的智能算力分配方法
  • LangChain 聊天模型多场景实战:从固定角色到合规客服
  • FPGA 是什么?一文读懂现场可编程门阵列
  • php正则表达式替换URL链接地址为指定url的形式
  • AstrBot+NapCat 一键部署智能 QQ 机器人及 cpolar 公网访问配置

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online