Linux/Android启动之Machine-Init函数

前言:

前面写过两篇Linux/Android的启动过程分析,这篇接着前两篇的知识点进行分析。

Linux/Android的启动过程包括了很多内容,其中有些需要了解,有些则需要在系统移植的时候进行修改。本篇文章主要来讲述Machine-Init函数在系统启动过程中如何被调用的以及在何时被调用。

Linux中的Machine-Init在功能和调用位置上类似于Win CE/ Windows Mobile中的OAL初始化函数OEMInit。

哈哈。如果有不正确或者不完善的地方,欢迎前来拍砖留言或者发邮件到guopeixin@126.com进行讨论,先行谢过。

一. 基础知识

1. Linux启动过程中驱动模块初始化的位置

Linux OS的启动过程中将会去创建线程kernel_init,该线程负责Driver初始化等一系列工作。线程kernel_init将会依次调用do_basic_setup()àdo_initcalls()àdo_one_initcall(),并在do_initcalls()中完成对各个驱动模块Init函数的调用。

这部分代码如下:

函数do_basic_setup()如下:

/*

* Ok, the machine is now initialized. None of the devices

* have been touched yet, but the CPU subsystem is up and

* running, and memory and process management works.

*

* Now we can finally start doing some real work..

*/

static void __init do_basic_setup(void)

{

rcu_init_sched(); /* needed by module_init stage. */

init_workqueues();

usermodehelper_init();

driver_init();

init_irq_proc();

do_initcalls();

}

函数do_initcalls():

extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];

static void __init do_initcalls(void)

{

initcall_t *call;

for (call = __early_initcall_end; call < __initcall_end; call++)

do_one_initcall(*call);

/* Make sure there is no pending stuff from the initcall sequence */

flush_scheduled_work();

}

函数do_one_initcall(initcall_t fn)如下:

int initcall_debug;

core_param(initcall_debug, initcall_debug, bool, 0644);

int do_one_initcall(initcall_t fn)

{

int count = preempt_count();

ktime_t calltime, delta, rettime;

char msgbuf[64];

struct boot_trace_call call;

struct boot_trace_ret ret;

if (initcall_debug) {

call.caller = task_pid_nr(current);

printk("calling  %pF @ %i/n", fn, call.caller);

calltime = ktime_get();

trace_boot_call(&call, fn);

enable_boot_trace();

}

ret.result = fn();

if (initcall_debug) {

disable_boot_trace();

rettime = ktime_get();

delta = ktime_sub(rettime, calltime);

ret.duration = (unsigned long long) ktime_to_ns(delta) >> 10;

trace_boot_ret(&ret, fn);

printk("initcall %pF returned %d after %Ld usecs/n", fn,

ret.result, ret.duration);

}

msgbuf[0] = 0;

if (ret.result && ret.result != -ENODEV && initcall_debug)

sprintf(msgbuf, "error code %d ", ret.result);

if (preempt_count() != count) {

strlcat(msgbuf, "preemption imbalance ", sizeof(msgbuf));

preempt_count() = count;

}

if (irqs_disabled()) {

strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf));

local_irq_enable();

}

if (msgbuf[0]) {

printk("initcall %pF returned with %s/n", fn, msgbuf);

}

return ret.result;

}

二.Machine-Init函数被调用的位置

Machine-Init也将在函数do_one_initcall(initcall_t fn)中伴随其它的模块Init函数一起被调用,不同之处在于Machine-Init的优先级最高。

三.Machine-Init函数相关解析过程

1. 重要结构体machine_desc说明

struct machine_desc描述了机器(machine, 也就是目标板)对内核初始阶段资源分配至关重要的一些参数。

首先,来看一下结构体machine_des的内容(在arch/arm/include/asm/mach/arch.h):

struct machine_desc {

/*

* Note! The first four elements are used

* by assembler code in head.S, head-common.S

*/

unsigned int nr; /* architecture number,记录体系结构 */

unsigned int phys_io; /* start of physical io */

unsigned int io_pg_offst; /* byte offset for io

* page tabe entry */

const char *name; /* architecture name,体系结构名字 */

unsigned long boot_params; /* tagged list */

unsigned int video_start; /* start of video RAM */

unsigned int video_end; /* end of video RAM */

unsigned int reserve_lp0 :1; /* never has lp0 */

unsigned int reserve_lp1 :1; /* never has lp1 */

unsigned int reserve_lp2 :1; /* never has lp2 */

unsigned int soft_reboot :1; /* soft reboot */

void (*fixup)(struct machine_desc *,

struct tag *, char **,

struct meminfo *);

void (*map_io)(void);/* IO mapping function */

void (*init_irq)(void);

struct sys_timer *timer; /* system tick timer */

void (*init_machine)(void);

};

