20251125 - 韦东山Linux第三篇笔记【下】

韦东山Linux第三篇笔记 - 下

笔记一、Linux 系统的核心机制:用户空间 (User Space) 与 内核空间 (Kernel Space) 的分离

为什么 写驱动比写应用程序难得多

🔬 概念一:内核空间 vs. 用户空间

要理解驱动和应用的区别,首先要理解 Linux 操作系统是如何划分权力区域的:

空间 权力 (权限) 运行的代码类型 目的
内核空间 最高(特权模式),可以直接访问所有硬件寄存器和内存。 Linux 内核本身、驱动程序 管理和控制硬件资源,保证系统稳定。
用户空间 受限(非特权模式),无法直接访问硬件。 应用程序、Shell、库函数 (glibc) 运行用户程序,提供用户界面。

💻 概念二:Linux 应用 vs. Linux 驱动

您的 HelloWorld.c 示例,即在虚拟机Linux环境下上写个HelloWorld.c,然后通过交叉编译输出ARM可运行文件HelloWorld,再让开发板运行,打印HelloWorld,就是典型的 Linux 应用

1. Linux 应用 (User Space Application)
  • 定位: 运行在用户空间。
  • 功能: 实现业务逻辑。例如,计算、网络连接、显示 LVGL 界面、读取文件等。
  • 如何与硬件通信?
    • 您的 HelloWorldprintf 函数最终会调用内核的 系统调用 (System Call),请求内核来帮忙完成屏幕输出。
    • 应用永远不直接操作硬件。 比如,您想点亮 LED,应用会打开 /dev/led 文件,然后调用 write(),由内核将请求转发给驱动程序。
  • 依赖: 依赖于标准的 C 库(如 glibc)和操作系统的稳定接口。
2. Linux 驱动 (Kernel Space Driver)
  • 定位: 运行在内核空间。
  • 功能: 直接控制硬件。 驱动程序是硬件和应用程序之间的翻译官。它接收来自应用的请求(系统调用),然后操作 I.MX6ULL 芯片的寄存器 来完成实际的物理动作(比如拉高 GPIO 电平)。
  • 如何与应用通信?
    • 它向用户空间提供文件接口 (例如 /dev/xxx),供应用程序调用。
  • 依赖: 它依赖于整个 Linux 内核的内部结构和API。

🧩 概念三:为什么编译驱动程序那么复杂?

驱动程序和应用程序在编译环境上有着根本区别。

1. 驱动程序依赖内核(就像树根依赖土壤)

驱动程序是内核的一部分,它必须知道内核内部的各种数据结构、函数定义、宏定义、中断号等等。这些信息都存储在内核源码的 头文件 (.h) 中。

2. asm/ 文件链接是关键难点

您提到的 asm/ 目录,它代表 Assembly/Architecture Specific(汇编/架构相关)。

  • 原因: Linux 内核代码中有一些与硬件架构密切相关的定义(例如,CPU 如何处理内存、如何处理中断)。
  • 结构: 为了实现通用性,内核源码中有一个名为 include/asm 的目录,它是一个软链接 (Symbolic Link)
  • 动作: 当您配置和编译内核时,构建系统会根据您选择的 ARM 架构 ,将这个 asm 软链接指向 asm-arm 目录。
  • 结果: 如果您直接编译驱动,而没有先配置和编译内核,那么 asm 这个链接可能指向错误的位置,或者根本不存在。驱动程序在编译时找不到这些架构相关的定义,自然就会失败。

3. 为什么必须先编译内核?

Linux 内核源码里,有成千上万个 .c 文件。当驱动程序想操作 CPU 的底层功能(比如"关闭中断")时,它会引用一个头文件: #include <asm/irq.h>

但是,Linux 支持几十种 CPU(ARM, X86, MIPS...)。

  • ARM 的关中断指令在 arch/arm/include/asm/irq.h
  • X86 的关中断指令在 arch/x86/include/asm/irq.h

代码里的 <asm/irq.h> 到底指的是哪一个?

