0
点赞
收藏
分享

微信扫一扫

《深入理解linux驱动程序设计》01

村里搬砖的月野兔 2022-01-13 阅读 50

《深入理解linux驱动程序设计》 第1章

1 linux内核组成和机制

此书基于linux内核3.8.13源代码。

1.1 linux内核版本与发展

1.1.1 linux 操作系统的诞生

在最开始的时候。MS-DOS和UNIX的使用都需要支付高额的费用,linus通过MINIX系统了解到了操作系统的原理,并开发了一款崭新的操作系统Linux,并通过分布式开发模式和全世界的爱好者们一起不断完善操作系统。最后linus使用GNU通用公共许可证(GPL)将linux重新授权,保证爱好者可以自由地复制,学习和修改源代码。

1.1.2 linux内核版本的变迁

linux内核版本一般划分为3个阶段,
第 1 阶段: 001.-1.0
第 2 阶段: 1.0-1.2.x
第 3 阶段: 1.2.x之后的版本

1.2 linux 内核编译

1.2.1 获取内核源码

获取内核源码的方式
1. 直接从linux内核官方网站([Linux 内核官方网站](https://www.kernel.org/))下载完全源码的压缩包;
2. 获取增量补丁形式的压缩包;
3. 利用Git回去源码

官方网站中存在2种类型的压缩包gzip和bzip2.
gzip对应的后缀为.tar.gz,解压命令如下:

#tar -xvzf XXX.tar.gz

bzip2对应的后缀名为.tar.bz2,解压命令如下:

#tar -xvjf XXX.tar.bz2

补丁是Linux内核开发和管理中常用的形式,用户可以通过补丁发布对代码的修改,也可以下载补丁接受其他人的修改。
增量补丁的获取方式与源码大致相同,获取补丁之后,需要从内核源码树开始应用增量。

patch - pl <../patch-3.8.13

1.2.2 内核源码树

内核源码的目录结构
内核源码目录结构

1.2.3 编译内核

每个版本的内核都会为用户提供编译工具,但是在编译前需要用户进行相关的配置。
Linux提供了不同的工具来简化内核的配置。注意:在执行命令前务必切换到root用户。

1.基于文本命令行方式
	#make config
2. 基于ncurse库编制的图形界面方式
	#make menuconfig
3. 基于gtk_的图形界面方式
	#make gconfig

以上3种方式都可以完成对内核的配置,并且生成用于存储配置信息的.config文件,文件存放在内核源码的根目录下,可以直接进行修改,并且可以查找和修改选项。
以下命令回根据体系结构对内核进行配置

# mkae defconfig

完成修改内核配置以后,还需要验证和更新配置。

# make oldconfig

配置选项CONFIG_IKCONFIG_PROC把完整的压缩过的内核配置文件存放在/proc/config. gz下,如果想编译一个新的内核源码,而且不修改当前的内核配置,可以直接将该压缩文件的内容复制到新的内核源码文件下,操作命令为:

$zcat /proc/config. gz > . conf ig
$make oldconfig

以上操作完成以后,内核编译前的配置基本完成,在编译之前,还需要准备一些编译所需要的工具。包括gcc,make以及相关的库。

#apt- get install build - essential 1 ibncurse - dev kernel - package fakeroot initramfs- tools module- init- tools

下载了Linux-3.8. 13后,将其解压并存放在/usr/sre目录下,开始进行编译。

#make mrproper(保证旧文件不再使用)
#make menuconf ig
#make(编译)
#make install(安装内核)
#make modules_ install(安 装模块)

此外,内核还提供了一个并行的编译方式: make - jn,这里n是并行的作业数量。在实际操作中,通常一个处理器衍生出一个或者两个作业,并行的方式大大提升了编译的效率。
Linux内核编译部分基本完成,可以尝试完成内核的修改和编译了。

1.3 linux内核组成

Linux内核由进程调度(SCHED),进程间通信(IPC),内存管理(MM),网络接口(NET)以及虚拟文件系统(VFS)组成。这5部分彼此依赖。

  1. 进程调度
    Linux内核使用的较为简单的基于优先级的进程调度算法来选择进程。

  2. 进程间通信
    Linux内核支持多种进程间通信机制,包括信号量,管道以及共享内存等。这种机制可协助多进程多资源的互斥访问,进程间同步和消息传递。

  3. 内存管理
    内存管理实现的是控制多个进程安全地共享内存区域。内存管理实现的功能是控制多个进程安全地共享主内存区域。Linux 内存管理支持虚拟内存,即在计算机中运行的程序,其代码、数据、堆栈的总量可能超过实际内存大小,操作系统只把当前使用的程序块保留在内存中,其余程序块保留在磁盘中。当CPU提供内存管理单元时,Linux内存管理完成为每个进程进行虚拟内存到物理内存的转换。
    内存管理从逻辑.上分为硬件无关部分和硬件相关部分,硬件无关部分提供了进程的映射和逻辑内存的兑换;硬件相关部分为内存管理硬件提供虛拟接口。

  4. 网络接口
    网络接口提供了对各种网络标准和网络硬件的支持。网络接口分为2类:网络协议和网络设备驱动程序。网络协议负责实现每一种可能的网络传输协议,网络设备驱动程序负责与硬件设备通信,每一种可能的硬件设备都有对应的设备驱动程序。

  5. 虚拟文件系统
    虚拟文件系统(VFS)隐藏了各种硬件的具体细节,为所有的设备提供了统一的接口,VFS提供了数十种不同的文件系统。虚拟文件系统独立于各个具体的文件系统,是对各个文件系统的一个抽象,它使用超级块super block 存放文件系统的相关信息,使用索引节点inode存放文件的物理信息,使用目录项dentry存放文件的逻辑信息。

1.4 linux内核机制

1.4.1 内核启动过程

内核启动过程如图所示:
在这里插入图片描述

内核启动过程:
梳理这个流程

  1. 实模式的入口函数_ _start(): 在header.S中,这里会进人main函数,它复制bootloader的各个参数,执行基本硬件设置,解析命令行参数。

  2. 保护模式的入口函数startup 32():在compressed/header_ 32. S中,解压bzImage内核映像,加载vmlinux内核文件

  3. 内核入口函数startup. 32():在kernel/header. 32. S中,这就是所谓的进程0,它会进入体系结构无关的start_ kernel()函数, 即Linux内核启动函数。start_ kernel()会 做大量的内核初始化操作,解析内核启动的命令行参数,并启动一个内核线程来完成内核模块初始化的过程,然后进人空闲循环。

  4. 内核模块初始化的人口函数kernel_init();在init/main.c中,这里会启动内核模块、创建基于内存的rootfs、加载initramfs文件或cpio-initrd,并启动一个内核线程来运行其中的/init脚本,完成真正的根文件系统的挂载。

  5. 根文件系统挂载脚本/init:挂载根文件系统、运行/sbin/init,从而启动进程1。

  6. init 进程的系统初始化过程:执行相关脚本,以完成系统初始化,例如设置键盘、字体、装载模块、设置网络等,最后运行登录程序,出现登录界面。

1.4.2 模块机制

模块(动态可加载内核模块 Loadable Kernel Module)。模块是具有独立功能的程序,可单独编译,但不能独立运行。它是在运行时被链接到内核中,作为内核中的一部分在内核空间运行。模块通常由一组函数和数据结构组成,用来实现一种文件系统,一个驱动程序或其他内核上层的功能。
模块从创建到使用需要经过源代码、Makefile文件、编译模块、加载模块和卸载模块等步骤。

模块的加载:

手动加载: insmod或modprobe
自动加载:内核线程kmod,kmod通过调用modprobe来实现。

模块的卸载:

1.用户使用rmmod命令
2.通过kernel或kmod卸载。

以insmod分析模块的加载过程:
(1)insmod通过系统调用query_module函数遍历模块链表来获取系统中的所有符号及其在内存中的物理地址,然后利用得到的符号表修正模块中引用到的外部符号,此过程中记录改模块所要用到的模块;
模块内核空间的地址引用是正确的,所以直接使用符号在内存的物理地址进行更正,若有一些符号的地址未知,则不能加载此模块。

(2)insmod填写模块module_ref表, dep指向本模块使用的模块,即在修正本模块使用到的外部符号过程中标志过的模块,模块在内存的物理地址是通过系统调用query_module得到的。其他模块要使用本模块提供的服务,所以需要insmod完成本模块的输出符号表。

(3)insmod使用系统调用create_module函数,它为模块分析提供足够的内存空间,并初始化位于该空间起始处的struct module 结构体,insmod通过系统调用init_module完成后续工作。

举报

相关推荐

0 条评论