其中,结构体的最后一个函数指针init_machine会被初始化为Machine-Init的地址。系统中针对每一种CPU都会去定义一个该结构体变量,并在系统的启动过程中进行引用。

2. 对应当前开发板的结构体machine_des的初始化

这里以Samsung S3C6410的开发板的BSP为例来进行分析。

Samsung S3C6410的machine_des在文件arch/arm/mach-s3c6410/mach-s3c6410.c中定义,定义方式如下:

MACHINE_START(SMDK6410, "SMDK6410")

/* Maintainer: Ben Dooks <ben@fluff.org> */

.phys_io = S3C_PA_UART & 0xfff00000,

.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,

.boot_params = S3C64XX_PA_SDRAM + 0x100,

.fixup = smdk6410_fixup,

.init_irq = s3c6410_init_irq,

.map_io = smdk6410_map_io,

.init_machine = smdk6410_machine_init,

#ifndef CONFIG_HIGH_RES_TIMERS

.timer = &s3c64xx_timer,

#else

.timer = &sec_timer,

#endif /* CONFIG_HIGH_RES_TIMERS */

MACHINE_END

Linux中针对各个不同的CPU存在很多个类似于上面的定义,宏定义MACHINE_START的定义如下:

/*

* Set of macros to define architecture features.  This is built into

* a table by the linker.

*/

#define MACHINE_START(_type,_name) /

static const struct machine_desc __mach_desc_##_type /

__used /

__attribute__((__section__(".arch.info.init"))) = { /

.nr = MACH_TYPE_##_type, /

.name = _name,

#define MACHINE_END /

};

其实,宏定义替换后的就变成了如下的定义方式:

static const struct machine_desc__SMDK6410

__used

__attribute__((__section__(".arch.info.init"))) = { /*__section__指定了该结构体被链接的位置*/

.nr = MACH_TYPE_SMDK6410,

.name = "SMDK6410",

phys_io = S3C_PA_UART & 0xfff00000,

.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,

.boot_params = S3C64XX_PA_SDRAM + 0x100,

.fixup = smdk6410_fixup,

.init_irq = s3c6410_init_irq,

.map_io = smdk6410_map_io,

.init_machine = smdk6410_machine_init,

#ifndef CONFIG_HIGH_RES_TIMERS

.timer = &s3c64xx_timer,

#else

.timer = &sec_timer,

};

3. 函数setup_machine实现对结构体machine_des的定位

函数setup_machine实现对结构体machine_des位置的判别,其代码代码如下:

static struct machine_desc * __init setup_machine(unsigned int nr)

{

struct machine_desc *list;

/*

* locate machine in the list of supported machines.可能支持多个cpu哦

*/

list = lookup_machine_type(nr);

if (!list) {

printk("Machine configuration botched (nr %d), unable "

"to continue./n", nr);

while (1);

}

printk("Machine: %s/n", list->name);

return list;

}

上面红色标记的函数lookup_machine_type(nr)用汇编实现在文件head.S中,用来查找对应当前设备的machine_desc变量位置,并通过函数lookup_machine_type指向其起始位置,后续就可以直接通过该返回值来对结构体machine_desc变量machine_desc__SMDK6410的值进行读取,如后面读取Machine-Init函数的指针的操作init_machine = mdesc->init_machine。

而函数setup_machine被调用的过程如下(start_kernelàsetup_archàsetup_machine):

在函数setup_machine最后会在全局指针变量init_machine中记录Machine-Init函数的信息。

4. Machine-Init的导出

在文件arch/arm/kernel/setup.c中通过如下的方式将Machine-Init设置为模块的Init函数:

static void (*init_machine)(void) __initdata;

static int __init customize_machine(void)

{

/* customizes platform devices, or adds new ones */

if (init_machine)

init_machine();

return 0;

}

arch_initcall(customize_machine);

可能不太熟悉驱动优先级的人会觉得,这里怎么使用arch_initcall导出,而不是通常的module_init方式进行导出。

在Linux中,没有办法像CE/Mobile中通过注册表的方式来定义驱动的优先级,只有通过导出函数的Level和Makefile中模块驱动书写的先后顺序来定义。

关于导出函数Level的代码如下:

#define __define_initcall(level,fn)   static initcall_t __initcall_##fn __attribute_used__   __attribute__((__section__(".initcall" level ".init"))) = fn

#define core_initcall(fn) __define_initcall("1",fn)

#define postcore_initcall(fn) __define_initcall("2",fn)

#define arch_initcall(fn) __define_initcall("3",fn)

#define subsys_initcall(fn) __define_initcall("4",fn)

#define fs_initcall(fn) __define_initcall("5",fn)

#define device_initcall(fn) __define_initcall("6",fn)

#define late_initcall(fn) __define_initcall("7",fn)

#define __initcall(fn) device_initcall(fn)

#define __exitcall(fn)   static exitcall_t __exitcall_##fn __exit_call = fn

