Linux内核分析

Linux内核基本概念

从技术上说 linux 是一个内核
“内核”指的是一个提供硬件抽象层、磁盘及文件系统控制、多任务等功能的系统软件。一个内核不是一套完整的操作系统。
通常我们使用的 linux 系统是一个集 linux 内核、工具集、各种库、桌面管理器、应用程序等一体的一个发布包 (发行版)‏

Linux 内核的特性

  1. 免费开源
  2. 可以移植,支持的硬件平台广泛 arm, i386, m68k, m32r,m68knommu, mips, ppc, s390, sh, sparc
  3. 高可扩展性
  4. 可剪裁、可扩展,可以运行在大型主机,也可以运行在个人计算机上
  5. 高可靠性、稳定性 稳定性是linux鲜明特点,安装了linux系统的主机,连续运行一年不宕机是很平常的事情
  6. 超强的网络功能
  7. 真正的多任务,多用户系统
  8. 模块化设计 模块可以动态加载,卸载,可以减少系统体积,同时可以用来解决冲突问题,模块调试

Linux内核版本

目前linux系统采用 A.B.C.D 的版本号管理方式
A 表示linux的主版本号
B 表示linux的次版本号,B 为偶数表示稳定版本,奇数表示开发中的版本
C 表示linux的发行版本号
D 表示更新版本号
主版本(X.Y)
1.0 2.0 2.2 2.4 2.6 3.x

linux官网网址 https://www.kernel.org/

Linux内核子系统

linux采用时间片的形式进行内核调度,并不是真正意义上的实时系统。相对应的 VxWork系统是实时操作系统,采用抢占式内核调度和中断管理。

进程管理
内存管理
文件系统
网络协议
设备管理

编译内核及设备树流程(此处使用已移植好的fs4412)


注意要把 linux源码 拷贝到 Ubuntu中 解压, 因为 源码中会有一些软件链文件, 这些文windows文件系统 不能识别

tar -xvf ***.tar.xz

配置交叉编译工具链:

    1.export PATH=/自己的路径/gcc-4.6.4/bin:$PATH
    2. 写到启动脚本中 /etc/profile  或者 .bashrc
	
	修改 源码顶层目录的 Makfile
	ARCH ?= arm
	CROSS_COMPILE ?= arm-none-linux-gnueabi-

注意,编译工具与内核要配套使用。

编译内核

make uImage

编译设备树

make dtbs


设备树: 用于描述硬件设备信息。
低版本内核的设硬件备信息,是存放在linux源码中的
高版本是写到 设备树文件中

提醒: 如果想启动内核,还需要配置一下网络
网络配置学习视频
系统移植之网络配置 http://e.vhall.com/102020226

Linux内核模块结构图

Linux内核代码结构

☆	arch  不同CPU体系结构的差异代码
		-> arm  不同板子板子的源码 
			-> boot  uImage zImage 
			    -> dts 设备树
☆	driver 驱动目录
		-> net 网络驱动 (与设备相关)
	kernel 内核调度算法
	net	网络协议  (与设备无关的)
	block 块设备相关驱动
	fs  文件系统
	include 头文件存放目录
	Documentation 帮助文档
	tools 工具
	scripts 编译内核时使用的脚本
	ipc 进程间通信
	mm 内存管理

linux 内核启动分析

启动 u-boot -> kernel -> rootfs(root filesystem)
启动脚本 /etc/init.d/rcS  自启动应用
Starting kernel ... 属于 uboot的打印

重点理解 uboot 启动流程(面试出现频率高)

内核启动流程
a. 自解压 decompess (arch/arm/boot/compressed/head.S) 汇编
b. 检测合法性 入口 stext (arch/arm/kernel/head.S)
    (CPU 类型 机器类型)
c.  运行 C代码 start_kernel (init/main.c)
d. 挂在 rootfs
e. 运行第一个应用程序 linuxrc

嵌入式系统启动流程分析


