Linux内核启动时驱动初始化过程

一、驱动模块存在形式

内核源码树中大部分内容为驱动代码,那么在内核中,每个驱动在内核编译时都是以如下的两种形式进行编译的:

1、静态编译

将驱动编译进内核;

2、动态编译

将驱动编译成可以在需要时动态插入到内核中的模块,即ko的形式;

二、内核初始化时驱动是如何加载的?

整体的流程为先加载内嵌驱动,后加载模块形式的驱动;

1、内嵌驱动加载

start_kernel中会去创建1号进程,此时1号进程执行的函数为kernel_init,kernel_init负责完成大部分的初始化功能;驱动的初始化加载的函数在do_initcalls中;

    kernel_init->kernel_init_freeable->do_basic_setup->do_initcalls->async_synchronize_full //需要等待所有的initcalls完成

在do_initcalls中按照顺序依次加载内嵌驱动,一共定义了如下几种level,在编写时可以自定义驱动初始化函数的level,例如scsi驱动的初始化函数的定义如下所示:

/*drivers/scsi/scsi.c*/
//定义init_scsi为subsys_initcall,level4,会优先于module_init的驱动执行
subsys_initcall(init_scsi);
 *//* 数字越小,优先级越高 */
#define pure_initcall(fn)               __define_initcall(fn, 0)#define core_initcall(fn)               __define_initcall(fn, 1)
#define core_initcall_sync(fn)          __define_initcall(fn, 1s)
#define postcore_initcall(fn)           __define_initcall(fn, 2)
#define postcore_initcall_sync(fn)      __define_initcall(fn, 2s)
#define arch_initcall(fn)               __define_initcall(fn, 3)
#define arch_initcall_sync(fn)          __define_initcall(fn, 3s)
#define subsys_initcall(fn)             __define_initcall(fn, 4)
#define subsys_initcall_sync(fn)        __define_initcall(fn, 4s)
#define fs_initcall(fn)                 __define_initcall(fn, 5)
#define fs_initcall_sync(fn)            __define_initcall(fn, 5s)
#define rootfs_initcall(fn)             __define_initcall(fn, rootfs)
#define device_initcall(fn)             __define_initcall(fn, 6)
#define device_initcall_sync(fn)        __define_initcall(fn, 6s)
#define late_initcall(fn)               __define_initcall(fn, 7)
#define late_initcall_sync(fn)          __define_initcall(fn, 7s)#define __initcall(fn) device_initcall(fn)
static void __init do_initcalls(void)
{int level;size_t len = strlen(saved_command_line) + 1;char *command_line;command_line = kzalloc(len, GFP_KERNEL);if (!command_line)panic("%s: Failed to allocate %zu bytes\n", __func__, len);for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) {/* Parser modifies command_line, restore it each time */strcpy(command_line, saved_command_line);do_initcall_level(level, command_line);//level 0->7依次执行}kfree(command_line);
}

相同的level下执行函数为do_initcall_level,那么当level相同时如何执行呢?链接顺序有关,即顺序与makefile文件中的排序有关

static void __init do_initcall_level(int level, char *command_line)
{initcall_entry_t *fn;parse_args(initcall_level_names[level],command_line, __start___param,__stop___param - __start___param,level, level,NULL, ignore_unknown_bootoption);trace_initcall_level(initcall_level_names[level]);for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)do_one_initcall(initcall_from_entry(fn));//执行一个初始化函数
}

2、外部模块加载

async_synchronize_full 等待所有的内嵌驱动初始化完成后kernel_init才开始继续执行。此时1号进程还处于内核态,直到kernel_init通过run_init_process去加载用户态的init程序(centos 7以后为systemd进程:/sbin/init->systemd),此时由内核态转为用户态;

外部模块加载,此时是由systemd-udev负责监控uevent并根据规则去modporbe驱动来加载的

3、debug

cmdline中打开initcall_debug即可打印出kernel启动时的信息,可以直观的看到驱动的初始化顺序。

至于为何通过modprobe形式加载的外部模块也会存在initall的信息,原因在于module_init也是一种device_initcall;调用的循序为module_init(x)->__initcall(x)->device_initcall(x);

#ifndef MODULE
/*** module_init() - driver initialization entry point* @x: function to be run at kernel boot time or module insertion** module_init() will either be called during do_initcalls() (if* builtin) or at module insertion time (if a module).  There can only* be one per module.*/
#define module_init(x)  __initcall(x);//#define __initcall(fn) device_initcall(fn)

