Cubietruck---9.Linux3.3_initramfs启动分析
来源:互联网 发布:简单 三维建模软件 编辑:程序博客网 时间:2024/05/21 13:13
先大体上看一下vmlinux的启动流程
在/work/ct/lichee/linux-3.3/arch/arm/kernel/vmlinux.lds.S中标明了内始的起始位置:
ENTRY(stext)
a. 开始__lookup_processor_type,查找cpu的型号
b. __create_page_tables创建页表
c.把开启mmu后的跳转地址__mmap_switched放在r13中
d.__enable_mmu开启mmu
e. 执行 __mmap_switched
在/work/ct/lichee/linux-3.3/arch/arm/kernel/head-common.S中
a. 清bss
b. 设置sp
c. 将start_kernel的参数,保存一下
d. 跳到start_kernel中执行
一. initramfs的地址传递
1. 解析initramfs的大小与地址
从u-boot传给内核的参数中解析出initramfs在内存中的地址与大小
在arch/arm/mm/init.c中
2. 将参数中的物理地址转为虚拟地址
start_kernel
--> setup_arch
--> arm_memblock_init
在arch/arm/mm/init.c中
二.文件系统的建立
在调用initramfs之前,系统中己经建立了虚拟的根文件系统("/").
在init/initramfs.c中
populate_rootfs会两次调用unpack_to_rootfs把initramfs_datp.cpio与ramdisk.img释放到/下.
2.1 第一次调用unpack_to_rootfs
其中第一个unpack_to_rootfs是把initramfs_data.cpio解出来
cong@dell:/work/ct/lichee$ ls -l ./linux-3.3/usr/initramfs_data.cpio
-rw-rw-r-- 1 cong cong 512 May 20 17:00 ./linux-3.3/usr/initramfs_data.cpio
initramfs_data.cpio是链接在内核中的,
其地址__initramfs_start是在./arch/arm/kernel/vmlinux.lds中定义 __initramfs_start = .
其大小__initramfs_size就是initramfs_data.cpio这个文件的大小
在linux-3.3/scripts/gen_initramfs_list.sh中
说明initramfs_data.cpio中有/dev/ /root两个目录和/dev/console一个设备文件
unpack_to_rootfs会把这些文件都释放到 / 下
2.2第二次调用unpack_to_rootfs
unpack_to_rootfs
--> write_buffer
--> while (!actions[state]())
count = len; //剩余initram的长度
victim = buf; //初始指向initram的首地址,解析时指向的位置依次递增
this_header = 0;
state = Start
do_start
如果剩余的数据长度足够
collected=victim; //指向initram的文件头
victim+=110, this_header+=110, count-=110;
state = GotHeader
如果剩余的数据长度不够
下一步调用do_collect将这不足的数据存在临时buff中,return 1,即结束action的循环,
等有数据之后,当前状态为collect,即再调一次collect将不足的数据补齐,之后再进入下一状态
do_header
判断前6个字节是 "070701" (cpio的头标志)
解析header中的信息相应的全局变量中
获取下一个header的位置 next_header
state=SkipIt
判断文件是符号链接还是普通文件
符号: next_state=GotSymlink state=Collect
文件: collected指向文件名字符串, victim+=sizeof(name) this_header+=sizeof(name),
count-=sizeof(name) state=GotName
do_collect(符号连接)
将符号连接的名字memcpy到collect中(即symblink_buf中)
state = next_sate=GotSymlink
do_symlink
清路径上的文件,并在路径上建立符号连接
state = SkipIt; next_state = Reset;
do_name (若是文件,非符号连接)
判断是不是结尾
若路径上己有文件,则清除己有的文件
若是普通文件,则在该路径上创建文件类似于touch, state = CopyFile;
若是目录,则创建目录 state = SkipIt; next_state = Reset;
若是设备文件(或FIFO等)则创建 state = SkipIt; next_state = Reset;
do_copy
将文件拷贝到/路径下
do_skip
eat(next_header-this_header); state=Reset
do_reset
注:
1. 把文件拷贝到/下,有点不好理解:
其实这类似于上层应用程序调用open, write,close,其实也就是调用内核层的sys_open,sys_write, sys_close
2. 这儿的sys_write(wfd, victim, body_len);其实wfd与victim都是放在内存中,只不过wfd所代表的文件没有放在/的路径下,
sys_write就是把wfd写到/的指定路径下.
附录1. 关于unpack_to_rootfs中的三个buf
header_buf symlink_buf 与 name_buf的作用:
要知道 header_buf symlink_buf name_buf,这三个buf的作用需要对cpio的格式有所了解.
CPIO 的结构是这样的:
110字节的Head(6 + 8*13)
不定长的文件名(文件名的长度是olen)
结束字符 \0
文件的内容
.... //重复上面4个
最后的文件名是一个 TRAILER!!!
了解了cpio的格式之后就很容易知道:
1. header_buf就是一个文件在cpio中的head,共110个字节
header_buf = kmalloc(110, GFP_KERNEL);
2. symlink_buf就是符号链接所指的路径
假设有这样的符号连接: ln -s /home/cong/Desktop/record.txt link
symlink_buf="/home/cong/Desktop/record.txt"
3. name_buf 就是文件名(除符号链接之外的文件名),
可以是普通文件名,目录名,FIFO名等反正就是存名字
这些都很容易理解, 就是有一点需要注意: 什么时候会用到这些buffer?
以header_buf为例说明一下:
内核两次调用了unpack_to_rootfs,
第一次解压usr/initramfs_data.cpio,这时候head_buff压根就没有用到.
第二次是解压initramfs,这个rootfs是gzip压缩的,内核会一边解压缩initramfs一边把解压出来的文件填充到文件系统中.
解压后并不是每一次都把110个字节拷贝到header_buf中,这样没有必要
而是在gunzip中每次解压出的32K的buffer, 只有当这32k剩下的数据是cpio的头且不足110个字节时,才会用到header_buf.
先将不足的字节临时放在header_buf中,然后再调用gunzip,从解出32K的buffer中拿走剩下的字节填充到header_buf中凑満110个字节.
symlink_buf, name_buf同样也是这种情况下才使用.
附录2:关于宏__tagtable
lichee/linux-3.3/arch/arm/include/asm/setup.h
在/work/ct/lichee/linux-3.3/arch/arm/kernel/vmlinux.lds.S中标明了内始的起始位置:
ENTRY(stext)
a. 开始__lookup_processor_type,查找cpu的型号
b. __create_page_tables创建页表
c.把开启mmu后的跳转地址__mmap_switched放在r13中
d.__enable_mmu开启mmu
e. 执行 __mmap_switched
在/work/ct/lichee/linux-3.3/arch/arm/kernel/head-common.S中
a. 清bss
b. 设置sp
c. 将start_kernel的参数,保存一下
d. 跳到start_kernel中执行
一. initramfs的地址传递
1. 解析initramfs的大小与地址
从u-boot传给内核的参数中解析出initramfs在内存中的地址与大小
在arch/arm/mm/init.c中
- static int __init parse_tag_initrd2(const struct tag *tag)
- {
- //uboot传给内核的参数tag中保存了initramfs的起始地址(0x41000000)与大小
- phys_initrd_start = tag->u.initrd.start; //(物理地址)
- phys_initrd_size = tag->u.initrd.size;
- return 0;
- }
- __tagtable(ATAG_INITRD2, parse_tag_initrd2);
start_kernel
--> setup_arch
--> arm_memblock_init
在arch/arm/mm/init.c中
- void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc)
- {
- for (i = 0; i < mi->nr_banks; i++)
- memblock_add(mi->bank[i].start, mi->bank[i].size);
- memblock_reserve(__pa(_stext), _end - _stext); //将内核的代码段保留
- if (phys_initrd_size) {
- memblock_reserve(phys_initrd_start, phys_initrd_size); //将initramfs的内存保留
- initrd_start = __phys_to_virt(phys_initrd_start); //将物理地址转为虚所地址
- initrd_end = initrd_start + phys_initrd_size;
- }
- }
二.文件系统的建立
在调用initramfs之前,系统中己经建立了虚拟的根文件系统("/").
- start_kernel
- --> vfs_cache_init
- --> mnt_init
- --> init_rootfs(); //注册根文件系统这个类型
- --> init_mount_tree(); //创建根文件系统"/"
- --> rest_init //创建线程kernel_thread
- --> do_basic_setup
- --> populate_rootfs
- static int __init populate_rootfs(void)
- {
- unpack_to_rootfs(__initramfs_start, __initramfs_size); //2.1
- unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start); //2.2
- free_initrd(); //将initramfs保留的内存释放
- return 0;
- }
- rootfs_initcall(populate_rootfs);
2.1 第一次调用unpack_to_rootfs
其中第一个unpack_to_rootfs是把initramfs_data.cpio解出来
cong@dell:/work/ct/lichee$ ls -l ./linux-3.3/usr/initramfs_data.cpio
-rw-rw-r-- 1 cong cong 512 May 20 17:00 ./linux-3.3/usr/initramfs_data.cpio
initramfs_data.cpio是链接在内核中的,
其地址__initramfs_start是在./arch/arm/kernel/vmlinux.lds中定义 __initramfs_start = .
其大小__initramfs_size就是initramfs_data.cpio这个文件的大小
在linux-3.3/scripts/gen_initramfs_list.sh中
- default_initramfs() {
- cat <<-EOF >> ${output}
- # This is a very simple, default initramfs
- dir /dev 0755 0 0
- nod /dev/console 0600 0 0 c 5 1
- dir /root 0700 0 0
- # file /kinit usr/kinit/kinit 0755 0 0
- # slink /init kinit 0755 0 0
- EOF
- }
unpack_to_rootfs会把这些文件都释放到 / 下
2.2第二次调用unpack_to_rootfs
- static char * __init unpack_to_rootfs(char *buf, unsigned len)
- {
- int written, res;
- decompress_fn decompress;
- const char *compress_name;
- static __initdata char msg_buf[64];
- header_buf = kmalloc(110, GFP_KERNEL);
- symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL);
- name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL);
- state = Start;
- this_header = 0;
- message = NULL;
- while (!message && len) {
- loff_t saved_offset = this_header;
- if (*buf == '0' && !(this_header & 3)) { //通过判断buf[0]是否是字符'0',来判断是initramfs_data.cpio还是ramdisk.img
- state = Start; //如果是initramfs_datap.cpio己加载到内存且开头是"070701",buf[0]=='0'
- written = write_buffer(buf, len); //如果是ramdisk.img己加载到内存且开头是0x1f, 0x8b,gzip压缩的
- buf += written;
- len -= written;
- continue;
- }
- if (!*buf) {
- buf++;
- len--;
- this_header++;
- continue;
- }
- this_header = 0;
- decompress = decompress_method(buf, len, &compress_name); //根据buf[0],buf[1]查找表,得到压缩类型,及解压函数指针
- //调用解压函数进行解压,同时回调函数flush_buffer,会将刚解压出来的文件填充到"/"下.
- decompress(buf, len, NULL, flush_buffer, NULL, &my_inptr, error);
- this_header = saved_offset + my_inptr;
- buf += my_inptr;
- len -= my_inptr;
- }
- dir_utime();
- kfree(name_buf);
- kfree(symlink_buf);
- kfree(header_buf);
- return message;
- }
unpack_to_rootfs
--> write_buffer
--> while (!actions[state]())
count = len; //剩余initram的长度
victim = buf; //初始指向initram的首地址,解析时指向的位置依次递增
this_header = 0;
state = Start
do_start
如果剩余的数据长度足够
collected=victim; //指向initram的文件头
victim+=110, this_header+=110, count-=110;
state = GotHeader
如果剩余的数据长度不够
下一步调用do_collect将这不足的数据存在临时buff中,return 1,即结束action的循环,
等有数据之后,当前状态为collect,即再调一次collect将不足的数据补齐,之后再进入下一状态
do_header
判断前6个字节是 "070701" (cpio的头标志)
解析header中的信息相应的全局变量中
获取下一个header的位置 next_header
state=SkipIt
判断文件是符号链接还是普通文件
符号: next_state=GotSymlink state=Collect
文件: collected指向文件名字符串, victim+=sizeof(name) this_header+=sizeof(name),
count-=sizeof(name) state=GotName
do_collect(符号连接)
将符号连接的名字memcpy到collect中(即symblink_buf中)
state = next_sate=GotSymlink
do_symlink
清路径上的文件,并在路径上建立符号连接
state = SkipIt; next_state = Reset;
do_name (若是文件,非符号连接)
判断是不是结尾
若路径上己有文件,则清除己有的文件
若是普通文件,则在该路径上创建文件类似于touch, state = CopyFile;
若是目录,则创建目录 state = SkipIt; next_state = Reset;
若是设备文件(或FIFO等)则创建 state = SkipIt; next_state = Reset;
do_copy
将文件拷贝到/路径下
do_skip
eat(next_header-this_header); state=Reset
do_reset
注:
1. 把文件拷贝到/下,有点不好理解:
其实这类似于上层应用程序调用open, write,close,其实也就是调用内核层的sys_open,sys_write, sys_close
2. 这儿的sys_write(wfd, victim, body_len);其实wfd与victim都是放在内存中,只不过wfd所代表的文件没有放在/的路径下,
sys_write就是把wfd写到/的指定路径下.
附录1. 关于unpack_to_rootfs中的三个buf
header_buf symlink_buf 与 name_buf的作用:
要知道 header_buf symlink_buf name_buf,这三个buf的作用需要对cpio的格式有所了解.
CPIO 的结构是这样的:
110字节的Head(6 + 8*13)
不定长的文件名(文件名的长度是olen)
结束字符 \0
文件的内容
.... //重复上面4个
最后的文件名是一个 TRAILER!!!
了解了cpio的格式之后就很容易知道:
1. header_buf就是一个文件在cpio中的head,共110个字节
header_buf = kmalloc(110, GFP_KERNEL);
2. symlink_buf就是符号链接所指的路径
假设有这样的符号连接: ln -s /home/cong/Desktop/record.txt link
symlink_buf="/home/cong/Desktop/record.txt"
3. name_buf 就是文件名(除符号链接之外的文件名),
可以是普通文件名,目录名,FIFO名等反正就是存名字
这些都很容易理解, 就是有一点需要注意: 什么时候会用到这些buffer?
以header_buf为例说明一下:
内核两次调用了unpack_to_rootfs,
第一次解压usr/initramfs_data.cpio,这时候head_buff压根就没有用到.
第二次是解压initramfs,这个rootfs是gzip压缩的,内核会一边解压缩initramfs一边把解压出来的文件填充到文件系统中.
解压后并不是每一次都把110个字节拷贝到header_buf中,这样没有必要
而是在gunzip中每次解压出的32K的buffer, 只有当这32k剩下的数据是cpio的头且不足110个字节时,才会用到header_buf.
先将不足的字节临时放在header_buf中,然后再调用gunzip,从解出32K的buffer中拿走剩下的字节填充到header_buf中凑満110个字节.
symlink_buf, name_buf同样也是这种情况下才使用.
附录2:关于宏__tagtable
lichee/linux-3.3/arch/arm/include/asm/setup.h
0 0
- Cubietruck---9.Linux3.3_initramfs启动分析
- Cubietruck---10.Linux3.3_开机log的打印分析
- Cubietruck---11. Linux3.3_serail串口驱动分析
- cubietruck lubuntu启动信息分析
- linux3.0.1内核启动分析
- Cubietruck开发板SPL启动分析
- Cubietruck---18.input子系统启动流程分析
- CubieTruck(CubieBoard 3)从NAND启动Cubian
- cubietruck启动错误信息记录
- Cubietruck---25.android蓝牙分析3_search分析 2
- Linux3.2.8系统启动过程及启动延时初步分析
- Linux3.2.8系统启动过程及启动延时初步分析
- Linux3.2.8系统启动过程及启动延时初步分析
- Cubietruck---3. android编译命令分析
- Cubietruck---3. android编译命令分析
- Cubietruck---4. boot0源码流程简略分析
- Cubietruck---5. boot1源码流程简略分析
- Cubietruck---7. fex2bin源码简要分析(未完成)
- 关于预防流氓软件入侵的一点小心得
- AndroidManifest.xml中的minSdkVersion、targetSdkVersion、maxSdkVersion和project.properties中target API lev
- iOS Xcode项目在SVN更新后无法打开解决办法
- RTP协议全解(H264码流和PS流)
- MySQL各版本升级最佳实践
- Cubietruck---9.Linux3.3_initramfs启动分析
- 可恶的流氓软件、捆绑软件,今晚上有活干了
- 我在安全行业的成长踪迹
- 【bzoj3676】[Apio2014]回文串
- ios 常用公共方法
- JAVA自学之每日一题(二)
- java.net.SocketException: Unrecognized Windows Sockets error: 0: JVM_Bind 【 解决方案】
- 使用ContentProvider管理多媒体内容
- 重要提醒,万能五笔变身超级大流氓升级超级流氓团组团静默安装!