与上面的对照分析着看 更易理解

linux 内核调试方法

  1. 汇编阶段 点灯法
  2. puts(内核解压前)
  3. printascii (console 初始化前)
  4. printk (内核解压后,信息输出显示到console初始化之后)
    打印级别
cat /proc/sys/kernel/printk            显示   4 4 1 7
echo "7 4 1 7" > /proc/sys/kernel/printk   
cat /proc/sys/kernel/printk            显示7 4 1 7


8个打印级别

#define KERN_EMERG "<0>"	/*紧急事件消息,系统崩溃之前提示,表示系统不可用*/
#define KERN_ALERT "<1>"	/*报告消息,表示必须立即采取措施*/
#define KERN_CRIT "<2>"		/*临界条件,通常涉及严重的硬件或软件操作失败*/
#define KERN_ERR "<3>"		/*错误条件,驱动程序常用KERN_ERR来报告硬件的错误*/
#define KERN_WARNING "<4>"	/*警告条件,对可能出现问题的情况进行警告*/
#define KERN_NOTICE "<5>"	/*正常但又重要的条件,用于提醒。常用于与安全相关的消息*/
#define KERN_INFO "<6>"		/*提示信息,如驱动程序启动时,打印硬件信息*/
#define KERN_DEBUG "<7>"	/*调试级别的消息*/

init/main.c - start_kernel




第一次编译内核代码会比较长时间
如果修改或者添加源文件,再次编译,只会编译变化的部分 会快一些

将新编译的uImage存入tftp文件夹,运行开发板。

测试printk成功。

  1. oop内核异常信息
pc : 程序计数器
arm-none-linux-gnueabi-addr2line pc地址 -e vmlinux -f
在源码中显示具体出错的位置

范例:

  1. 制造错误
    修改drivers/char/fs4412_led_drv.c
    在s5pv210_led_init函数中int  ret=0;下增加下面语句: int *ptr  =  NULL; *ptr  = 0xff;

  1. 编译内核,上传tftp。
linux-3.14-fs4412/drivers/char/Makefile 这里需要修改下,不然编译的时候不会编译到该文件。


  1. 启动开发板
[    1.165000] Unable to handle kernel NULL pointer dereference at virtual address 00000000
[    1.170000] pgd = c0004000
[    1.175000] [00000000] *pgd=00000000
[    1.175000] Internal error: Oops: 805 [#1] PREEMPT SMP ARM
[    1.180000] Modules linked in:
[    1.185000] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.14.0 #25
[    1.190000] task: ee8a0000 ti: ee8a4000 task.ti: ee8a4000
[    1.195000] PC is at s5pv210_led_init+0x18/0x180
[    1.200000] LR is at do_one_initcall+0x30/0x144
[    1.205000] pc : [<c024225c>]    lr : [<c00087b4>]    psr: 60000153
[    1.205000] sp : ee8a5ef8  ip : c059afac  fp : 00000000
[    1.215000] r10: c052d4fc  r9 : c0564b80  r8 : c0242244
[    1.220000] r7 : c05a3400  r6 : c055134c  r5 : 00000000  r4 : ee8a4000
[    1.230000] r3 : 00000055  r2 : c04c0430  r1 : 00000001  r0 : 1f400000
[    1.235000] Flags: nZCv  IRQs on  FIQs off  Mode SVC_32  ISA ARM  Segment kernel
[    1.245000] Control: 10c5387d  Table: 4000404a  DAC: 00000015
[    1.250000] Process swapper/0 (pid: 1, stack limit = 0xee8a4240)
[    1.255000] Stack: (0xee8a5ef8 to 0xee8a6000)

  1. 找出出错位置
根据PC is at s5pv210_led_init+0x18/0x180 知道出错的函数是s5pv210_led_init
根据pc : [<c024225c>]  知道出错的位置
#arm-none-linux-gnueabi-addr2line c024225c -e vmlinux -f   在源码中会显示具体出错的位置