linux内核结构和启动过程

(以下内容来自教学课件)

一、Linux内核结构

arch

与体系结构相关的代码。对应于每个支持的体系结构,有一个相应的子目录如x86、arm等与之对应,相应目录下有对应的芯片与之对应

drivers

设备驱动代码,占整个内核代码量的一半以上,里面的每个子目录对应一类驱动程序,如:char:字符设备、block:块设备、net:网络设备等

fs

文件系统代码,每个支持的文件系统有相应的子目录,如cramfs,yaffs,jffs2等

include

这里包括编译内核所需的大部分头文件

与平台无关的头文件include/linux

各类驱动或功能部件的头文件(/media、/mtd、/net等)

与体系相关的头文件arch/arm/include/

与平台相关的头文件arch/arm/mach-s5pv210/include/mach

lib

与体系结构无关的内核库代码

与体系结构相关的内核库代码在arch/arm/lib下

init

内核初始化代码,其中的main.c中的start_kernel函数是系统引导起来后运行的第1个函数,这是研究内核开始工作的起点

ipc

内核进程间通信代码

mm

与体系结构无关的内存管理代码

与处理器体系结构相关的代码在arch/arm/mm下

kernel

内核管理的核心代码

net

网络部分代码,其每个目录对应于网络的一个方面

scripts

存放一些脚本文件,如配置内核时用到的make menuconfig命令

Documentation

内核相关文档

crypto

常用加密及散列算法,和一些压缩及CRC校验算法

二、Linux编程风格

缩进

采用制表符(Tab)进行缩进,而不是空格

禁止制表符和空格混合使用

命名规范

Linux规定名称中不允许使用混合大小写字符

单词之间用下划线分隔

避免取有疑惑的简单名称,如pad(),应该写成platform_add_devices()

代码长度

每行尽量不超过80个字符(可进行有意义分行切割)

函数体代码长度尽量不超过两屏

函数局部变量尽量不超过十个

根据函数使用频率和函数体大小可以使用inline声明,以提高调用效率

注释

一般情况注释用于描述代码可以做什么和为什么要做,尽量不写实现方式

函数的修改和维护日志统一集中在文件开头

其他

指针中的"*"号应靠近变量名,而不是类型关键字

函数之间用空行隔开

函数导出申明紧跟在函数定义的下面

等等......

代码风格的事后修正

indent命令是大多数Linux系统中都带有的工具,当得到一段与内核编码风格大相径庭的代码时,可以通过这个工具进行调整:

#indent -kr -i8 -ts8 -sob -l80 -ss -bs -psl  <filename>

三、Linux内核启动引导过程

在了解Linux启动流程之前,首先需要了解Linux镜像的格式及其产生过程

Image:直接生成并未压缩的内核,一般用于PC机

zImage:Image的压缩版,采用gzip进行压缩,比Image体积小,但启动时需要进行自解压,嵌入式系

统中一般采用此种方法

uImage:是u-boot专用的一种内核镜像格式,它是在zImage的基础上又添加了一个长度为0x40的标签头,在u-boot启动时会去掉此头信息,仍按zImage启动,头信息主要用于区分不同格式的内核镜像

xipImage:片上执行的未压缩内核,(如norflash等)

(ps:实际应用中由于norflash比较贵,所以大多使用NAND flash作为存储器件,而NAND flash不支持片上执行,故xipImage用的比较少)

bootpImage:将内核与根文件系统制作在一起的镜像

zImage产生过程:vmlinux(内核代码生成的镜像)->Image(objcopy对vmlinux二进制化处理)->compressed/vmlinux(压缩的Image加上head.S和misc.c)->zImage(vmlinux二进制化)

Linux内核的启动过程大体上可以分为3个阶段:

1、内核解压(汇编+C)

主要由arch/arm/boot/compressed/对zImage完成解压,并调用call_kernel跳转到下阶段代码

2、板级引导阶段(汇编)

主要进行cpu和体系结构的检查、cpu本身的初始化以及页表的建立等

3、通用内核启动阶段(C语言)

进入init/main.c文件,从start_kernel开始进行内核初始化工作,最后调用rest_init

rest_init()会创建内核第一个线程,并进入线程函数kernel_init()

在kernel_init()中会初始化各种驱动并建立起标准输入/标准输出/错误输出,最后调用init_post()

