首页 > 代码库 > Linux 内核开发—内核简单介绍
Linux 内核开发—内核简单介绍
内核简单介绍
Linux 构成
Linux 为什么被划分为系统空间和内核空间
隔离核心程序和应用程序,实现对核心程序和数据的保护。
什么内核空间,用户空间
内核空间和用户空间是程序执行的两种不同的状态,Linux对自身软件系统进行了划分,一部分核心的软件独立于普通的软件,拥有特权级别,可以訪问平台的全部硬件和资源,称为“内核空间”。而普通的软件执行在“用户空间”,它仅仅拥有有限的系统资源,不能直接訪问内核空间和硬件资源。
将系统分为“内核空间”和“系统空间”能提高系统的安全性,可以避免一些恶意程序的窥探,也能避免一些低劣的普通程序影响核心系统的执行,可以有效的保证系统的稳定性。
内核构成
SCI:
为用户空间提供了一套统一的系统调用函数来訪问Linux内核,是用户空间到内核空间的桥梁。
Glibc:
是GNU公布的libc库,也是C执行库,是Linux中最底层的API,差点儿全部的app都依赖于Glibc,它除了封装Linux系统提供的系统函数以外,其自身也提供了非常多其它的功能的函数库,基本的库函数例如以下:
(1)string库,字符串处理库函数
(2)signal库,提供信号处理库函数
(3)dlfcn库,管理共享库的动态载入函数库
(4)directory库,文件文件夹操作函数
(5)elf库,共享库的动态载入器,也即interpreter
(6)iconv,不同字符集的编码转换
(7)sockets,socket接口库
(8)Date and Time,日期和时间
(9)input/output,输入输出流
(10)linux threads库,LINUX线程库函数
(11)locale库,本地化和国际化的接口库
(12)Character库,字符处理
(13)Memory库,动态内存的分配与管理
(14)Processes and job control库,进程和进程控制库
(15)stdlib库,其他基本功能
内存管理:
控制多个进程安全的共享有限的内存区域。
进程管理:
进程管理的主要任务包含进程的创建、停止、进程间通信,还包含不同的进程共享CPU。
VFS:
虚拟文件系统隐藏了不同文件系统的细节,为文件操作提供了统一的接口。
网络协议栈:
提供丰富的网络协议实现。
设备驱动:
内核中大量的源代码都是在驱动中实现的,它们控制着特定的硬件设备。
ARM 有哪些工作模式
用户模式(user)
高速中断模式(frq)
外部中断模式(irq)
管理模式(svc)
数据訪问终止(abt)
系统模式(sys)
为定义指令异常(und)
不同模式下有不同权限,如寄存器权限
内核空间和用户空间怎样转换
硬件中断和系统调用能够实现用户空间跳转到内核空间执行
Linux内核源码
内核源码下载地址:www.kernel.org
内核文件夹结构
Arch(Architecture):内核支持的每一种CPU体系,在该文件夹下都有一个子文件夹,每一个不同的CPU子文件夹又都分为boot(系统引导)、mm(内存管理)、kernel(内核特性相关实现)等子文件夹。
|--x86 /*英特尔CPU及与其体系结构兼容的子文件夹*/
||--boot /*引导程序*/
|||--compressed /*内核解压缩*/
||--tools /*生成压缩内核印象的程序*/
||--kernel /*内核特性相关的实现,如信号处理、时钟处理*/
||--lib /*硬件相关工具函数*/
文件夹结构
Linux source code
├─ boot 系统引导汇编程序
├─ fs 文件系统
│ ├─ devpts /*/dev/pts虚拟文件系统*/
│ ├─ ext2 第二扩展文件系统
│ ├─ fat MS 的fat32文件系统
├─ block 部分块设备驱动程序
├─ crypto 加密、压缩、CRC校验算法
├─ 内核的文档
├─ drivers 设备驱动程序
├─ include 头文件(*.h) 内核所需的头文件,与平台相关的头文件放在对应的字文件夹下
│ ├─ asm 与CPU 体系结构相关的部分
│ ├─ linux Linux 内核专用部分,与平台无关的头文件
│ └─ sys 系统数据结构部分
├─ init 内核初始化程序
├─ IPC 进程间通信的实现代码
├─ samples 一些内核编程的范例
├─ scripts 配置内核的脚本
├─ sound 音频设备的驱动程序
├─ usr cpio 命令实现
├─ virt 内核虚拟机
├─ kernel 内核进程调度、信号处理、系统调用等程序
│ ├─ blk_drv 块设备驱动程序
│ ├─ chr_drv 字符设备驱动程序
│ └─ math 数学协处理器仿真处理程序
├─ lib 内核库函数
├─ mm 内存管理程序
└─ tools 生成内核Image 文件的工具程序
Linux内核配置与编译
内核配置相关命令
1)清除类命令make clean :清除kernel文件夹下大部分编译生成的文件。
make mrproper :清除全部编译生成的文件+配置文件(config等相关文件)。
make distclean :清除全部backup 文件 + patch 文件 + make mrproper
2)配置命令
make config :基于纯文本模式的交互式配置模式。
make menuconfig :基于文本模式的菜单性配置模式(推荐使用)。
make oldconfig :与 make config 类似,使用已有的配置文件进行配置,对于新增的菜单会进行提示。
make xconfig :基于图形化界面的配置方式,需呀安装图形化工具。
make menuconfig 是最经常使用的内核配置命令,能够使用方向键上/下移动,“Enter”进入文件夹,每一个配置选项能够选择“y”、“m”、“n”,也能够使用空格在这三个选项中进行切换。而且能够使用“h”显示这个选项的说明。
“y” :表示会被编译进内核的镜像文件。
“m” :表示会被编译成内核模块,不会放在内核的镜像文件的。
“n” :(空白) 表示不会对此模块进行编译,更不会放在内核镜像中。
内核配置选项说明
一般在开发的时候不会从无到有又一次配置全部的选项,而是在一个已有的配置文件基础之上依据实际的需求进行改动得到新的配置文件。Linux提供了一系列的配置文件模板,放在:Arch/$(cpu)/configs内核镜像文件
对于Linux内核,编译能够生成不同的镜像文件
- zImage :
- bzImage :
- uImage :
- vmlinux:
- vmlinuz:
对于更直观的联系请參考下图:
内核的编译
1) 编译内核镜像编译内核命令:
- make zImage
- make bzImage
- make uImage
- make zImage V=1
- make bzImage V=1
编译好的内核会放在arch/$(cpu)/boot/文件夹下
2) 编译内核模块
- make modules
make modules_install
4) 查看内核版本号
- cat /proc/version
- uname –a
- uname -r
虚拟内存盘(Ramdisk/Ramfs)
Ramdisk 是将一部分内存(RAM)虚拟成硬盘来使用的技术,相对于直接的硬盘訪问来说,ramdisk的訪问速度要高非常多,只是因为RAM的易失性,断电后ramdisk上的内容不会被保存。Linux 上的Ramdisk主要是指linux rootfs,它包括了除内核以外全部的linux系统在引导和管理师须要的工具(一般会使用busybox来完毕ramdisk的创建)
根文件系统
1)根文件系统概念
根文件系统是其它文件系统的根,它包括了系统引导和挂载其它文件系统的必要文件。根文件系统包括了linux启动时必要的文件夹和关键性文件,所以它必须是第一个载入的文件系统。内核的安装
1)怎样载入执行内核镜像
内核模块
什么是内核模块
内核模块就是为内核或者其它内核模块提供功能的代码块,内核模块具有举例的功能,能独立的编译,可是须要在内核环境下才干执行。它在执行时被链接进内核作为内核的一部分在内核空间执行。内核模块的存在提高了单内核可可扩展性差、可维护性差的缺陷。
怎样使用内核模块
方法一:
将全部的内核组件都编译进内核,生成zImage或者bzImage文件。
缺陷:内核文件过大、新增/删除内核组件的时候须要又一次编译源代码
方法二:
zImage或者bzImage文件并不包括不论什么组件,仅仅是在须要用到某个组件的时候,动态的将内核组件加入到内核中去。
内核模块的特点
1)模块本身并不被编译进内核文件
2)可依据需求,在内核执行期间,动态的加入或者卸载
内核模块缺陷
添加了管理内核模块的开销
内核版本号与模块版本号不兼容,会导致系统崩溃
添加内存、中断、符号表等资源消耗
内核模块的编译
Makefile
ifneq ($(KERNELRELEASE),) #“hello”是模块的名称,仅仅有“hello”可变。 obj-m := hello.o hello-objs := file1.o file2.o else #内核源码的位置 KDIR := ~/kernel/ all: make -C $(KDIR) M=$(PWD) modules clean: rm -f *.ko *.o *.mod.c *.mod.o *.symvers endif
内核模块的安装与卸载
命令:
1) insmod 载入内核模块
2) rmmod 卸载内核模块
3)lsmod 查看已经存在的内核模块
4)modprobe 载入内核模块
modprobe 与 insmod的差别在于,modprobe能处理内核模块之间的依赖关系,它可以依据/lib/modules/version/modules.dep 文件所描写叙述的依赖关系来载入此模块所需的其它内核模块。
内核模块与应用程序的区别
1) 应用程序在运行完毕后会从内存中消失
2) 内核模块会在运行该模块前将其注冊到内核中去,当完毕初始化函数后仍然处于内核中,直到被卸载
内核模块相关宏
1) MODULE_LICENSE("GPL");
2) MODULE_AUTHOR("Jack Chen");
3) MODULE_DESCRIPTION("Hello World");
4) MODULE_ALIAS("A simple module");
5) MODULE_VERSION("V1.0");
带參数的内核模块
能够使用宏module_param(name,type,perm)声明输入參数,当中:
1) name 表示參数的名称
2) type 表示參数的类型,常见的类型有int(整形),charp(字符串类型)
3) perm 訪问权限
a) S_IRUGO 不论什么用户都对/sys/module中出现的该參数具有读权限
b) S_IWUSR 同意root用户改动/sys/module 中出现的该參数
#include<linux/init.h> #include<linux/module.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jack Chen"); MODULE_DESCRIPTION("Hello World"); MODULE_ALIAS("A simple module"); MODULE_VERSION("V1.0"); static int age = 10; static char* name = "Jack chen"; module_param(age,int,S_IRUGO); module_param(name,charp,S_IRUGO); static int hello_init(void) { printk(KERN_EMERG" age:%d\n",age); printk(KERN_EMERG" name:%s\n",name); return 0; } static void hello_exit(void) { printk("<3>Goodbye,world !\n"); } module_init(hello_init); module_exit(hello_exit);
PS:使用insmod命令时的语法为 insmod hello.ko age=27 name=”David”
内核模块的导出
用于向其它内核模块提供功能函数,否则其它内核模块无法使用模块内的功能函数,当中/proc/kallsyms记录了内核中全部导出符号的名字和地址。
1) EXPORT_SYMBOL(符号名)
2) EXPORT_SYMBOL_GPL(符号名) 适用于包括了GPL许可证的的内核模块
使用EXPORT_SYMBOL声明后的符号将加入在/proc/kallsyms 文件里,能够使用cat/proc/kallsyms | grep “符号名” 查询
[calculate.c]
#include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("GPL"); int add_integar(int a,int b) { return a+b; } int sub_integar(int a,int b) { return a-b; } static int __init sym_init() { return 0; } static void __exit sym_exit() { } module_init(sym_init); module_exit(sym_exit); EXPORT_SYMBOL(add_integar); EXPORT_SYMBOL(sub_integar);
[hello.c]
#include <linux/module.h> #include <linux/init.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jack Chen"); MODULE_DESCRIPTION("Hello World"); MODULE_ALIAS("A simple module"); MODULE_VERSION("V1.0"); extern int add_integar(int a,int b); extern int sub_integar(int a,int b); static int hello_init() { int res = add_integar(1,2); printk("<3> res:%d\n",res); return 0; } static void hello_exit() { int res = sub_integar(2,1); printk("<3> res:%d\n",res); } module_init(hello_init); module_exit(hello_exit);
printk 的优先级
1) printk的日志级别定义例如以下(在linux26/includelinux/kernel.h中):
2) #defineKERN_EMERG"<0>"/*紧急事件消息,系统崩溃之前提示,表示系统不可用*/
3) #defineKERN_ALERT"<1>"/*报告消息,表示必须马上採取措施*/
4) #defineKERN_CRIT"<2>"/*临界条件,通常涉及严重的硬件或软件操作失败*/
5) #defineKERN_ERR"<3>"/*错误条件,驱动程序经常使用KERN_ERR来报告硬件的错误*/
6) #defineKERN_WARNING"<4>"/*警告条件,对可能出现故障的情况进行警告*/
7) #defineKERN_NOTICE"<5>"/*正常但又重要的条件,用于提醒。经常使用于与安全相关的消息*/
8) #defineKERN_INFO"<6>"/*提示信息,如驱动程序启动时,打印硬件信息*/
9) #defineKERN_DEBUG"<7>"/*调试级别的消息*/
Linux 内核开发—内核简单介绍