diff --git a/Chapter10_Linux_Driver/10.1_概述.md b/Chapter10_Linux_Driver/10.1_概述.md index 6d30898..bc79359 100644 --- a/Chapter10_Linux_Driver/10.1_概述.md +++ b/Chapter10_Linux_Driver/10.1_概述.md @@ -1 +1,105 @@ -# 概述 \ No newline at end of file +# 10。1 概述 + +## 嵌入式系统的分类 + +SOC:通常内存较大,使用外部磁盘,大容量 Flash 或 EMMC,有 MMU,区分用户态和内核态,运行 Linux/Android/WinCE 等大型系统,应用程序与系统可分开编译,系统可动态安装程序,一般使用远程 GDB 和日志输出方式进行调试。开发分为 BSP、驱动、中间件、应用/UI 等。比如 Intel Atom 系列、ARM Cortex A 系列。 + +MCU:通常内存和 Flash 较小,无 MMU,不区分用户态和内核态,无系统或运行小型实时系统,应用程序与系统一起编译形成镜像进行烧录,通常采用 JTAG 等硬件调试器进行调试。开发不分家或分为系统、驱动、应用开发。比如 ARM Cortex M 系列、Atmel AVR 系列、TI MSP430 系列。 + +## 认识 Linux 系统 + +Linux 可运行于多种平台,比如个人电脑、Power PC 以及 ARM Cortex A 系列 SOC 上。 + +Linux 分为内核态和用户态,内核态为受保护的代码,用户态程序执行异常不会导致系统漰溃,是因为用户态只能执行用户权限指令,特权指令只能在内核态执行 (这是由硬件设计决定的),且由于内核态的内存受到硬件保护,因此用户程序的溢出不会导致内核崩溃。内核态和用户态分离的目的就是为了保证系统的稳定,避免丰富的应用程序导致系统死机,而这种情况在 DOS 时期是很常见的。 + +由于驱动程序往往需要进行特权操作,比如直接操作物理内存,硬件寄存器,使用中断资源等,因此 Linux 系统的硬件驱动程序需要在内核态执行。Linux 的核心代码很小,在源代码中占大多数的为驱动程序或驱动程序框架。 + +为了适应不同类型设备的特征,Linux 内核驱动划分为多个子系统,比如:SPI 子系统、存储子系统、GPU 子系统等。每个子系统都为该类型设备而进行优化设计。同时,许多公司在子系统框架下还会按照自己的产品线构建下一级子系统。 + +## Linux 内核源代码目录结构 + +* arch:包含和硬件体系结构相关的代码,每种平台占一个相应的目录,如 i386、ARM、PowerPC、MIPS 等。 +* block:块设备驱动程序 I/O 调度。 +* Documentation:内核各部分的通用解释和注释。 +* drivers:设备驱动程序,每个不同的驱动占用一个子目录,如 char、block、net、mtd、i2c 等。 +* include:头文件,与系统相关的头文件被放置在 include/linux 子目录下。 +* kernel:内核的最核心部分,包括进程调度、定时器等,而和平台相关的一部分代码放在 arch/*/kernel 目录下。 +* lib:库文件代码。 +* net:网络相关代码,实现了各种常见的网络协议。 +* scripts:包含用于配置内核的脚本文件。 +* sound:ALSA、OSS 音频设备的驱动核心代码和常用设备驱动。 + +## Linux 内核驱动开发的特点 + +* 由于驱动运行在内核态,任何错误都有可能导致内核崩溃,因此软件自量的要求很高。 +* 开发时需要对驱动有所定位,需要清楚驱动在哪个子系统下,属于总线驱动还是设备驱动等。 +* 内核除为驱动划分了各个子系统外,还将驱动进行了分类,比如:字符设备,块设备,网络设备,杂项设备等。 +* 与用户态一样,内核中提供了很多同步异步,以及通讯机制,学习 Linux 内核驱动开发时应注意各 API 的学习。 +* Linux 的内核版本更新很快,不同版本间架构,API 等会有差异,应注意区分。 +* 内核驱动可以与内核代码编译成一体,称为 Build In,也可以编译成独立模块。 +* 学习时应注意区分驱动程序的入口,加载时机等。 +* 内核源码中会使用大量的高级 C 语言宏操作,有些代码比较绕,通常先知道其用途,再深入学习原理。 +* Linux 内核源码使用 Linux 代码风格进行开发,代码风格应共同遵守。 + +## 一个简单的 Linux 内核模块 + +```cpp +#include +#include +MODULE_LICENSE("Dual BSD/GPL"); + +static int hello_init(void) +{ + printk(KERN_ALERT "hello driver enter\n"); + return 0; +} + +static void hello_exit(void) +{ + printk(KERN_ALERT "hello driver exit\n "); +} + +module_init(hello_init); +module_exit(hello_exit); +MODULE_AUTHOR("rick.chan"); +MODULE_DESCRIPTION("a simple hello world module"); +MODULE_ALIAS("a simplest module"); +``` + +其由 C 程序的 include、函数、变量以及诸多 MODULE_* 宏构成,这个程序里没有用户态程序的标准入口 main() 函数。程序的入口由 module_init() 定义,出口由 module_exit() 定义。 + +Makefile 文件如下: + +```Makefile +obj-m:= \ + hellodrv.o + +hellodrv-objs:= \ + hello_drv.o + +EXTRA_CFLAGS += \ + -I$(PWD) + +all: + $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +clean: + $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean +``` + +该程序的编译和使用方式如下: + +```bash +make +indmod hellodrv.ko +lsmod +rmmod hellodrv +``` + +除可使用 indmod 命令加载模块外,还可以使用 modprobe 命令,该命令比 insmod 命令要强大,它在加载某模块时会同时加载该模块所依赖的其他模块。使用 modprobe 命令加载的模块若以: + +```bash +modprobe -r +``` + +的方式卸载,将同时卸载其依赖的模块。 diff --git a/Chapter10_Linux_Driver/10.2_内核开发环境的搭建.md b/Chapter10_Linux_Driver/10.2_内核开发环境的搭建.md new file mode 100644 index 0000000..baa008a --- /dev/null +++ b/Chapter10_Linux_Driver/10.2_内核开发环境的搭建.md @@ -0,0 +1 @@ +# 10.2 内核开发环境的搭建 diff --git a/Chapter10_Linux_Driver/10.3_内核的配置和编译.md b/Chapter10_Linux_Driver/10.3_内核的配置和编译.md new file mode 100644 index 0000000..6ffb7f3 --- /dev/null +++ b/Chapter10_Linux_Driver/10.3_内核的配置和编译.md @@ -0,0 +1 @@ +# 10.3 内核的配置和编译