# Linux 环境变量与交叉编译
## 1. 什么是环境变量
环境变量本质上就是一组 **键=值** 的配置,例如:
“`bash
PATH=/usr/bin:/bin:/opt/gcc-arm/bin
HOME=/home/erhu
LANG=zh_CN.UTF-8
“`
它们的核心作用是:
> **告诉当前 shell 以及它启动出来的子进程,应该在什么环境下运行。**
你可以把它理解成:
– shell 是“父进程”
– `make`、`gcc`、`arm-linux-gnueabihf-gcc` 是它启动的“子进程”
– 父进程会把环境变量传给子进程
所以一旦你在 shell 里设置了环境变量,后面运行的编译器、脚本、构建系统通常都能看到它。
—
## 2. 环境变量有什么作用
环境变量的作用很广,但你学交叉编译时,最重要的是这几类。
### 2.1 告诉系统“命令去哪里找”—— PATH
例如:
“`bash
export PATH=/opt/gcc-arm/bin:$PATH
“`
作用:
– 当你输入 `arm-linux-gnueabihf-gcc` 时
– shell 会去 `PATH` 里的目录依次查找
– 找到 `/opt/gcc-arm/bin/arm-linux-gnueabihf-gcc` 就执行
所以安装交叉编译器后,最常做的就是把工具链的 `bin/` 加到 `PATH` 中。
### 2.2 告诉构建系统“用哪个工具链”—— ARCH、CROSS_COMPILE
内核编译里很常见:
“`bash
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
“`
作用:
– `ARCH=arm`:告诉内核这是 ARM 平台
– `CROSS_COMPILE=arm-linux-gnueabihf-`:告诉 Makefile 交叉工具前缀是什么
这样内核构建时会去调用:
“`bash
arm-linux-gnueabihf-gcc
arm-linux-gnueabihf-ld
arm-linux-gnueabihf-objcopy
“`
注意:
– `CROSS_COMPILE` 一般是**前缀**
– 真正能不能找到这些命令,还要依赖 `PATH`,或者你把绝对路径直接写进 `CROSS_COMPILE`
例如:
“`bash
export CROSS_COMPILE=/opt/gcc-arm/bin/arm-linux-gnueabihf-
“`
这样即使 `PATH` 没配,也能找到。
### 2.3 告诉程序“库和头文件在哪”—— LD_LIBRARY_PATH、PKG_CONFIG_PATH 等
例如:
– `LD_LIBRARY_PATH`:运行时动态库搜索路径
– `LIBRARY_PATH`:链接时库搜索路径
– `C_INCLUDE_PATH`:头文件搜索路径
– `PKG_CONFIG_PATH`:`pkg-config` 查 `.pc` 文件的路径
这类变量在应用开发中更常见,在内核编译里不如 `PATH`、`ARCH`、`CROSS_COMPILE` 常用。
### 2.4 控制程序行为
比如:
– `HOME`:用户家目录
– `SHELL`:当前使用的 shell
– `TERM`:终端类型
– `LANG` / `LC_ALL`:语言、编码、区域设置
– `EDITOR`:默认编辑器
所以环境变量不仅影响编译,也影响整个命令行环境。
—
## 3. 为什么装交叉编译器时常改 /etc/profile
因为交叉编译器安装后,系统默认**不知道它在哪里**。
比如工具链放在:
“`bash
/opt/gcc-linaro-arm/bin
“`
如果不加到 `PATH`,你输入:
“`bash
arm-linux-gnueabihf-gcc -v
“`
系统可能会提示:
“`bash
command not found
“`
所以很多教程会在 `/etc/profile` 里加:
“`bash
export PATH=/opt/gcc-linaro-arm/bin:$PATH
“`
这样所有用户登录 shell 时都会自动带上这个路径。
—
## 4. 环境变量是怎么生效的
这里有个很关键的概念。
### 4.1 shell 变量 ≠ 环境变量
例如:
“`bash
MYVAR=123
“`
这只是当前 shell 里的一个普通变量。
如果你执行:
“`bash
export MYVAR
“`
或者直接:
“`bash
export MYVAR=123
“`
它才会变成**环境变量**,并传给子进程。
### 4.2 子进程继承
例如:
“`bash
export CROSS_COMPILE=arm-linux-gnueabihf-
make
“`
执行流程可以理解成:
“`text
bash
└─ make
└─ arm-linux-gnueabihf-gcc
“`
因为 `CROSS_COMPILE` 是 export 过的,所以 `make` 能看到,进而调用对应编译器。
—
## 5. 修改环境变量有哪些方法
下面按“临时 / 永久 / 当前用户 / 全局”来整理。
### 方法 1:命令行临时设置,仅当前 shell 有效
“`bash
export PATH=/opt/gcc-arm/bin:$PATH
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
“`
**特点:**
– 只对当前终端会话有效
– 关闭终端后失效
– 当前 shell 启动的子进程可见
**适用场景:**
– 临时测试
– 不想污染系统环境
– 验证工具链是否可用
**优点:**
– 安全
– 改动小
– 出问题直接关终端就恢复
**缺点:**
– 每次开新终端都要重新设置
### 方法 2:只对某一条命令生效
“`bash
PATH=/opt/gcc-arm/bin:$PATH make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
“`
或者:
“`bash
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make zImage
“`
**特点:**
– 只影响这一次命令
– 不污染当前 shell 后续环境
**适用场景:**
– 最推荐的**项目级**做法
– 特别适合多个工具链并存
**优点:**
– 作用范围最小
– 不容易误用错工具链
– 最适合内核/驱动工程
**缺点:**
– 每次命令都要写一遍
### 方法 3:写入用户自己的 ~/.bashrc
“`bash
export PATH=/opt/gcc-arm/bin:$PATH
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
“`
**特点:**
– 只对当前用户生效
– 一般对交互式 bash shell 有效
– 新开终端后自动生效
**适用场景:**
– 你自己长期使用某个工具链
– 不想影响其他用户
**优点:**
– 常用、方便
– 不需要 root 权限
– 比改 `/etc/profile` 更安全
**缺点:**
– 只对当前用户有效
– 对某些非交互/非 bash 场景不一定生效
应用后通常执行:
“`bash
source ~/.bashrc
“`
### 方法 4:写入 ~/.profile 或 ~/.bash_profile
**特点:**
– 通常用于 login shell
– 用户登录时生效
**与 ~/.bashrc 的区别:**
– `~/.bash_profile` / `~/.profile` 更偏“登录时初始化”
– `~/.bashrc` 更偏“交互式 bash 的配置”
一般经验:
– 命令别名、提示符常放 `.bashrc`
– 登录环境变量常放 `.profile` / `.bash_profile`
### 方法 5:写入 /etc/profile
“`bash
export PATH=/opt/gcc-arm/bin:$PATH
“`
**特点:**
– 系统全局
– 对所有用户的登录 shell 生效
– 需要 root 权限
**适用场景:**
– 机器是专门的开发机
– 所有人都要用这套工具链
**优点:**
– 一次配置,所有用户受益
– 传统教程里很常见
**缺点:**
– 改动范围大
– 配错会影响所有用户
– 不利于多个工具链共存
– 某些非登录 shell、GUI 程序未必按预期生效
**建议:**
如果一定要做全局配置,更推荐用 `/etc/profile.d/xxx.sh`,不要直接改 `/etc/profile`。
### 方法 6:放到 /etc/profile.d/xxx.sh
例如新建:
“`bash
/etc/profile.d/arm-toolchain.sh
“`
内容:
“`bash
export PATH=/opt/gcc-arm/bin:$PATH
“`
**特点:**
– 也是系统全局
– 更模块化
– 通常会被 `/etc/profile` 自动加载
**优点:**
– 管理清晰
– 方便删除/维护
– 比直接改 `/etc/profile` 更规范
**缺点:**
– 仍然是全局生效
– 仍需 root 权限
### 方法 7:写入 /etc/environment
例如:
“`bash
PATH=”/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/gcc-arm/bin”
“`
**特点:**
– 更偏“系统环境变量配置”
– 不是标准 shell 脚本
– 通常由 PAM 在登录时读取
**与 /etc/profile 的区别:**
– `/etc/profile` 是 shell 脚本
– `/etc/environment` 是简单的键值对文件
– 不能像 shell 那样灵活写逻辑
– 一般不写 `export`
– 对 `$PATH` 这种展开支持不如 shell 脚本直观可靠
**适用场景:**
– 设置简单、固定的系统级变量
### 方法 8:写成单独脚本,手动 source
例如建一个:
“`bash
~/env/imx6ull-gcc.sh
“`
内容:
“`bash
export PATH=/opt/gcc-arm/bin:$PATH
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
“`
使用时:
“`bash
source ~/env/imx6ull-gcc.sh
“`
**特点:**
– 很适合项目/平台隔离
– 不改系统全局
**优点:**
– 多套工具链管理最方便
– 适合嵌入式开发
– 切换平台容易
**缺点:**
– 每次要手动 `source`
### 方法 9:systemd 服务里单独设置
如果某个服务要用特定环境变量,可以在 unit 文件里写:
“`ini
[Service]
Environment=”PATH=/opt/gcc-arm/bin:/usr/bin:/bin”
Environment=”MYVAR=123″
EnvironmentFile=/etc/myenv.conf
“`
**特点:**
– 只对这个服务生效
– 和 shell 登录环境是两套体系
**适用场景:**
– 守护进程
– 自动化构建服务
– CI/CD
**与 /etc/profile 的区别:**
– `/etc/profile` 是 shell 登录时读
– systemd 服务默认不会依赖你的 shell 配置
—
## 6. 这些方法的核心区别
可以从 4 个维度来记:
1. **作用范围**:当前命令 / 当前 shell / 当前用户 / 全系统
2. **是否持久**:临时 / 永久
3. **在哪种场景生效**:登录 shell / 交互 shell / 服务进程
4. **维护风险**:越全局,影响越大
| 方法 | 作用范围 | 是否永久 | 谁能用 | 典型用途 |
|—|—|—|—|—|
| `export VAR=…` | 当前 shell + 子进程 | 否 | 当前终端 | 临时测试 |
| `VAR=… command` | 仅当前命令 | 否 | 当前命令 | 最干净的项目级用法 |
| `~/.bashrc` | 当前用户 | 是 | 当前用户 | 日常开发 |
| `~/.profile` / `~/.bash_profile` | 当前用户登录环境 | 是 | 当前用户 | 登录初始化 |
| `/etc/profile` | 全局 | 是 | 所有用户 | 全局登录环境 |
| `/etc/profile.d/*.sh` | 全局 | 是 | 所有用户 | 更规范的全局配置 |
| `/etc/environment` | 全局系统环境 | 是 | 登录用户/会话 | 简单静态变量 |
| systemd `Environment=` | 单个服务 | 是 | 指定服务 | 服务运行环境 |
—
## 7. /etc/profile 和 ~/.bashrc 到底区别在哪
这是最容易混淆的地方。
### /etc/profile
– 系统级
– 登录 shell 常读
– 影响所有用户
– 需要 root
### ~/.bashrc
– 用户级
– 当前用户自己的 bash 配置
– 不需要 root
– 新开终端常会生效
所以:
– **你自己学开发**:优先 `~/.bashrc`
– **整台开发机所有人都要用**:用 `/etc/profile.d/*.sh`
– **只想对当前工程生效**:用 `source env.sh` 或 `VAR=… make`
—
## 8. 对交叉编译器来说,最推荐怎么做
如果是 Linux 内核 / 驱动学习环境,建议按下面优先级:
### 推荐 1:项目级命令传参,最稳
“`bash
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage
“`
如果 PATH 没配,也可以:
“`bash
make ARCH=arm CROSS_COMPILE=/opt/gcc-arm/bin/arm-linux-gnueabihf- zImage
“`
**优点:**
– 不影响系统其他工程
– 不容易误用错编译器
– 适合多个平台并存
### 推荐 2:单独 env 脚本
“`bash
source ~/env/imx6ull.sh
make zImage
“`
内容:
“`bash
export PATH=/opt/gcc-arm/bin:$PATH
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
“`
**优点:**
– 切换开发板方便
– 管理多套工具链清晰
### 推荐 3:只给自己长期使用,放 ~/.bashrc
如果长期只做这一套 ARM 开发,可以放到 `~/.bashrc`。
### 不太推荐:直接改 /etc/profile
除非:
– 这台机器就是专门做该平台开发
– 所有人都用这套工具链
– 你确定不会和其他版本冲突
否则全局改 PATH 很容易埋坑。
—
## 9. 常见坑
### 9.1 PATH 写反了
通常写:
“`bash
export PATH=/opt/gcc-arm/bin:$PATH
“`
放前面表示优先使用你的工具链。
如果写成:
“`bash
export PATH=$PATH:/opt/gcc-arm/bin
“`
系统可能先找到别的同名工具。
### 9.2 CROSS_COMPILE 少了末尾的 –
正确:
“`bash
export CROSS_COMPILE=arm-linux-gnueabihf-
“`
不是:
“`bash
export CROSS_COMPILE=arm-linux-gnueabihf
“`
因为 Makefile 最终会拼出:
“`bash
$(CROSS_COMPILE)gcc
“`
少了 `-` 就变成:
“`bash
arm-linux-gnueabihfgcc
“`
错了。
### 9.3 改了配置没生效
修改完以后要么:
“`bash
source ~/.bashrc
“`
或者:
“`bash
source /etc/profile
“`
要么重新登录/重开终端。
### 9.4 sudo 下环境变量失效
有些环境变量用 `sudo` 执行时会被清理。
所以编译一般不要动不动用 `sudo`。内核/驱动编译通常普通用户就够了。
### 9.5 把所有平台都塞进全局 PATH
比如同时装:
– `arm-linux-gnueabihf`
– `aarch64-linux-gnu`
– `arm-none-eabi`
全局 PATH 一股脑全加,后面很容易混乱。
更好的做法是:
– 用项目脚本
– 或命令行指定 `CROSS_COMPILE`
—
## 10. 一句话总结
> **环境变量就是“告诉 shell 和程序应该怎么运行的一组配置”。**
对交叉编译器来说,最关键的就是:
– `PATH`:去哪里找编译器
– `ARCH`:编译哪个架构
– `CROSS_COMPILE`:用哪套交叉工具前缀
而修改环境变量的方法,本质区别在于:
1. **作用范围**:当前命令 / 当前 shell / 当前用户 / 全系统
2. **是否持久**:临时 / 永久
3. **在哪种场景生效**:登录 shell / 交互 shell / 服务进程
4. **维护风险**:越全局,影响越大
—
## 11. 针对嵌入式 Linux 学习的建议
对于内核、设备树、驱动开发,推荐优先使用以下两种方式:
1. **命令行显式指定**
“`bash
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
“`
2. **项目专用环境脚本**
“`bash
source ~/env/imx6ull.sh
“`
这样比直接修改 `/etc/profile` 更安全,也更适合多平台开发。