Linux内核启动中驱动初始化过程相关推荐

  1. c++ map 初始化_如何调整Linux内核启动中的驱动初始化顺序?

    如何调整Linux内核启动中的驱动初始化顺序?[问题] 此处我要实现的是将芯片的ID用于网卡MAC地址,网卡驱动是enc28j60_init. 但是,读取芯片ID的函数,在as352x_afe_ini ...

  2. 如何调整Linux内核启动中的驱动初始化顺序

    [问题] 此处我要实现的是将芯片的ID用于网卡MAC地址,网卡驱动是enc28j60_init. 但是,读取芯片ID的函数,在as352x_afe_init模块中,所以要先初始化as352x_afe_ ...

  3. Linux内核学习:EXT4 文件系统在 Linux 内核系统中的读写过程

    目录 1 概述 2 虚拟文件系统 与 Ext4 文件系统 2.1 sys_write( ) 代码跟踪 2.2 sys_write( ) 过程分析 2.3 sys_write( ) 的核心部分 vfs_ ...

  4. 内核logo 前闪 linux,Linux内核启动中显示的logo的修改

    1.配置内核 使内核启动时加载logo,在源代码的主目录下make menuconfig Device Drivers  ---> Graphics support  ---> 选上 并 ...

  5. AR9331中Linux内核启动中与IRQ中断相关的文件

    先列出框架,具体后继再来分析. 首先是lds文件,该文件设置了各个section在FLASH或RAM中的先后顺序. 位于~/openwrt1407/build_dir/target-mips_34kc ...

  6. linux内核启动以及文件系统的加载过程

    Linux 内核启动及文件系统加载过程 当u-boot 开始执行 bootcmd 命令,就进入 Linux 内核启动阶段.普通 Linux 内核的启动过程也可以分为两个阶段.本文以项目中使用的 lin ...

  7. Linux内核启动及文件系统加载过程

    当u-boot开始执行bootcmd命令,就进入linux内核启动阶段 与 u-boot 类似,普通 Linux 内核的启动过程也可以分为两个阶段,但针对压缩了的内核如 uImage 就要包括内核自解 ...

  8. linux内核启动第一个进程,linux内核启动流程

    描述 Linux的启动代码真的挺大,从汇编到C,从Makefile到LDS文件,需要理解的东西很多.毕竟Linux内核是由很多人,花费了巨大的时间和精力写出来的.而且直到现在,这个世界上仍然有成千上万 ...

  9. linux内核启动过程3:内核初始化阶段

    上一篇<<linux内核启动过程2:保护模式执行流程>>分析了保护模式启动过程以及bzImage的解压入口函数,本篇继续分析内核启动过程,从保护模式到C代码初始化. start ...

  10. linux内核中启动页面,Linux内核启动过程分析

    下面给出内核映像完整的启动过程: arch/x86/boot/header.S: --->header第一部分(以前的bootsector.S):  载入bootloader到0x7c00处,设 ...

最新文章

  1. HTTP2 基础知识点总结
  2. 【ASP】简单Url编码和Url解码实例
  3. 终于找到你!如何将前端console.log的日志保存成文件?
  4. CSS实现背景透明而背景上的文字不透明
  5. 标签传播(阅读笔记)
  6. 【电路】简易的桥式整流电路---选取滤波电容
  7. 《深入浅出DPDK》读书笔记(三):NUMA - Non Uniform Memory Architecture 非统一内存架构
  8. Spring Cloud与微服务学习总结(8)——Spring Boot、微服务架构和大数据治理三者之间的故事
  9. 程序阅读理解题目(高中语文版,附答案)
  10. mysql 虚拟表 分页_MySql大表分页(附独门秘技)
  11. java 计算限行尾号(北京)
  12. psp3000 java_psp上的python
  13. centos7+docker+安装mysql5.7
  14. Python期末复习题及代码
  15. 上帝永远不会问你的十件事
  16. 十款浏览器插件,让你拥有更好的浏览器体验
  17. 携程、飞猪?大数据杀熟的背后,到底杀死了谁?
  18. _access()函数的使用
  19. java1.8 list stream求平均数
  20. 整数运算(加减法)详解

热门文章

  1. UVa 674 Coin Change(完全背包)
  2. Java设计模式-Builder生成器模式
  3. vijos1053 用spfa判断是否存在负环
  4. php中sprintf与printf函数用法区别
  5. 如何 把 laravel model 的主键修改为字符串类型
  6. echarts环形图加边框
  7. 【Docker篇之三】Dockerfile创建镜像
  8. 极限编程的12个实践原则
  9. C# 文件读写系列三
  10. 【FPGA】TestBench中关于@eachvec