在init_post()中会释放初始化内存段,标志着内核启动完成,并努力寻找一个用户进程,通过kernel_execve()函数加载,将该进程作为系统的第一个用户进程init,进程号为1,至此内核启动完成

四、内核的配置与编译

make menuconfig完成Linux内核配置裁剪

根据配置裁剪的结果配合Makefile完成内核编译

linux2.6以后的版本是通过每层目录的Kconfig和Makefile实现了整个Linux内核的分布式配置

Kconfig:对应内核模块的配置菜单

Makefile:对应内核模块的编译选项

当执行make menuconfig时,配置工具会自动根据根目录下的ARCH变量读取arch/$(ARCH)/Kconfig文件来生成配置界面(引用其它目录的kconfig,通过改变宏定义的方式进行条件编译),这个文件是所有文件的总入口,其它目录的Kconfig都由它来引用

配置界面的内容来自于所有目录的Kconfig, 选择"*", " ", 或"M"进行条件编译,对应宏定义在顶层.config文件中

在读取配置界面的同时,系统会读取顶层目录下的.config文件生成所有配置选项的默认值

3、当修改完配置并保存后,系统会更新顶层目录下的.config文件

4、当执行make时,各层的Makefile会根据.config文件中的编译选项来决定哪些文件会被编译到内核中,或编译成模块

Kconfig语法格式可以参考具体文件,如:drivers/char/Kconfig

menu "Character devices"

config VT

bool "virtual terminal" if EMBEDDED

depends on !s390

select INPUT

default y

--help--

if you say Y here,you will get support.....

config代表一个选项的开始,最终会出现在.config中(会自动增加一个CONFIG_前缀)

bool代表此选项仅能选中或不选中,bool后面的字符串代表此选项在make menuconfig中的名字

tristate:代表可以选择编译、不编译、编译成模块

string:字符串; hex:16进制的数; int:10进制的数

depends on:依赖其余的选项

default:默认选项值

select:表示当前config被选中时,此选项也被选中

menu/endmenu:表示生成一个菜单

choice/endchoice:表示选择性的菜单条目

comment:注释信息,菜单和.config文件中都会出现

source:用于包含其它Kconfig

将自己开发的内核代码加入到Linux内核中,需要有3个步骤:

1. 确定把自己开发代码放入到内核合适的位置

2. 把自己开发的功能增加到Linux内核的配置选项中,使用户能够选择此功能

3. 构建或修改Makefile,根据用户的选择,将相应的代码编译到最终生成的Linux内核中去

比如要把一个key1*5键盘驱动添加进内核

步骤如下:

Step1:将s5pv210-key15-simple.c拷到drivers/char/目录下

Step2: vi driver/char/Kconfig,在Kconfig文件结尾,在endmenu的前面加入一个config选项

config UNSP210_KEY15

bool "key1*5 driver for sunplusedu unsp210 boards"

default y

help

this is GPIO driver for unsp210 boards.

Step3:make menuconfig(添加配置选项)

Device driver->

character devices->

[*] key1*5 driver for sunplusedu unsp210 boards

Step4: vi driver/char/Makefile添加内容如下:

obj-$(CONFIG_UNSP210_KEY15) += s5pv210-key15-simple.o

Step5:make (更新内核镜像到开发板)

Step6:交叉编译测试程序,并放到开发板运行

#arm-linux-gcc key15_test.c -o key15_test

原文地址:http://blog.51cto.com/13603157/2118482

时间: 2024-10-12 01:33:10

linux内核结构和启动过程的相关文章

Linux系统开机和启动过程

提起操作系统这个词,想必大家并不陌生,有电脑端操作系统和手机端操作系统.电脑端操作系统较为熟悉的就是微软开发的windows操作系统,还有一种就是大家稍微陌生的linux操作系统,而手机端的操作系统分别为iOS操作系统,Android操作系统.而今天小编就给大家着重讲讲Linux系统开机和启动过程. 内核引导 当计算机打开电源后,首先是BIOS开机自检,按照BIOS中设置的启动设备(通常是硬盘)来启动. 操作系统接管硬件以后,首先读入 /boot 目录下的内核文件. 运行init init 进程

Linux内核,文件系统移植过程中出现的一些问题与解决办法

