关于我们
![]() ![]() |
嵌入式C语言自我修养——从芯片、编译器到操作系统 读者对象:嵌入式学习者、开发者,从事Linux下C语言开发工作的人员
这是一本专门为嵌入式读者打造的C语言进阶学习图书。本书的学习重点不再是C语言的基本语法,而是和嵌入式、C语言相关的一系列知识。作者以C语言为切入点,分别探讨了嵌入式开发所需要的诸多核心理论和技能,力图帮助读者从零搭建嵌入式开发所需要的完整知识体系和技能树。本书从底层CPU的制造流程和工作原理开始讲起,到计算机体系结构,C程序的反汇编分析,程序的编译、运行和重定位,程序运行时的堆栈内存动态变化,GNU C编译器的扩展语法,指针的灵活使用,C语言的面向对象编程思想,C语言的模块化编程思想,C语言的多任务编程思想,进程、线程和协程的概念,从底层到上层,从芯片、硬件到软件、框架,几乎涵盖了嵌入式开发的所有知识点。
王利涛,嵌入式工程师、培训讲师,多年嵌入式开发经验,包括芯片测试、BSP、驱动开发、USB子系统等。目前在开发《嵌入式工程师自我修养》系列在线视频教程,以及在个人博客(www.zhaixue.cc)分享更多的嵌入式、Linux、AIoT技术。
第1 章 工欲善其事,必先利其器 ·····································································.1
1.1 代码编辑工具:Vim ·············································································.2 1.1.1 安装Vim ·················································································.2 1.1.2 Vim 常用命令 ············································································.3 1.1.3 Vim 配置文件:vimrc ··································································.6 1.1.4 Vim 的按键映射 ·········································································.8 1.2 程序编译工具:make ···········································································.10 1.2.1 使用IDE 编译C 程序 ·································································.10 1.2.2 使用gcc 编译C 源程序 ·······························································.11 1.2.3 使用make 编译程序 ···································································.14 1.3 代码管理工具:Git ·············································································.16 1.3.1 什么是版本控制系统 ··································································.16 1.3.2 Git 的安装和配置 ······································································.18 1.3.3 Git 常用命令 ············································································.18 第2 章 计算机体系结构与CPU 工作原理 ·························································.21 2.1 一颗芯片是怎样诞生的 ········································································.22 2.1.1 从沙子到单晶硅 ········································································.22 2.1.2 PN 结的工作原理 ······································································.24 2.1.3 从PN 结到芯片电路 ···································································.26 2.1.4 芯片的封装 ··············································································.28 2.2 一颗CPU 是怎么设计出来的 ·································································.29 2.2.1 计算机理论基石:图灵机 ····························································.30 嵌入式C 语言自我修养——从芯片、编译器到操作系统 ? XIV ? 2.2.2 CPU 内部结构及工作原理 ····························································.31 2.2.3 CPU 设计流程 ··········································································.33 2.3 计算机体系结构 ·················································································.37 2.3.1 冯·诺依曼架构 ··········································································.38 2.3.2 哈弗架构 ················································································.38 2.3.3 混合架构 ················································································.38 2.4 CPU 性能提升:Cache 机制 ···································································.39 2.4.1 Cache 的工作原理 ······································································.39 2.4.2 一级Cache 和二级Cache ·····························································.41 2.4.3 为什么有些处理器没有Cache ·······················································.42 2.5 CPU 性能提升:流水线 ········································································.42 2.5.1 流水线工作原理 ········································································.43 2.5.2 超流水线技术 ···········································································.44 2.5.3 流水线冒险 ··············································································.47 2.5.4 分支预测 ················································································.49 2.5.5 乱序执行 ················································································.50 2.5.6 SIMD 和NEON ········································································.51 2.5.7 单发射和多发射 ········································································.52 2.6 多核CPU ·························································································.54 2.6.1 单核处理器的瓶颈 ·····································································.54 2.6.2 片上多核互连技术 ·····································································.55 2.6.3 big.LITTLE 结构 ·······································································.58 2.6.4 超线程技术 ··············································································.59 2.6.5 CPU 核数越多越好吗 ··································································.61 2.7 后摩尔时代:异构计算的崛起 ································································.62 2.7.1 什么是异构计算 ········································································.62 2.7.2 GPU ······················································································.63 2.7.3 DSP ·······················································································.64 2.7.4 FPGA ·····················································································.64 2.7.5 TPU ·······················································································.65 2.7.6 NPU ······················································································.67 2.7.7 后摩尔时代的XPU 们 ·································································.70 目 录 ? XV ? 2.8 总线与地址 ·······················································································.71 2.8.1 地址的本质 ··············································································.72 2.8.2 总线的概念 ··············································································.73 2.8.3 总线编址方式 ···········································································.74 2.9 指令集与微架构 ·················································································.74 2.9.1 什么是指令集 ···········································································.75 2.9.2 什么是微架构 ···········································································.76 2.9.3 指令助记符:汇编语言 ·······························································.79 第3 章 ARM 体系结构与汇编语言 ··································································.81 3.1 ARM 体系结构 ··················································································.81 3.2 ARM 汇编指令 ··················································································.84 3.2.1 存储访问指令 ···········································································.85 3.2.2 数据传送指令 ···········································································.87 3.2.3 算术逻辑运算指令 ·····································································.87 3.2.4 操作数:operand2 详解 ·······························································.88 3.2.5 比较指令 ················································································.88 3.2.6 条件执行指令 ···········································································.89 3.2.7 跳转指令 ················································································.90 3.3 ARM 寻址方式 ··················································································.91 3.3.1 寄存器寻址 ··············································································.91 3.3.2 立即数寻址 ··············································································.91 3.3.3 寄存器偏移寻址 ········································································.92 3.3.4 寄存器间接寻址 ········································································.92 3.3.5 基址寻址 ················································································.92 3.3.6 多寄存器寻址 ···········································································.92 3.3.7 相对寻址 ················································································.93 3.4 ARM 伪指令 ·····················································································.94 3.4.1 LDR 伪指令 ·············································································.94 3.4.2 ADR 伪指令 ·············································································.96 3.5 ARM 汇编程序设计·············································································.97 3.5.1 ARM 汇编程序格式 ···································································.97 嵌入式C 语言自我修养——从芯片、编译器到操作系统 ? XVI ? 3.5.2 符号与标号 ··············································································.98 3.5.3 伪操作 ···················································································.99 3.6 C 语言和汇编语言混合编程 ·································································.101 3.6.1 ATPCS 规则 ···········································································.101 3.6.2 在C 程序中内嵌汇编代码 ··························································.103 3.6.3 在汇编程序中调用C 程序 ··························································.104 3.7 GNU ARM 汇编语言 ·········································································.105 3.7.1 重新认识编译器 ······································································.105 3.7.2 GNU ARM 编译器的伪操作 ························································.107 3.7.3 GNU ARM 汇编语言中的标号 ·····················································.108 3.7.4 .section 伪操作 ········································································.109 3.7.5 基本数据格式 ·········································································.110 3.7.6 数据定义 ··············································································.110 3.7.7 汇编代码分析实战 ···································································.111 第4 章 程序的编译、链接、安装和运行 ·························································.114 4.1 从源程序到二进制文件 ······································································.115 4.2 预处理过程 ·····················································································.120 4.3 程序的编译 ·····················································································.123 4.3.1 从C 文件到汇编文件 ································································.124 4.3.2 汇编过程 ··············································································.127 4.3.3 符号表与重定位表 ···································································.129 4.4 链接过程························································································.132 4.4.1 分段组装 ··············································································.132 4.4.2 符号决议 ··············································································.136 4.4.3 重定位 ·················································································.140 4.5 程序的安装 ·····················································································.142 4.5.1 程序安装的本质 ······································································.142 4.5.2 在Linux 下制作软件安装包 ························································.143 4.5.3 使用apt-get 在线安装软件 ··························································.145 4.5.4 在Windows 下制作软件安装包 ····················································.147 目 录 ? XVII ? 4.6 程序的运行 ·····················································································.150 4.6.1 操作系统环境下的程序运行 ·······················································.150 4.6.2 裸机环境下的程序运行 ·····························································.153 4.6.3 程序入口main()函数分析 ···························································.154 4.6.4 BSS 段的小秘密 ······································································.157 4.7 链接静态库 ·····················································································.158 4.8 动态链接························································································.162 4.8.1 与地址无关的代码 ···································································.164 4.8.2 全局偏移表 ············································································.165 4.8.3 延迟绑定 ··············································································.166 4.8.4 共享库 ·················································································.168 4.9 插件的工作原理 ···············································································.169 4.10 Linux 内核模块运行机制 ···································································.171 4.11 Linux 内核编译和启动分析 ································································.174 4.12 U-boot 重定位分析 ··········································································.179 4.13 常用的binutils 工具集 ······································································.185 第5 章 内存堆栈管理 ·················································································.188 5.1 程序运行的“马甲”:进程 ·································································.189 5.2 Linux 环境下的内存管理 ····································································.190 5.3 栈的管理························································································.192 5.3.1 栈的初始化 ············································································.193 5.3.2 函数调用 ··············································································.195 5.3.3 参数传递 ··············································································.199 5.3.4 形参与实参 ············································································.201 5.3.5 栈与作用域 ············································································.206 5.3.6 栈溢出攻击原理 ······································································.208 5.4 堆内存管理 ·····················································································.210 5.4.1 裸机环境下的堆内存管理 ··························································.212 5.4.2 uC/OS 的堆内存管理 ································································.213 5.4.3 Linux 堆内存管理 ····································································.219 5.4.4 堆内存测试程序 ······································································.227 嵌入式C 语言自我修养——从芯片、编译器到操作系统 ? XVIII ? 5.4.5 实现自己的堆管理器 ································································.229 5.5 mmap 映射区域探秘 ··········································································.233 5.5.1 将文件映射到内存 ···································································.236 5.5.2 mmap 映射实现机制分析 ···························································.239 5.5.3 把设备映射到内存 ···································································.240 5.5.4 多进程共享动态库 ···································································.243 5.6 内存泄漏与防范 ···············································································.245 5.6.1 一个内存泄漏的例子 ································································.245 5.6.2 预防内存泄漏 ·········································································.246 5.6.3 内存泄漏检测:MTrace ·····························································.248 5.6.4 广义上的内存泄漏 ···································································.250 5.7 常见的内存错误及检测 ······································································.251 5.7.1 总有一个Bug,让你泪流满面 ·····················································.253 5.7.2 使用core dump 调试段错误 ························································.254 5.7.3 什么是内存踩踏 ······································································.256 5.7.4 内存踩踏监测:mprotect ····························································.257 5.7.5 内存检测神器:Valgrind ····························································.259 第6 章 GNU C 编译器扩展语法精讲 ·····························································.263 6.1 C 语言标准和编译器 ·········································································.264 6.1.1 什么是C 语言标准 ··································································.264 6.1.2 C 语言标准的内容 ···································································.265 6.1.3 C 语言标准的发展过程 ·····························································.265 6.1.4 编译器对C 语言标准的支持 ·······················································.268 6.1.5 编译器对C 语言标准的扩展 ·······················································.268 6.2 指定初始化 ·····················································································.269 6.2.1 指定初始化数组元素 ································································.269 6.2.2 指定初始化结构体成员 ·····························································.271 6.2.3 Linux 内核驱动注册 ·································································.271 6.2.4 指定初始化的好处 ···································································.273 6.3 宏构造“利器”:语句表达式 ······························································.273 6.3.1 表达式、语句和代码块 ·····························································.273 目 录 ? XIX ? 6.3.2 语句表达式 ············································································.274 6.3.3 在宏定义中使用语句表达式 ·······················································.276 6.3.4 内核中的语句表达式 ································································.280 6.4 typeof 与container_of 宏 ·····································································.280 6.4.1 typeof 关键字 ·········································································.280 6.4.2 typeof 使用示例 ······································································.281 6.4.3 Linux 内核中的container_of 宏 ····················································.281 6.4.4 container_of 宏实现分析 ····························································.283 6.5 零长度数组 ·····················································································.286 6.5.1 什么是零长度数组 ···································································.286 6.5.2 零长度数组使用示例 ································································.288 6.5.3 内核中的零长度数组 ································································.288 6.5.4 思考:指针与零长度数组 ··························································.290 6.6 属性声明:section·············································································.292 6.6.1 GNU C 编译器扩展关键字:__attribute__ ·······································.292 6.6.2 属性声明:section ···································································.294 6.6.3 U-boot 镜像自复制分析 ·····························································.297 6.7 属性声明:aligned ············································································.299 6.7.1 地址对齐:aligned ···································································.299 6.7.2 结构体的对齐 ·········································································.302 6.7.3 思考:编译器一定会按照aligned 指定的方式对齐吗 ·························.304 6.7.4 属性声明:packed ···································································.305 6.7.5 内核中的aligned、packed 声明 ····················································.306 6.8 属性声明:format ·············································································.306 6.8.1 变参函数的格式检查 ································································.306 6.8.2 变参函数的设计与实现 ·····························································.308 6.8.3 实现自己的日志打印函数 ··························································.312 6.9 属性声明:weak ···············································································.314 6.9.1 强符号和弱符号 ······································································.314 6.9.2 函数的强符号与弱符号 ·····························································.316 6.9.3 弱符号的用途 ·········································································.317 6.9.4 属性声明:alias ······································································.319 嵌入式C 语言自我修养——从芯片、编译器到操作系统 ? XX ? 6.10 内联函数 ······················································································.320 6.10.1 属性声明:noinline ·································································.320 6.10.2 什么是内联函数 ····································································.320 6.10.3 内联函数与宏 ·······································································.321 6.10.4 编译器对内联函数的处理 ·························································.322 6.10.5 思考:内联函数为什么定义在头文件中 ········································.324 6.11 内建函数 ······················································································.324 6.11.1 什么是内建函数 ····································································.324 6.11.2 常用的内建函数 ····································································.325 6.11.3 C 标准库的内建函数 ·······························································.328 6.11.4 内建函数:__builtin_constant_p(n) ···············································.328 6.11.5 内建函数:__builtin_expect(exp,c) ···············································.329 6.11.6 Linux 内核中的likely 和unlikely ················································.330 6.12 可变参数宏 ···················································································.333 6.12.1 什么是可变参数宏 ··································································.334 6.12.2 继续改进我们的宏 ··································································.335 6.12.3 可变参数宏的另一种写法 ·························································.336 6.12.4 内核中的可变参数宏 ·······························································.336 第7 章 数据存储与指针 ··············································································.339 7.1 数据类型与存储 ···············································································.340 7.1.1 大端模式与小端模式 ································································.340 7.1.2 有符号数和无符号数 ································································.343 7.1.3 数据溢出 ··············································································.345 7.1.4 数据类型转换 ·········································································.348 7.2 数据对齐························································································.352 7.2.1 为什么要数据对齐 ···································································.352 7.2.2 结构体对齐 ············································································.353 7.2.3 联合体对齐 ············································································.356 7.3 数据的可移植性 ···············································································.358 7.4 Linux 内核中的size_t 类型 ··································································.360 7.5 为什么很多人编程时喜欢用typedef ·······················································.361 目 录 ? XXI ? 7.5.1 typedef 的基本用法 ··································································.361 7.5.2 使用typedef 的优势 ··································································.364 7.5.3 使用typedef 需要注意的地方 ······················································.366 7.5.4 typedef 的作用域 ·····································································.368 7.5.5 如何避免typedef 被大量滥用 ······················································.368 7.6 枚举类型························································································.369 7.6.1 使用枚举的三种方法 ································································.370 7.6.2 枚举的本质 ············································································.370 7.6.3 Linux 内核中的枚举类型 ···························································.372 7.6.4 使用枚举需要注意的地方 ··························································.372 7.7 常量和变量 ·····················································································.373 7.7.1 变量的本质 ············································································.374 7.7.2 常量存储 ··············································································.376 7.7.3 常量折叠 ··············································································.377 7.8 从变量到指针 ··················································································.378 7.8.1 指针的本质 ············································································.379 7.8.2 一些复杂的指针声明 ································································.381 7.8.3 指针类型与运算 ······································································.383 7.9 指针与数组的“暧昧”关系 ·································································.386 7.9.1 下标运算符[] ··········································································.388 7.9.2 数组名的本质 ·········································································.389 7.9.3 指针数组与数组指针 ································································.392 7.10 指针与结构体 ················································································.395 7.11 二级指针 ······················································································.399 7.11.1 修改指针变量的值 ··································································.400 7.11.2 二维指针和指针数组 ·······························································.402 7.11.3 二级指针和二维数组 ·······························································.403 7.12 函数指针 ······················································································.407 7.13 重新认识void ················································································.408 第8 章 C 语言的面向对象编程思想 ·······························································.411 8.1 代码复用与分层思想 ·········································································.412 嵌入式C 语言自我修养——从芯片、编译器到操作系统 ? XXII ? 8.2 面向对象编程基础 ············································································.413 8.2.1 什么是OOP ···········································································.414 8.2.2 类的封装与实例化 ···································································.414 8.2.3 继承与多态 ············································································.416 8.2.4 虚函数与纯虚函数 ···································································.418 8.3 Linux 内核中的OOP 思想:封装 ··························································.419 8.3.1 类的C 语言模拟实现 ································································.420 8.3.2 链表的抽象与封装 ···································································.422 8.3.3 设备管理模型 ·········································································.423 8.3.4 总线设备模型 ·········································································.427 8.4 Linux 内核中的OOP 思想:继承 ··························································.429 8.4.1 继承与私有指针 ······································································.429 8.4.2 继承与抽象类 ·········································································.430 8.4.3 继承与接口 ············································································.431 8.5 Linux 内核中的OOP 思想:多态 ··························································.433 第9 章 C 语言的模块化编程思想 ··································································.436 9.1 模块的编译和链接 ············································································.437 9.2 系统模块划分 ··················································································.439 9.2.1 模块划分方法 ·········································································.439 9.2.2 面向对象编程的思维陷阱 ··························································.441 9.2.3 规划合理的目录结构 ································································.442 9.3 一个模块的封装 ···············································································.443 9.4 头文件深度剖析 ···············································································.444 9.4.1 基本概念 ··············································································.444 9.4.2 隐式声明 ··············································································.446 9.4.3 变量的声明与定义 ···································································.447 9.4.4 如何区分定义和声明 ································································.449 9.4.5 前向引用和前向声明 ································································.450 9.4.6 定义与声明的一致性 ································································.453 9.4.7 头文件路径 ············································································.454 9.4.8 Linux 内核中的头文件 ······························································.456 目 录 ? XXIII ? 9.4.9 头文件中的内联函数 ································································.459 9.5 模块设计原则 ··················································································.459 9.6 被误解的关键字:goto ·······································································.461 9.7 模块间通信 ·····················································································.462 9.7.1 全局变量 ··············································································.462 9.7.2 回调函数 ··············································································.466 9.7.3 异步通信 ··············································································.470 9.8 模块设计进阶 ··················································································.471 9.8.1 跨平台设计 ············································································.471 9.8.2 框架 ····················································································.473 9.9 AIoT 时代的模块化编程 ·····································································.476 第10 章 C 语言的多任务编程思想和操作系统入门 ···········································.478 10.1 多任务的裸机实现 ···········································································.479 10.1.1 多任务的模拟实现 ··································································.479 10.1.2 改变任务的执行频率 ·······························································.481 10.1.3 改变任务的执行时间 ·······························································.484 10.2 操作系统基本原理 ···········································································.488 10.2.1 调度器工作原理 ····································································.490 10.2.2 函数栈与进程栈 ····································································.492 10.2.3 可重入函数 ··········································································.493 10.2.4 临界区与临界资源 ··································································.495 10.3 中断 ····························································································.497 10.3.1 中断处理流程 ·······································································.497 10.3.2 进程栈与中断栈 ····································································.500 10.3.3 中断函数的实现 ····································································.504 10.4 系统调用 ······················································································.506 10.4.1 操作系统的API ·····································································.506 10.4.2 操作系统的权限管理 ·······························································.508 10.4.3 CPU 的特权模式 ····································································.508 10.4.4 Linux 系统调用接口 ································································.509 嵌入式C 语言自我修养——从芯片、编译器到操作系统 ? XXIV ? 10.5 揭开文件系统的神秘面纱 ··································································.510 10.5.1 什么是文件系统 ····································································.511 10.5.2 文件系统的挂载 ····································································.513 10.5.3 根文件系统 ··········································································.514 10.6 存储器接口与映射 ···········································································.515 10.6.1 存储器与接口 ·······································································.515 10.6.2 存储映射 ·············································································.518 10.6.3 嵌入式启动方式 ····································································.520 10.7 内存与外部设备 ·············································································.521 10.7.1 内存与外存 ··········································································.521 10.7.2 外部设备 ·············································································.522 10.7.3 I/O 端口与I/O 内存 ································································.523 10.8 寄存器操作 ···················································································.524 10.8.1 位运算应用 ··········································································.524 10.8.2 操作寄存器 ··········································································.527 10.8.3 位域 ···················································································.529 10.9 内存管理单元MMU ········································································.531 10.9.1 地址转换 ·············································································.532 10.9.2 权限管理 ·············································································.534 10.10 进程、线程和协程 ·········································································.535 10.10.1 进程 ·················································································.536 10.10.2 线程 ·················································································.538 10.10.3 线程池 ··············································································.539 10.10.4 协程 ·················································································.540 10.10.5 小结 ·················································································.541 参考文献 ···································································································.543
你还可能感兴趣
我要评论
|