Linux学习笔记之内核启动流程与模块机制

本文旨在简单的介绍一下Linux的启动流程与模块机制:

Linux启动的C入口位于/Linux.2.6.22.6/init/main.c::start_kernel()

下图简要的描述了一下内核初始化的流程:

本文我们分析一下do_initcalls ()函数,他负责大部分模块的初始化(比如U盘驱动就是在这里被初始化的)。

 1 static void __init do_initcalls(void)
 2 {
 3     initcall_t *call;
 4     int count = preempt_count();
 5
 6     for (call = __initcall_start; call < __initcall_end; call++) {
 7         ktime_t t0, t1, delta;
 8         char *msg = NULL;
 9         char msgbuf[40];
10         int result;
11         ....
12         }
13 }

它会循环执行一段区域里面的内容,那么这段区域里面是什么呢?

先把相关的定义贴出来:

1         __initcall_start = .;
2             INITCALLS
3         __initcall_end = .;
 1 #define INITCALLS                             2       *(.initcall0.init)                         3       *(.initcall0s.init)                         4       *(.initcall1.init)                         5       *(.initcall1s.init)                         6       *(.initcall2.init)                         7       *(.initcall2s.init)                         8       *(.initcall3.init)                         9       *(.initcall3s.init)                        10       *(.initcall4.init)                        11       *(.initcall4s.init)                        12       *(.initcall5.init)                        13       *(.initcall5s.init)                        14       *(.initcallrootfs.init)                15       *(.initcall6.init)                        16       *(.initcall6s.init)                        17       *(.initcall7.init)                        18       *(.initcall7s.init)
1 #define __define_initcall(level,fn,id) 2     static initcall_t __initcall_##fn##id __attribute_used__ 3     __attribute__((__section__(".initcall" level ".init"))) = fn

这里涉及的是链接器的知识,链接器在链接时会把文件分段,而do_initcalls ()里面的for循环就是循环的某段区域(这里指initcall段),我们把每个模块的入口函数放到这个区域里,这样模块在内核初始化的过程中就会跑到。这里还涉及另一个重要的知识点就是:GCC支持用户通过__attribute__来自定义段(比如通过__init修饰的函数只在初始化过程中执行一次,因为这段区域在初始化之后就被释放掉了)。链接器的相关知识是理解这里的重点,大家可以找点这方面的资料看看。

下面我们以U盘驱动程序为例,看看他是如何被加载的:

/Linux2.6.22.6/drivers/usb/storage/usb.c::

1 module_init(usb_stor_init);
2 module_exit(usb_stor_exit);

module_init()是一个宏,这里表示指定usb_stor_init()为U盘驱动程序的入口点。

#define module_init(x)    __initcall(x);
#define __initcall(fn)    device_initcall(fn)
#define device_initcall(fn)        __define_initcall("6",fn,6)

到这里,大概可以知道,程序在链接时,usb_stor_init()会被放到*(.initcall6.init)可以访问到的地方,这样在do_initcalls ()的for循环里面,usb_stor_init()就会被执行。

到此,也可以大概清楚Linux的模块机制是什么样子的了。

 1 #define pure_initcall(fn)                __define_initcall("0",fn,1)
 2
 3 #define core_initcall(fn)                __define_initcall("1",fn,1)
 4 #define core_initcall_sync(fn)          __define_initcall("1s",fn,1s)
 5 #define postcore_initcall(fn)            __define_initcall("2",fn,2)
 6 #define postcore_initcall_sync(fn)       __define_initcall("2s",fn,2s)
 7 #define arch_initcall(fn)                __define_initcall("3",fn,3)
 8 #define arch_initcall_sync(fn)          __define_initcall("3s",fn,3s)
 9 #define subsys_initcall(fn)              __define_initcall("4",fn,4)
10 #define subsys_initcall_sync(fn)         __define_initcall("4s",fn,4s)
11 #define fs_initcall(fn)                    __define_initcall("5",fn,5)
12 #define fs_initcall_sync(fn)            __define_initcall("5s",fn,5s)
13 #define rootfs_initcall(fn)              __define_initcall("rootfs",fn,rootfs)
14 #define device_initcall(fn)              __define_initcall("6",fn,6)
15 #define device_initcall_sync(fn)        __define_initcall("6s",fn,6s)
16 #define late_initcall(fn)               __define_initcall("7",fn,7)
17 #define late_initcall_sync(fn)          __define_initcall("7s",fn,7s)

上面的定义表示模块也是分优先级的,这也很好理解,还以U盘为例,内核必须先完成USB控制器以及USB总线驱动的初始化,然后U盘的驱动才能初始化成功,所以USB_Core的模块入口定义是下面这样的:

/Linux2.6.22.6/drivers/usb/core/usb.c::

subsys_initcall(usb_init);
module_exit(usb_exit);
时间: 2024-08-25 20:13:47

Linux学习笔记之内核启动流程与模块机制的相关文章

Linux学习笔记之内核线程

本文旨在简单介绍一下Linux内核线程: 先举个例子: 不插U盘,在Linux命令行中输入:ps -el:然后插上U盘,再次输入:ps -el 会发现多出了下面一行(当然还会有其他的,比如scsi相关的): 1 F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 2 1 S 0 2749 2 0 80 0 - 0 - ? 00:00:00 usb-storage usb-storage 就是U盘驱动程序(驱动程序是动态加载的)起来之后,由内核