1.bootm地址和load address一样 此种情况下,bootm不会对uImage header后的zImage进行memory move的动作,而会直接go到entry point开始执行.因此此时的entry point必须设置为load address + 0x40.如果kernel boot过程没有到uncompressing the kernel,就可能是这里设置不对. boom address == load address == entry point - 0x40 2.

linux内核结构

Linux内核由5个主要的子系统组成 分别是:进程调度(SCHED).进程间通信(IPC).虚拟文件系统(VFS).内存管理(MM).网络通信(NET) 进程调度与内存管理之间的关系:这两个子系统互相依赖.在多道程序环境下,必须为程序创建进程,而创建进程的第一件事情就是将程序和数据装入内存. 进程间通信与内存管理的关系:进程间通信子系统要依赖内存管理支持”共享内存“通信机制,这种机制允许两个进程除了拥有自己的私有空间,还可以存取共同的内存区域. 虚拟文件系统与网络接口之间的关系:虚拟文件系统利用

通过gdb跟踪Linux内核装载和启动可执行程序过程

作者:吴乐 山东师范大学 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验目的:通过对一个简单的可执行程序用gdb进行代码的跟踪,剖析linux内核是如何动态和静态装载和启动程序的,进而总结linux内核可执行程序加载的过程. 一.实验过程 1.编写一个简单的Exec的创建进程的函数 2.打开gdb,并设置好如下断点 3.开始跟踪,找到第一个断点. (主程序还未创建子进程) 4.继续在此断点处逐步跟踪 5.

Linux的2种启动过程

Linux启动过程分为2种,init方式和systeamd方式. 先说init启动过程: 1.首先,启动电源. 2.启动POST(Power on self test),加电自检程序,检查主板硬件上有无短路等等,如果正常,会"滴"的响一声,这是很多人熟知的开机"滴"声.(上面那篇文章没写到.) 3.然后处理器会在系统存储中找到BIOS(Basic Input Output System). (注:BIOS一组固化到计算机内主板上一个ROM芯片上的程序,它保存着计算机

Linux(RHEL6)启动过程详解

Linux(红帽RHEL6)启动过程详解: RHEL的一个重要和强大的方面是它是开源的,并且系统的启动过程是用户可配置的.用户可以自由的配置启动过程的许多方面,包括可以指定启动时运行的程序.同样的,系统关机时所要终止的进程也是可以进行组织和配置的,即使这个过程的自定义很少被需要. 理解系统的启动和关机过程是如何实现的不仅可以允许自定义,而且也可以更容易的处理与系统的启动或者关机相关的故障.  1.启动过程  以下是启动过程的几个基本阶段:   ① 系统加载并允许boot loader.此过程的细

Linux内核结构体--kfifo 环状缓冲区

1.前言 最近项目中用到一个环形缓冲区(ring buffer),代码是由linux内核的kfifo改过来的.缓冲区在文件系统中经常用到,通过缓冲区缓解cpu读写内存和读写磁盘的速度.例如一个进程A产生数据发给另外一个进程B,进程B需要对进程A传的数据进行处理并写入文件,如果B没有处理完,则A要延迟发送.为了保证进程A减少等待时间,可以在A和B之间采用一个缓冲区,A每次将数据存放在缓冲区中,B每次冲缓冲区中取.这是典型的生产者和消费者模型,缓冲区中数据满足FIFO特性,因此可以采用队列进行实现.

代码学习-Linux内核网卡收包过程(NAPI)

本文通过学习RealTek8169/8168/8101网卡的驱动代码(drivers/net/r8169.c),梳理一下Linux下网卡的收包过程. 在下水平相当有限,有不当之处,还请大家斧正^_^ 驱动的初始化 如下的rtl8169_init_module函数是此驱动的初始化代码,此函数只干了一件事,就是向内核注册一个pci驱动rtl8169_pci_driver. static int __init rtl8169_init_module(void) { returnpci_register

Tomcat结构、启动过程、关键组件简单分析

Tomcat 结构: Tomcat最顶层容器叫Server,代表整个服务器,Server中包含至少一个Service,用于具体提供服务,Service主要包含:Connector和Container,前者处理链接并提供Socket与request和response的转换,Container用于封装和管理Servlet,以及具体处理request请求. 一个Tomcat里一个Server,包含多个Service,一个Service只有一个Container,可以有多个Connector.一个Con