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

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

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

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

 static void __init do_initcalls(void)
{
initcall_t *call;
int count = preempt_count(); for (call = __initcall_start; call < __initcall_end; call++) {
ktime_t t0, t1, delta;
char *msg = NULL;
char msgbuf[];
int result;
....
}
}

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

先把相关的定义贴出来:

         __initcall_start = .;
INITCALLS
__initcall_end = .;
 #define INITCALLS                            \
*(.initcall0.init) \
*(.initcall0s.init) \
*(.initcall1.init) \
*(.initcall1s.init) \
*(.initcall2.init) \
*(.initcall2s.init) \
*(.initcall3.init) \
*(.initcall3s.init) \
*(.initcall4.init) \
*(.initcall4s.init) \
*(.initcall5.init) \
*(.initcall5s.init) \
*(.initcallrootfs.init) \
*(.initcall6.init) \
*(.initcall6s.init) \
*(.initcall7.init) \
*(.initcall7s.init)
 #define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __attribute_used__ \
__attribute__((__section__(".initcall" level ".init"))) = fn

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

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

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

 module_init(usb_stor_init);
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的模块机制是什么样子的了。

 #define pure_initcall(fn)                __define_initcall("0",fn,1)

 #define core_initcall(fn)                __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#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);

Linux内核启动流程与模块机制的更多相关文章

  1. 【内核】linux内核启动流程详细分析

    Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件,包括内核入口ENTRY(stext)到start_kernel间的初始化代码, 主要作用 ...

  2. 【内核】linux内核启动流程详细分析【转】

    转自:http://www.cnblogs.com/lcw/p/3337937.html Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件 ...

  3. linux 内核启动流程

    Linux内核启动流程详细分析: http://www.linuxidc.com/Linux/2014-10/108034.htm ARM Linux内核启动过程: http://blog.csdn. ...

  4. Linux内核启动流程(简介)

    1. vmlinux.lds 首先分析 Linux 内核的连接脚本文件 arch/arm/kernel/vmlinux.lds,通过链接脚本可以找到 Linux 内核的第一行程序是从哪里执行的: 第 ...

  5. linux 内核启动流程分析,移植

    分析 linux-2.6.22.6 内核启动流程 移植 linux-3.4.2 到 JZ2440 开发板 Linux内核源码百度云链接: https://pan.baidu.com/s/1m1ymGl ...

  6. Tiny4412 Linux 内核启动流程

    Linux内核的启动分为压缩内核和非压缩内核两种,这里我们以压缩内核为例.压缩内核运行时,将运行一段解压缩程序,得到真正的内核镜像,然后跳转到内核镜像运行.此时,Linux进入非压缩内核入口,在非压缩 ...

  7. Linux内核启动流程分析(一)【转】

    转自:http://blog.chinaunix.net/uid-25909619-id-3380535.html 很久以前分析的,一直在电脑的一个角落,今天发现贴出来和大家分享下.由于是word直接 ...

  8. linux内核启动流程[转]

    启动流程一览 既然启动是很严肃的一件事,那我们就来了解一下整个启动的过程吧! 好让大家比较容易发现启动过程里面可能会发生问题的地方,以及出现问题后的解决之道! 不过,由於启动的过程中,那个启动管理程序 ...

  9. Linux内核启动流程分析(二)【转】

    转自:http://blog.chinaunix.net/uid-25909619-id-3380544.html S3C2410 Linux 2.6.35.7启动分析(第二阶段) 接着上面的分析,第 ...

随机推荐

  1. 分组加密的四种模式(ECB、CBC、CFB、OFB)

    加密一般分为对称加密(Symmetric Key Encryption)和非对称加密(Asymmetric Key Encryption).对称加密又分为分组加密和序列密码.分组密码,也叫块加密(bl ...

  2. (转)Java大数操作(BigInteger、BigDecimal)

    基础知识 对于二进制来说,最高位代表正负号,-0表示-128,+0表示032位系统int型4个字节:-(2的31次方) ~ (2的31次方) 减 1最大负数:10000000 00000000 000 ...

  3. Python3.6下使用会话session保持登陆状态

    本次工具主要利用python easygui模块的inputbox让用户首次输入登陆信息,作为网站requests-post请求的data字段,观察XHR(异步加载)的数据包,构造post请求,利用r ...

  4. [LeetCode] 884. Uncommon Words from Two Sentences_Easy tag: Hash Table

    We are given two sentences A and B.  (A sentence is a string of space separated words.  Each word co ...

  5. CentOS6.5安装Scrapy

    1.安装命令超级简单: [root@mycentos ~]# pip install Scrapy 建立软链接: [root@mycentos ~]# ln -s /usr/local/python3 ...

  6. css页面布局--三栏(两边固定中间自适应&两边自适应中间固定)

    http://www.cnblogs.com/zhanyishu/p/5656875.html

  7. react结合redux开发

    先加上我码云的地址:https://gitee.com/ldlx/react_and_rudex

  8. HDU 1568 Fibonacci(大数前4位)

    转载自:http://blog.csdn.net/thearcticocean/article/details/47615241 分析:x=1234567.求其前四位数: log10(x)=log10 ...

  9. C#字符串比较方法

    用C#比较字符串有多种方法,如: 1. string.Compare(x,y);2. string.Equals(x,y) ; 如果要不区分大小写进行比较,则对应为:string.Compare(x, ...

  10. 如何删除WINDOWS中服务中不再使用的服务?

    右键选择那个不需要的服务,点击属性,看到"服务名",拷贝下来 然后通过管理员的方式进入到控制台,输入 sc delete "你刚才拷贝的服务名" ,就行了,注意 ...