#define module_init(x) __initcall(x);

#define module_exit(x) __exitcall(x);

可以看到,通常驱动中采用的module_init优先级为6,也即最低优先级,而前面采用的arch_initcall优先级为最高优先级1。

(完)

Linux-Android启动之Machine-Init函数相关推荐

  1. android启动过程之init.rc文件浅析

    1.  init.rc文件结构 文件位置: init.c  : /system/core/init init.rc  : /system/core/rootdir 首先init.rc文件是以模块为单位 ...

  2. android 启动流程

     Android系统启动流程 -- bootloader 摘要:本文讲解Android系统在启动过程中的关键动作,摈弃特定平台之间的差异,讨论共性的部分,至于启动更加详细的过程,需要结合代码分析, ...

  3. android 启动过程

    android系统启动的时候首先会启动Linux的基础进程,加载Linux kernel启动初始化(init)进程. 接着,回启动Linux deamon(守护进程)会启动以下的内容: ①启动USBd ...

  4. 【Linux】Linux Systemd 启动守护进程

    1.概述 转载:http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html 去看原文吧,排版比较好,这里转载防丢失 Sy ...

  5. Android系统开机启动流程及init进程浅析

    Android系统启动概述 Android系统开机流程基于Linux系统,总体可分为三个阶段: Boot Loader引导程序启动 Linux内核启动 Android系统启动,Launcher/app ...

  6. Android研究-linux内核启动到android系统

    很多人阅读代码,总喜欢从头开始,这样觉得很安全,有依靠,无论如何总是能知道"头",有头就能找到任何需要的部分. Android生在linux内核基础上,linux内核启动的最后一步 ...

  7. linux文件系统启动流程,linux 内核启动过程以及挂载android 根文件系统的过程

    转载 作者:汕头大学-黄珠唐 时间:2009 年10 月29 日 主要介绍linux 内核启动过程以及挂载android 根文件系统的过程,以及介绍android 源代码中文件系统部分的浅析. 主要源 ...

  8. Android程序暂停sh,init进程 解析Android启动脚本init.rc 修改它使不启动android init.rc中启动一个sh文件...

    Android启动后,系统执行的第一个进程是一个名称为init 的可执行程序.提供了以下的功能:设备管理.解析启动脚本.执行基本的功能.启动各种服务.代码的路径: system/core/init,编 ...

  9. Android启动的init进程

    /system/core/init/- init.cpp- init_parser.cpp- signal_handler.cpp 一.概述 init是Linux系统中用户空间的第一个进程,进程号为1 ...

  10. Android 9(P)之init进程启动源码分析指南之一

         Android 9 之init进程启动源码分析指南之一 Android 9 (P) 系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 Andro ...

最新文章

  1. mysql linux centos 安装_Linux centos 下在线安装mysql
  2. Java中 Character方法练习:字符串中英文字母个数 5435abc54abc3AHJ5 正则:matches([a-zA-Z0-9]{1})...
  3. OpenCASCADE:扩展数据交换(XDE)的简介
  4. android: 调用摄像头拍照
  5. java异常处理,需要考虑的流程
  6. 易混淆知识点(1):word-wrap和word-break傻傻分不清楚
  7. 10、存储过程、while语句
  8. 电商美工必备,品质超高的万能套用促销PSD分层模板,宝藏!
  9. nginx与lighttpd性能简单对比
  10. 浙江商人立下的22条规矩
  11. html播放rtmp直播,video.js实现浏览器播放rtmp协议直播流的问题
  12. 抖音上最火的七个Excel视频教程专栏,利用休闲时间也能成为Excel高手。
  13. 光学成像系统的模型及MATLAB仿真
  14. MYSQL 安装时出现的问题error: Failed dependencies
  15. 4.2 存储器读写指令的发射与执行2
  16. 墨刀右键菜单被浏览器右键菜单遮挡导致墨刀右键菜单无法使用
  17. 【全国大学生IoT设计竞赛】安谋科技灵动赛题国赛一等奖分享:多足仿生机器人
  18. 字体图标iconfont的使用
  19. 28款GitHub最流行的开源机器学习项目,推荐GitHub上10 个开源深度学习框架
  20. 阿里云培训-负载均衡(CLB/ALB)

热门文章

  1. 有关sed命令的用法
  2. bootstrap的分页
  3. FireBug实用指南
  4. Ext Ajax:如何调用Ext.Ajax.request方法和使用Java Servlet进行处理
  5. html表格右键可编辑,Bootstrap table右键功能实现方法
  6. Party at Hali-Bula UVA - 1220(树形dp)
  7. php mysql读取数据_PHP MySQL 读取数据
  8. 福建职称计算机评聘任,职称聘任工作的有关补充规定(试行)
  9. 关于天线增益、发射角、阵列的一些见解
  10. PAT_B_1086_Java(15分)