文章目录
硬件环境
ARMv6-ARM1176jzfs
硬件资料
核心移植
驱动移植
- linux-5.11 ok6410a 增加 网卡 支持
- linux-5.11 ok6410a 增加 lcd 支持
- linux-5.11 ok6410a 增加 usb 支持
- linux-5.11 ok6410a 增加 usb camera支持 及 测试demo
rootfs及其他用户空间工具的制作
ARM-linux代码分析(这里有些是架构相关的,有些是架构无关的)
静态分析
Makefile Kconfig Kbuild 分析
简易开发环境搭建
汇编启动分析
start_kernel 启动分析
- start_kernel 运行时的关注点
1.打印
2.内存
3.硬件初始化(设备)
4.进程建立及调度
4.1 进程的创建(idle及其他)
4.2 多进程的调度
4.3 进程通信机制
4.4 线程同步机制
5.异常向量表角度
5.1 中断
5.2 其他异常
A.其他
1. 网络
2. 文件系统
以打印角度分析 启动
- 从打印角度简略分析第一阶段,共2阶段
- 第一阶段之setup_arch
- 第一阶段之mem
- 第一阶段之irq
- 第一阶段之console 待完善
- 第一阶段之vfs 待完善
- 从打印角度简略分析第二阶段,共2阶段 待完善
- 第二阶段之do_initcalls 待完善
- 第二阶段之initrd/initramfs技术 参考这个 TODO
以功能角度分析 启动
- 从功能角度简略分析总览,共三阶段 待完善
- 从功能角度简略分析第一阶段,共三阶段
- 从功能角度简略分析第二阶段,共三阶段
- 第二阶段之idle进程
- 从功能角度简略分析第三阶段,共三阶段
- 第三阶段之init进程
- 第三阶段之kthreadd进程
以进程角度分析 启动
一开始 是 裸机程序
然后将裸机程序 封装成 idle 进程
然后idle 就开始 fork 出 kernel_init 和 kthreadd
然后调度 开始,混乱的多进程调度开始了
接着 kthreadd fork出了 所有的内核进程
接着 用户进程init及其子进程 fork出了 所有的用户进程
计算机三大抽象之一
进程对CPU虚拟化 // 进程是对处理器 主存 和IO设备的抽象表示
考虑进程执行的时候(除去考虑竞态)一般不考虑调度
因为对于该进程来说,它执行的时候占用了整个cpu
- [调度的需求及历史及整体分析]
为什么需要调度
因为多进程的需求
为什么需要多进程
因为 只有一个进程,造成资源浪费
虽然有调度,可指令还是 one by one 执行的
宏观并行,微观串行
调度的实质
选中一个进程 (调度的核心,如何发展的?)
保存当前的进程/恢复选中的进程(架构强相关,因为涉及到寄存器的保存和恢复)
进程创建/调度的性能参数
???
系统有多少进程?进程是怎么被看见的?
如何遍历进程
以内存角度分析 启动
内存管理的历史
内存管理的性能
-
内存管理第三阶段及buddy的alloc和free . . .及 buddy的消费者
-
内存管理第四阶段及slab的alloc和free. . . . .及 slab的消费者
- 用户虚拟内存管理 VMA & [VMA与vmalloc的区别]
1.vmalloc ,一个内存块用vm_area 表示,挂载到了 vmap_area_root 和 vmap_area_list
vm_struct 结构体是其他模块可见的,vmap_area是动态映射区内部使用的
全局变量vmap_area_root是红黑树的根节点
全局变量vmap_area_list用于将vmap_area按地址从小到大排序。
2. mmap 一个内存块用vm_area_struct 表示,挂载到了 进程PCB中的mm_struct中的 mm_rb 和 mmap
- 基于VMA 的 sys_mmap 和 sys_brk
- glibc提供的mmap与malloc
- mmap实例 以fb为参考 从应用到驱动
- glibc提供的mmap的四种用途
- glibc提供的mmap四种用途参数及扩展
- glibc提供的mmap的四种用途之一 共享匿名映射 与 共享文件映射dev-zero
- glibc提供的mmap与linux中的 sys_mmap的联动
kmalloc
vmalloc
malloc
mmap
以文件系统FS角度分析 启动
以 无init_xxx技术 的启动流程为研究对象
start_kernel
vfs_caches_init_early
vfs_caches_init
mnt_init
init_rootfs //注册 rootfs 文件系统
init_mount_tree
kernel_init
prepare_namespace
mount_root //挂载真正的文件系统(在这里以root=/dev/mmcblk0p2中的rootfstype=ext3为例,该文件系统根目录有/linuxrc)
init_post
run_init_process(execute_command) //内核参数为init=/linuxrc
- 文件系统启动第一二阶段总览
- 第一阶段 start_kernel 阶段文件系统的初始化
- [第二阶段 kernel_init 阶段文件系统的初始化到挂载真正的文件系统]
- linuxrc的加载及运行-run_init_process
- linux 文件系统 1 2 3 4 导读
- 从流程去熟悉文件系统1-挂载
- [从流程去熟悉文件系统2-文件操作]
- [从流程去熟悉文件系统3-search&层级结构]
- [从流程去熟悉文件系统4-文件IO]
在文件IO时会用 file_operations 中的成员去访问 文件
所有磁盘文件系统中的 文件 都共用一个 file_operations
大部分内存文件系统的 文件 都共用一个 file_operations
/dev下的 文件系统中的 cdev 文件 每个文件有一个 file_operations
open流程中,对于 init_special_inode // 例如 /dev 下的 特殊文件系统的文件
const struct file_operations def_chr_fops = {
.open = chrdev_open,
SyS_open->do_sys_open->do_filp_open->path_openat->do_last->vfs_open->do_dentry_open->chrdev_open
chrdev_open
fops = fops_get(p->ops);
replace_fops(filp, fops);
filp->f_op->open(inode, filp);
- 文件系统的访问加速及一致性策略
- [文件系统访问加速1-dentry]
- [文件系统访问加速2-address_space]
当读写(write read,非mmap)时,我们将磁盘数据缓存到 内存的物理页(物理页由struct page描述)
address_space 是用来管理 这些 struct apge 的
address_space 直接挂在了 file结构体下,不用复杂管理
page 由 radix_tree 管理
- [文件的访问技术]
1. 直接读写磁盘 // 缺点是每次读写都要陷入内核空间,都要转动磁盘
2. 页缓存技术 // 缺点是每次读写都要陷入内核空间
3. 页缓存技术+fread/fwrite // 缺点是增加了数据在不同缓冲区复制的次数
4. mmap技术 // 优点 : 减少系统调用和内存复制的次数
通过fd 访问 内存(保存内核信息) 的方式 共分以下几种
1. open mmap
2. open write read close
3. userfaultfd
4. memfd_create和fd跨进程共享
通过fd 访问 文件(保存在硬盘上) 的方式 共分为以下几种
1. open mmap
2. open write read close
ARCH相关代码分析
arch/arm export 的函数与宏
arch/arm 页表
不同arch下的"boot到start_kernel"
文件系统
从文件系统到存储设备的数据流图解析
IO 的同步和异步
- [IO 同步/异步 阻塞/非阻塞 概念]
- [read & write 阻塞IO]
- [read & write 非阻塞IO]
- [select,pselect,poll,epoll 多路复用]
- [信号驱动式IO]
- [异步非阻塞 IO]
真实文件系统分析
磁盘文件系统比较好理解
网络文件系统暂时可以不用理解
但是内存文件系统类别比较多,各种内存文件系统都有,不知道为什么要分这么多类别
其中有 shmem tmpfs ramfs devtmpfs sysfs kernfs udevfs procfs debugfs devpts tracefs
cgroup pstore systemd-1 hugetlbfs mqueue sunrpc fusectl configfs nfsd binfmt_misc gvfsd-fuse
shmem tmpfs ramfs devtmfps 比较混乱
devtmpfs 的底层实现 用了 ramfs 和 shmem
tmpfs 的底层实现 用了 ramfs 和 shmem
系统启动过程中,挂载了
1. tmpfs
2. rootfs
3. bdev_cache_init
4. nsfs_init
5. devtmpfs
-
[文件系统性能]
异常
ARM异常分析
单核心的soc 的话,其实运行流程很单纯
包含
1. A进程代码
2. B进程代码
3. AB 系统调用(异常3)
4. AB 系统调用后进入内核的代码
5. 调度
6. 其他异常(异常1 2 4 5 6 7 8)
其他的就没了 // (由于会有异常及调度,所有会有竞态,所以需要同步)
多核心的soc,其实和这个一样
但是 多核心 相比 单核心,多了
1. 核间通信(应该归入异常范畴)
2. 核间同步
3. 多核boot
另外,会在 增加 调度方面的功能: cpu绑定,cpu间调度之类
- 异常1 00 reset 以 reset 按键为例
- 异常2 04 Undefined instructions
- 异常3 08 swi 即 系统调用发生时 的 流程,busybox->glibc->硬件->kernel->用户空间,以reboot为例
- 异常4 0c Prefetch Abort,未设置实例
- 异常5 10 Data Abort 以缺页异常为例
- [异常6 14 null
- 异常7 18 irq 即 中断发生时的流程,以vic为例
- 异常8 1c fiq
linux对ARM异常的处理总体分析
linux对ARM中断的处理总体分析
- 中断亲和性
- 中断抢占
- [异常之中断的处理方式-顶半部和底半部]
fiq 在linux中是不处理的
irq ,是由外设引起的,linux 会用 底半部来处理
每次中断后会一个domain,一个domain的来处理
每个domain内部 查询 status , 如果有bit置1,就处理.
直到处理完成后,再次查询一轮没有 bit置位1,handle_one_vic才返回0
所以如果一个中断线状态是 pending&active , 则会在一次中断发生时,都处理完该中断线的所有中断.
中断上下文有哪些代码
软中断上下文有哪些代码
中断处理函数中处理了 软中断,软中断处理时是开中断的, 界限一定要分清楚
同种softirq,tasklet,工作队列,因为有多个工作者线程,都可并发
但是 tasklet 做了处理,只能串行,不会并发
中断线程化,因为只有一个工作者线程,不会并发
- [底半部机制的区别
中断底半部执行延迟 对实时进程的影响 处理函数可花费时间 底半部并行
工作队列 慢 无影响 可以很多 并行
中断线程化 中 与实时进程共同竞争 可以较多 串行
软中断 快 比实时进程优先级高 尽量少 并行
tasklet 快 比实时进程优先级高 尽量少 串行
- [底半部实现方式1-工作队列
- 底半部实现方式2-softirq
软中断 就是 软件模拟 的中断,谈起中断
有几个方面是 必不可少的
硬件 和 软件 分别 该 如何 模拟?
中断有几根线 // 10
中断线的状态(pending)
中断什么时候处理,如何返回
- [底半部实现方式3-tasklet
- [底半部实现方式4-中断线程化
以前用work来线程化的处理内核,一个worker线程只能由一个CPU执行,多个中断的work都由同一个worker线程来处理,在单CPU系统中也只能忍着了。但是在SMP系统中,明明有那么多CPU空着,你偏偏让多个中断挤在这个CPU上?
新技术threaded irq,为每一个中断都创建一个内核线程;多个中断的内核线程可以分配到多个CPU上执行,这提高了效率。
------------------------------
实时进程运行,突然来了中断,中断后执行软中断,实时进程的实时性不可控
------------------------------
中断线程化(减少对软中断的使用),会优化对 实时进程 的处理
中断线程化(减少对workqueue的使用),会增加对底半部的实时处理
中断线程化 是 介于 workqueue 和 软中断 两种底半部技术的 一种技术
异常之后,会返回吗?
应用程序会被杀掉吗?内核会崩溃吗?
gic 还会一直中断吗?
linux 异常分析
除0 : SIGFPE
未对齐的数据访问 :
空指针的读写、写rodata section : SIGSEGV
prefetch abort
同步与通信
竞态与(内核)同步
- 并发 竞态 与 同步 总览
- [各种同步机制的关系及异同及使用场景]
- per-cpu的实现
- atomic bit int 64位 的实现
- 禁中断/中断屏蔽的实现
- 禁抢占的实现
- 禁软中断的实现
- 自旋锁的实现
- 读写锁的实现
- 顺序锁的实现
- 信号量(count=1)的实现
- 读写信号量的实现
- 互斥锁的实现
- [RCU的实现]
- [大内核锁BLK的实现(被遗弃了)
事件的同步
- "事件的同步"与"内核同步"的关系
- [sleep]
- 信号量(count初始化为0)
- [等待队列]
- [completion]
进程通信分析
linux 提供几种通信方式
1. 信号
2. 管道
3. 有名管道
4. 套接字
5. 信号量
6. 消息队列
7. 共享内存
A.dbus // dbus 基于 套接字
B.signalfd timerfd eventfd // 基于 anon inode fs
其中 信号量 消息队列 共享内存 posix 提供一套 , XSI(systemv) 提供一套
共享内存的实现 有很多种
1.posix mmap && open mmap
2.XSI(systemv)
3.memfd_create和fd跨进程共享
4.dma-buf
- 进程通信之 信号
- 进程通信之管道pipe
- 进程通信之有名管道FIFO
- [进程通信之套接字]
- 进程通信之 posix共享内存
- [进程通信之 posix消息队列]
- [进程通信之 podix信号量]
linux内核调试&性能调试
1. printk
2. syslog
3. console
4. syslogd
5. klogd
6. kmesg
7. 动态打印
8. dump_stack
---
9. 内核参数& CONFIG
---
10. procfs
11. sysctl
12. sysfs
13. debugfs
14. top/htop free
---
15. gdb-with-jtag
16. kdb
17. kgdb
10.kgtp
---
10.BUG&BUG_ON
11.oops&panic
10.kdump(based on kexec)&crash & kcore
10.SysRq magic key
---
11.kprobe & systemtap
12.ftrace(trace-cmd)
13.perf & eBPF
14.LTTng
15.分析工具:火焰图 https://github.com/brendangregg/FlameGraph
- 1. printk
- 3. 动态打印
- 9. procfs
- 11. sysfs
- 12. debugfs
- 6. gdb-with-jtag
- 7. kdb
- 8. kgdb
- 10.BUG&BUG_ON
- 11.oops&panic
- 12.ftrace(trace-cmd)
其他
linux中的栈
系统调用
文件格式
文件系统负责管理 文件,而不负责 解析文件里面的内容
arm-linux-gnueabi-gcc -E main.c -o main.i
arm-linux-gnueabi-gcc -S main.i -o main.s
arm-linux-gnueabi-gcc -c main.s -o main.o
arm-linux-gnueabi-gcc main.o -o main
arm-linux-gnueabi-objcopy -O srec main main.srec
arm-linux-gnueabi-objcopy -O binary main main.bin
arm-linux-gnueabi-objdump -D main > main.elf.asm
arm-linux-gnueabi-objdump -D -b binary -m arm main.bin > main.bin.asm
arm-linux-gnueabi-objdump -D -b srec -m arm main.srec > main.srec.asm
hexdump main > main.hex
LKM 的实现
- [LKM是什么]
- module elf命令文件的加载及执行
总线设备驱动模型及设备驱动及设备管理
总线设备驱动模型中的总线和实际总线(例如i2c的sda和sck) 是 不同的.
在 内核中,可以认为总线是 两个链表,一个用于挂驱动,一个用于挂设备
至于 设备挂到哪里
// 例如, 这个就挂到了 platform 上
dev.bus = &platform_bus_type;
device_add(&dev);
// 例如, 这个驱动就挂到了 platform上.
driver.bus = &platform_bus_type;
driver_register(&driver);
然后就需要 platform_bus_type中的 platform_match 来匹配驱动和设备
匹配成功就执行probe
总线 可以用 物理上不存在的东西命令,例如 platform总线,hid总线
总线 也可以用物理上存在的东西命令,例如 i2c总线,usb总线. // 但 内核中的usb总线和硬件中的usb总线并不相同.
SMP 启动
CPU配置 : cache
上下文切换
计算机系统体系基础 第三版 胡伟武
第 4 章 软硬件协同 /4. 2 六种常见的上下文切换场景
arm64的linux情况
arm32在linux应用上的问题
1.ATM32 32bit虚拟地址导致无法全映射,从而存在高端内存的问题
2.ARM32 TTBR0无法使用的问题
3.ARM32 页表无dirty和access位的问题
4.ARM32 异常处理入口复杂的问题
arm64都会解决
arm32 的硬件问题,导致了 linux 用一些复杂的架构相关代码来做了处理
而arm64对arm32做了升级
arm64-linux 在这几块的架构相关代码,显得简单了许多