Tiny4412 Linux 内核启动流程

Linux内核的启动分为压缩内核和非压缩内核两种,这里我们以压缩内核为例.压缩内核运行时,将运行一段解压缩程序,得到真正的内核镜像,然后跳转到内核镜像运行.此时,Linux进入非压缩内核入口,在非压缩内核入口中,完成各种初始化操作后跳转到C语言入口处运行.主要流程如下所示. 1.解压缩内核镜像 解压缩程序通常在arch/arm/boot/compressed/目录中 ├── atags_to_fdt.c ├── big-endian.S ├── decompress.c ├── head.S ├

嵌入式Linux学习笔记(基于S5PV210 TQ210)

基于S5PV210.TQ210平台. 本文更多的是教会大家如何学习! 包括如下内容: 1.前言 2.开发环境搭建 3.制作交叉编译器 4.裸机编程 4.1.汇编学习 4.2.S5PV210启动流程 4.3.点亮一个LED 4.4.串口 4.5.实现printf 4.6.时钟配置 4.7.重定位 4.8.DDR 4.9.NAND读写 4.11.LCD操作 5.移植u-boot(基于u-boot-2014.4版本) 5.1.概述 5.2.u-boot配置过程分析 5.3.u-boot编译过程分析 5

嵌入式Linux学习笔记之LED驱动

最近在学习嵌入式Linux驱动开发,大致了解了驱动的基本开发流程,本文主要针对字符设备驱动开发做一个简要介绍,也当作是对这几天工作的一个小小总结. 计算机系统是由软硬件相互协调共同完成工作的,作为专用计算机系统的嵌入式系统也不例外,既要有CPU.SDRAM.FLASH.IO等硬件,同时也少不了操作系统和应用软件等软件的支持,而作为应用程序与硬件的桥梁--驱动程序,是整个嵌入式系统开发过程中的关键环节.驱动开发涉及底层,而了解底层作用机制对于整个系统的开发意义重大. Linux内核中有60%以上是

Linux学习笔记(7)-系统资源查看

监控系统资源:vmstat #vmstat [采样时间] [ 采样次数] 如:#vmstat 3 2 每3秒采样一次,总共采样2次 输出信息各字段解释 r 表示运行队列,如果运行队列多大表示CPU很繁忙 b 表示阻塞的进程 swpd 虚拟内存已使用的大小,如果大于0,表示机器物理内存不足 free 空闲的物理内存大小 buff 缓冲 cache 缓存 si 每秒从磁盘读入虚拟内存的大小 so 每秒从虚拟内存写入磁盘的大小 bi 磁盘的写入速度 bo 磁盘的读速度 id cpu空闲资源所在百分比

马哥Linux学习笔记之二——网络

1.MAC(Media Access Control,介质访问控制) 是解决底层数据通信冲突的解决方案.因此,给每一个接入这样网络的主机一个id标示符,这个id标示符就叫做MAC地址. 2.CSMA/CD(Carrier Sense Multipath Access Collision Detection,载波侦听多路访问冲突检测),线型网络 以太网Ethernet最核心的标志. 3.Token-Ring(IBM)技术 环形网络解决冲突的方案.但是由于IBM的专利限制没能推广开来. 4.星形网络

linux学习笔记--vim程序编辑器

1,在linux系统中使用文本编辑器来编辑自己的linux参数配置文件是一件很重要的事情,因此系统管理员至少应该要熟悉一种文本编辑器. 2,不同的linux distribution各有不同的附加软件,linux命令行界面下的文本编辑器有Emacs,pico,nano,joe与vim 3,学习vim的重要性 (1)所有的UNIX Like系统都内置vi文本编辑器,其他的文本编辑器不一定存在 (2)很多软件的编辑接口都会主动调用vi (3)vim具有程序编辑的能力,可以主动以字体颜色辨别语法的正确

Linux学习笔记033_10

网卡命名: eth0,eth1 子接口(一个网卡上多个IP):eth0:1,eth0:2 ifconfig: 查看网卡信息 ifup ethx:开启网卡 ifdown ethx:关闭网卡 配置网卡: 图形 命令: ONBOOT为yes表示启动计算机时通过网卡 全局网络配置: 第一行为开启ipv4,第二行为开启ipv6,第三行为计算机名,注意计算机名在DNS要解析到,如clc.com 可以加一行:GATWAY=192.168.1.254,不过以network-scripts中的网关优先级高 DNS

linux学习笔记--服务器突然连不上。 要从哪些方面排查?

1.判断是不是大面积服务器问题.如果是就是机房问题. 2.如果只有这一台连不上,就ping下 如果不通的话.通过远程控制卡连到终端看屏幕提示.或者让机房人员接显示器 查看屏幕提示,如果能登陆就登陆进去看看 IP网卡情况. 3.根据预先灾备计划,如果长时间修复不了,可启动备机接管. 最重要的是: 不要拼死调节,业务持续服务更重要. linux学习笔记--服务器突然连不上. 要从哪些方面排查?,布布扣,bubuko.com