- Android Looper 和 Handler 分析
- Android MediaScanner 详尽分析
- Android 深入浅出之 Binder 机制
- 第一部分 AudioTrack 分析
- 第二部分 AudioFlinger 分析
- Android 深入浅出之 Audio 第三部分 Audio Policy
- Android 深入浅出之 Zygote
- Android 深入浅出之 Surface
- Linux Kernel 系列一 开篇和 Kernel 启动概要
- Linux Kernel 系列二 用户空间的初始化
- Linux Kernel 系列三 Kernel 编译和链接中的 linker script 语法详解
- 第五章 深入理解常见类
- linux kernel 系列四 嵌入式系统中的文件系统以及 MTD
- 随笔之 Android 平台上的进程调度探讨
- Android 4.0 External 下功能库说明
- 随笔之 Android 不吐不快
- Android Rom 移植知识普及
- 深入理解 Android 系列书籍的规划路线图
- Android 4.1 初识 - 7月12号
- Android 4.1 初识 - 7月13号
- Android 4.1 Surface 系统变化说明
- Android BSP 成长计划随笔之虚拟设备搭建和 input 系统
- 深入理解 Android 写作背后的故事
- 随笔之 GoldFish Kernel 启动过程中 arm 汇编分析
- Android Project Butter 分析
- Android Says Bonjour
- MTP in Android
- DRM in Android
- Tieto 公司 Android 多窗口解决方案展示
- 深入理解 SELinux SEAndroid 之二
- 深入理解 SELinux SEAndroid(最后部分)
- 前言
- 附录
- 第一章 准备工作
- 第二章 深入理解 Netd
- 第三章 Wi-Fi 基础知识
- 第四章 深入理解 wpa_supplicant
- 第五章 深入理解 WifiService
- 第六章 深入理解 wi-Fi Simple Configuration
- 第七章 深入理解 Wi-Fi P2P
- 第八章 深入理解 NFC
- 第九章 深入理解 GPS
- Google I/O 2014 之 Android 面面观
- 深入理解 Android 之 Java Security 第一部分
- 深入理解 Android 之 Java Security 第二部分(Final)
- 深入理解 Android 之设备加密 Device Encryption
- 第一章 阅读前的准备工作
- 第二章 深入理解 JNI
- 第三章 深入理解 init
- 第四章 深入理解 Zygote
- 第五章 深入理解常见类
- 第六章 深入理解 Binder
- 第七章 深入理解 Audio 系统
- 第八章 深入理解 Surface 系统
- 第九章 深入理解 Vold 和 Rild
- 第十章 深入理解 MediaScanner
- 第一章 开发环境部署
- 第二章 深入理解 Java Binder 和 MessageQueue
- 第三章 深入理解 SystemServer
- 第四章 深入理解 PackageManagerService
- 第五章 深入理解 PowerManagerService
- 第六章 深入理解 ActivityManagerService
- 第七章 深入理解 ContentProvider
- 第八章 深入理解 ContentService 和 AccountManagerService
- 第一章 开发环境部署
- 第二章 深入理解 Java Binder 和 MessageQueue
- 第三章 深入理解 AudioService
- 第四章 深入理解 WindowManagerService
- 第五章 深入理解 Android 输入系统
- 第六章 深入理解控件(ViewRoot)系统
- 第七章 深入理解 SystemUI
- 第八章 深入理解 Android 壁纸
- 边缘设备、系统及计算杂谈(16)——Apache 学习
- 边缘设备、系统及计算杂谈(17)——Ansible 学习
- ZFS 和 LVM
- Android 4.2 蓝牙介绍
- 了解一下 Android 10 中的 APEX
- 关于 Android 学习的三个终极问题
- 深入理解 Android 之 AOP
- Android 系统性能调优工具介绍
- 深入理解 SELinux SEAndroid(第一部分)
- Android Wi-Fi Display(Miracast)介绍
- 深入理解 Android 之 Gradle
Linux Kernel 系列二 用户空间的初始化
上篇我们知道,kernel初始化后将启动init进程,那么这个进程将干些什么呢?除此之外,kernel还需要做些什么事情呢?(想想文件系统、根存储设备是在什么时候初始化的呢?)
先从文件系统初始化说起。以前一直不明白,有了kernel为何还需要一个文件系统?经过反复琢磨,明白一个道理,kernel加载到内存后,kernel运行起来是没有问题的,但是如果没有Root FS,就好像PC上没有硬盘.....。另外,Linux中很多虚拟文件系统(proc,sys,dev等)都是挂靠在RootFS中的,所以RootFS在Linux中更加关键(必要条件简直就是)。(kernel中的FS是另外一个庞大的部分)
一 根文件系统
1 FHS:File system Hierachy Standard:Linux上文件系统布局的标准,例如 usr目录大概是干吗的,tmp目录大概是干嘛的。有空可以瞧瞧....其实使用LINUX OS多了,自然就理解了。
2 常用的文件夹布局:其实就是ES上普遍的文件目录:
- bin;dev;etc;home;lib;sbin;tmp;usr;var;
二 Post Boot
这里讲的是execve init之前的事情,因为源码中:
- run_init_process("/usr/bin/init"):这个时候已经有FS的布局了,也就是init程序本身必须放在一个FS中。
三 init
init进程很重要,不过android上的init进程的工作流程比较简单。这里介绍非Android上的init。一般它读取的配置文件是/etc/inittab中(ubuntu上似乎没有这个文件了,以后得找个FCore的系统看看)。
另外,这里还有一个叫run level的概念。见图1.
图1 run level
Run level说白了就是将系统运行状态分成几个级别,例如shutdown的时候init需要执行一些操作,reboot的时候需要执行一些操作。
这里关于Init的东西就不介绍了,很多关于linux系统配置的知识都有涉及。(确实比android的init要复杂多了)
四 Initial RAM Disk
LK在早期初始化的过程中,需要mount一个FS,目前有新旧两种方法:
old方法就是使用initial ram disk,也叫initrd
new方法就是使用iniramfs
这两个东西非常常见,咱们要好好研究下。
4.1 initrd
这个功能需要配置kernel的编译选项。
ARM支持将前面的initrd和vmlinux打包到一个image中。实际上只有ARM架构支持。(内核编译的时候要选择这一项)。讲了这么多,那么到底怎么用呢?
initrd也是一个image。由bootloader启动的时候,或者bootloader下载到某个地方
bootloader把initrd的地址告诉内核。内核启动时候把这个image解压并挂载
另外一种办法,编译的时候将initrd和kernel放到一个image中,这种方法只有ARM架构支持。使用这种办法话,建议用initramfs。注意,android中使用的就是一个kernel+initramfs的单一image。也就是第二种办法
(这里有很多细节问题,以后我们分析源码再来搞懂它)
BL启动内核的时候,需要给LK传递参数,即告诉LK这个initrd在什么位置...很简单不是?
KL如何使用这个initrd呢?
KL先根据参数指定的initrd地址,将这个image拷贝到内存中,然后解压,并挂载为/
找到这个disk中的linuxrc文件,然后执行里边的语句《====这给了我们定制化自己ES的好计划
处理完linuxrc后,KL unmount这个initrd,并加载真正的root device(看到没,这个initrd就是做些初始化的工作,但是你也可以不umount这个initrd。
)这里的处理稍有差异。如果BL在参数中指明root=/dev/ram0,(代码中可见到这些语句),那么KL就不会执行linuxrc,并且也不会umount initrd。也就是这个initrd就是最终的根文件系统了。
那么如何制作这个Initrd呢?
其实就是一个gzip打包的文件夹....
(这部分代码在do_mounts.c中的prepare_namespace函数中)
4.2 initramfs
(详细说明:参考kernel/documentations/filesystems/ramfs-rootfs-initramfs.txt)
kernel默认支持这个initramfs,所以编译的时候,会整一个default的initramfs放到内核中。initramfs是一个cpio的打包文件。我特意查了下cpio的info。一般用法就是:读取一个目录下所有文件的信息及其所有文件的内容(可能是直接read数据到一个buffer中),然后把这些信息写到一个文件中。说白了,可能就是一个序列化的工具。然后LK用同样的方法就可以反序列化,恢复原来目录中的内容了。
前面说,LK编译的时候默认会有一个简单的initramfs目录结构。这个结构由kernel/scripts/gen_initramfs_list.sh脚本生成。这个脚本很简单:
dir /dev 0755 0 0
nod /dev/console/ 0600 0 0 c 5 1
dir /root 0700 0 0
执行的时候,前面加上mk...就生成一个目录了,然后用cpio打包,生成iniramfs,最后由LK解包并挂载
(具体内容,参考ramfs-rootfs-initramfs.txt)
如何制作自己的initramfs呢?
搞一个文件夹吧,可仿照PC机器上linux的文件结构。也可以把busybox放上去。
find testramfs -depth -print | cpio -ov > testramfs.cpio cpio的输入是文件名,输出通过>定向到testramfs.cpio。大家可以试试。
解压的话,cpio -ivd < testramfs.cpio。这样就能还原testramfs文件夹中的内容了。
cpio:-o表示output,-v表示打印一些verbose信息,-i表示input,-d表示建立整个文件夹结构。没有-d的话,会出问题。
不过有了kernel编译的支持,我们不用自己调用cpio了,在编译选项中有一个INITRAMFS_SOURCE,把它指向目标文件夹,编译的时候自然会生成这个initramfs了。
参考文献:
这些参考文献中,最重要的是最后一个,ols2k-9.ps,下载并处理后得到一个pdf,实际上一篇论文。主要介绍了
Linux启动的一些问题。
再三解释一下,为什么需要init ram disk。FS一般安装在存储介质上,而读取这些存储介质需要驱动。内核启动的时候如果把这些驱动都加载的话,会非常麻烦,即使你把驱动静态编译到内核中,也不是一个完美的解决办法。所以。先整一个简单的,基于内存的FS,这样初始化工作都可以顺利进行。最后,等驱动都加载完后,再把实际存储上的FS挂载上来。这里要明白一点,没有一个FS的话,LK是没法正常工作的。
五 U-Boot
全名为Das U-Boot,是一个使用非常广泛的Bootloader。以后会专门撰文介绍UB。这里简单说两个点:
UB的代码结构,先从CPU的start.S开始,这里会根据不同的CPU进行初始化,大部分代码都不需要我们修改
再是Board的启动,这个和具体的板子有关。现在改名叫lowlevel_init.S了。
(最难的部分在于各个设备的初始化了,需要结合开发板的datasheet来做)
这里列出以下参考书:
其中,关于SDRAM.pdf,网址已经移到了:http://www.maxwell.com/products/microelectronics/docs/INTRO_TO_SDRAM.PDF
各位看官可以下载看看。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论