在你刚下载好的内核源码里,include/asm 这个路径可能根本不存在 ,或者是一个无效的空链接。这时候如果你直接去编译驱动,编译器找 <asm/irq.h> 找不到,直接报错。

当你配置好内核(告诉它"我是 ARM 架构"),并开始编译时,构建系统(Make)会自动做一个动作:

创建一个"快捷方式": 它会在内存或临时目录中,把 include/asm 强行指向 arch/arm/include/asm

结果:

  • 你的驱动程序再次引用 <asm/irq.h>
  • 编译器看到 asm,顺着快捷方式找到了 arch/arm/...
  • 编译器成功找到了 ARM 版本的代码。
  • 编译成功!

这就是为什么说"必须先编译内核,才能编译驱动"。

因为编译内核不仅仅是生成一个 zImage 文件,更重要的是:

  1. 生成配置: 运行 make menuconfig 会生成 .config 文件,定义了内核的结构。
  2. 生成头文件: 构建系统会根据 .config 文件,创建出所有正确的、指向特定 ARM 架构的 头文件路径链接结构

总结: 编译驱动程序,就像是为一栋复杂的房子(内核)定制一个特殊配件。您必须有那栋房子的全套图纸和配置信息(已配置编译的内核源码),才能让配件完美契合。而编译应用程序,则像是在房子的客厅里(用户空间)放一张沙发,不需要知道房子的结构细节。

名词解释
zImage (内核镜像)
  • 是什么: 这就是编译好的 Linux 内核可执行文件
  • 类比: 它就像 Windows 里的 C:\Windows\System32\ntoskrnl.exe,或者 STM32 编译出来的 .bin / .hex 文件。
  • 特点: z 代表 zipped(压缩的)。它是一个经过压缩的内核,开发板启动时会把它解压到内存里运行。
uImage (U-Boot 专用内核镜像)
  • 是什么: 它等于 zImage + 一个头部信息 (Header)
  • 作用: U-Boot(启动加载器)有时比较"笨",它不能直接识别原始的 zImage。所以我们需要用一个工具(mkimage)给 zImage 加个只有 64 字节的"帽子",告诉 U-Boot:"我是内核,加载地址是 xxx,长度是 xxx"。这个戴了帽子的文件就是 uImage
  • 关系: uImage = Header + zImage
make menuconfig (菜单配置)
  • 是什么: 这是一个图形化的配置界面(蓝底灰字)。
  • 作用: 它就像游戏的"设置"菜单。你可以在这里勾选或取消内核的功能。比如:"我要支持 USB 鼠标"、"我要支持 WiFi"、"我要开启调试模式"。
  • 结果: 当你配置完保存退出时,它会生成一个名为 .config 的文件。
. .config (配置单)
  • 是什么: 一个隐藏的文本文件,里面全是开关。
  • 内容示例: CONFIG_ARM=y (开启ARM支持), CONFIG_WIFI=n (关闭WiFi支持)。
  • 作用: 编译器(GCC)和构建工具(Make)会读取这个文件,决定哪些代码需要编译,哪些跳过。
相关推荐
XH-hui1 小时前
【打靶日记】VluNyx 之 Hat
linux·网络安全·vulnyx
RisunJan1 小时前
Linux命令-fping命令(网络诊断工具)
linux·网络
BD_Marathon1 小时前
【Zookeeper】Zookeeper内部的数据模型
linux·分布式·zookeeper
芯联智造1 小时前
【stm32简单外设篇】- 水银开关
c语言·stm32·单片机·嵌入式硬件
m0_598250001 小时前
S参数02-S参数中的纹波
笔记·嵌入式硬件
繁华似锦respect1 小时前
C++ 无锁队列(Lock-Free Queue)详细介绍
linux·开发语言·c++·windows·visual studio
qq_433192181 小时前
Linux ISCSI服务器配置
linux·服务器·数据库
Geek__19921 小时前
STM32F103开发板上移植Agile Modbus库的详细指南
stm32·嵌入式硬件·敏捷流程
沐欣工作室_lvyiyi1 小时前
基于单片机的用电器功率监测报警系统设计(论文+源码)
单片机·嵌入式硬件·功率监测