rk3188--4.android用initrd文件系统启动流程
2015-08-28 11:12
597 查看
在init/intramfs.c中
static
int __init populate_rootfs(void)
{
unpack_to_rootfs(__initramfs_start, __initramfs_size);
//1. initramfs的解压
if
(initrd_start) {
unpack_to_rootfs((char
*)initrd_start, initrd_end
- initrd_start);
//2.initrd的解压
free_initrd();
}
}
rootfs_initcall(populate_rootfs);
//这个相当于module_init在系统初始化时会调用
1. initramfs的解压
unpack_to_rootfs(__initramfs_start, __initramfs_size);
rootfs_initcall(populate_rootfs);
--> populate_rootfs
--> unpack_to_rootfs
在init/initramfs.c中
static char
* __init unpack_to_rootfs(char
*buf, unsigned
len)
{
int i;
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;
//如果开头以字符'0'开始,说明这是cpio格式的ram disk,不用解压直接用复制
if
(*buf
==
'0'
&&
!(this_header
& 3))
{
state
= Start;
written
= write_buffer(buf,
len);
buf
+= written;
len
-= written;
continue;
}
}
dir_utime();
kfree(name_buf);
kfree(symlink_buf);
kfree(header_buf);
return message;
}
在initramfs.cpio中打包了3个文件(2个目录 1个字符设备文件):
/dev 目录
/dev/console 文件
/root 目录
dir
/dev 0755 0 0
nod /dev/console 0600 0 0 c 5 1
dir /root 0700 0 0
下面来看一下它们是如何依次解出来的:
buf=__initramfs_start, len=__initramfs_size
static
int __init write_buffer(char
*buf, unsigned
len)
{
count =
len;
victim = buf;
while
(!actions[state]())
;
return len
- count;
}
1.1 do_start
因为在initramfs.cpio的文件长度都为0,所以没有do_copy的过程
write_buffer
--> do_start
在init/initramfs.c中
static
int __init do_start(void)
{
//实际作用是将collect指针移动到打包的cpio每一个文件头处
read_into(header_buf, 110, GotHeader);
return 0;
}
注意:这个名虽然叫read_into而且第一个参数又是buf,但实际上这个buf没有起到任何作用
static void __init read_into(char
*buf, unsigned size, enum state
next)
{
if
(count >= size)
{
collected
= victim;
eat(size);
state =
next;
//下一步要执行do_header
}
}
1.2 do_header解析110字节的头
write_buffer
--> do_start
--> do_header
static
int __init do_header(void)
{
if
(memcmp(collected,
"070707", 6)==0)
{
error("incorrect cpio method used: use -H newc option");
return 1;
}
if
(memcmp(collected,
"070701", 6))
{
error("no cpio magic");
return 1;
}
parse_header(collected);
//从101个字节的头中解析出inod mode uid gid等
next_header
= this_header + N_ALIGN(name_len)
+ body_len;
//移到下一个文件的头处
next_header
= (next_header
+ 3)
& ~3; //cpio的头部都是4字节对齐的
state = SkipIt;
if
(name_len <= 0
|| name_len
> PATH_MAX)
return 0;
if
(S_ISLNK(mode))
{
if
(body_len
> PATH_MAX)
return 0;
collect
= collected = symlink_buf;
remains
= N_ALIGN(name_len)
+ body_len;
next_state
= GotSymlink;
state = Collect;
return 0;
}
//注意下面这个 !body_len,目录的body_len为0设备文件的body_len也为0
//所以这儿代表的是,所有非链接文件
if
(S_ISREG(mode)
||
!body_len)
read_into(name_buf, N_ALIGN(name_len),
GotName);
//这个实际的作用是,将指针移动到下一个文件的头处
return 0;
//并将状态改为GotName,即要调用do_name
}
1.3 do_name建立目录文件
write_buffer
--> do_start
--> do_header
--> do_name
进行到此处,系统中己存在/与/root两个目录(都是虚拟的),此时再把打包在cpio里面的文件解析到系统的相应位置上.
static
int __init do_name(void)
{
state = SkipIt;
next_state
= Reset;
if
(strcmp(collected,
"TRAILER!!!")
== 0)
{ //判断是不是结尾
free_hash();
return 0;
}
clean_path(collected, mode);
//把原先有的路径去掉, 相当于rmdir /dev 或 rm /dev/console
if
(S_ISREG(mode))
{
int ml
= maybe_link();
if
(ml >= 0)
{
int openflags
= O_WRONLY|O_CREAT;
if
(ml != 1)
openflags
|= O_TRUNC;
wfd
= sys_open(collected, openflags, mode);
//如果是普通文件打开sys_open
if
(wfd >= 0)
{
sys_fchown(wfd, uid, gid);
//设置权限等
sys_fchmod(wfd, mode);
if
(body_len)
sys_ftruncate(wfd, body_len);
vcollected
= kstrdup(collected, GFP_KERNEL);
state
= CopyFile; //最后调用do_copy将文件内容复制过来
}
}
}
else if
(S_ISDIR(mode))
{ // 以/dev为例
sys_mkdir(collected, mode);
// 创建 /dev目录
sys_chown(collected, uid, gid);
// 设置所有者
sys_chmod(collected, mode);
// 设置权限
dir_add(collected, mtime);
// 更改/dev目录的mtime
}
else if
(S_ISBLK(mode)
|| S_ISCHR(mode)
|| S_ISFIFO(mode)
|| S_ISSOCK(mode))
{
if
(maybe_link()
== 0)
{ // 以/dev/console为例
sys_mknod(collected, mode, rdev);
// 创建 /dev/console结点
sys_chown(collected, uid, gid);
//
[b]设置所有者[/b]
sys_chmod(collected, mode);
//
[b]设置权限[/b]
do_utime(collected, mtime); //
更改时间戳
}
}
return 0;
}
1.3 do_skip
write_buffer
--> do_start
--> do_header
--> do_name
--> do_skip
static
int __init do_skip(void)
{
if
(this_header + count
< next_header)
{
dbmsg();
eat(count);
return 1;
}
else {
dbmsg();
eat(next_header
- this_header);
state = next_state;
return 0;
}
}
1.5 do_reset
write_buffer
--> do_start
--> do_header
--> do_name
--> do_skip
--> do_reset
static
int __init do_reset(void)
{
dbmsg();
while(count
&&
*victim
==
'\0')
eat(1);
if
(count &&
(this_header
& 3))
error("broken padding");
return 1;
}
2.initrd的解压
2.1 initrd的起始地址的获取
make menuconfig中
General setup --->
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
start_kernel
-->setup_arch
在arch/arm/kernel/setup.c中
void __init setup_arch(char
**cmdline_p)
{
struct machine_desc
*mdesc;
mdesc = setup_machine_fdt(__atags_pointer);
if
(!mdesc)
mdesc = setup_machine_tags(machine_arch_type); //读取内核参数
//uboot的参数: init=/init initrd=0x62000000,0x00130000
//指定了initrd在内存的起始地址0x62000000,长度0x130000
parse_early_param();
arm_memblock_init(&meminfo, mdesc);
//将物理地址转为虚地址
}
start_kernel
-->setup_arch
--> arm_memblock_init
在arch/arm/mm/init.c中
void __init arm_memblock_init(struct meminfo
*mi, struct machine_desc
*mdesc)
{
#ifdef CONFIG_BLK_DEV_INITRD
if
(phys_initrd_size)
{
memblock_reserve(phys_initrd_start, phys_initrd_size);
initrd_start
= __phys_to_virt(phys_initrd_start);
//将物理地址0x62000000转为虚地址
initrd_end
= initrd_start + phys_initrd_size;
//end地址+size=0x00130000
}
#endif
}
unpack_to_rootfs((char
*)initrd_start, initrd_end
- initrd_start);
其中initrd_start是uboot传入的参数0x62000000的虚地址
里面的内容是烧入板子的boot.img去掉头8字节与尾4个字节,即out/target/product/rk3188/ramdisk.img
注: boot.img的生成
目录out/target/product/rk30sdk/root存在
a.将root下的每个文件加上cpio头+每个文件的内容,打包成cpios格式
b. 将这个cpio文件用gzip压缩后写到文件ramdisk.img中
c. mkkrnlimg会对ramdisk.img加上8个字节的头标志,尾部加上4个字节
2.2 解压并释放initrd中的文件目录
rootfs_initcall(populate_rootfs);
--> populate_rootfs
--> unpack_to_rootfs
在init/initramfs.c中
static char
* __init unpack_to_rootfs(char
*buf, unsigned
len)
{
int i;
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);
if
(!header_buf
||
!symlink_buf ||
!name_buf)
panic("can't allocate buffers");
state = Start;
this_header
= 0;
message =
NULL;
while
(!message
&&
len)
{
loff_t saved_offset
= this_header;
if
(*buf
==
'0'
&&
!(this_header
& 3))
{
//不是cpio格式,zip压缩过的开头不为字符'0'
continue;
}
this_header
= 0;
//以开头的0x1f, 0x8b判断是zip压缩的,找到gunzip
decompress
= decompress_method(buf,
len,
&compress_name);
//调用压缩函数进行解压缩,解压后调用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;
}
do_start
do_header
do_name
do_copy
do_utime
do_skip
do_reset
这儿的wite_buffer,比initramfs的write_buffer多了一个do_copy的过程
因为initramfs中只有名,没有数据.initrd有数据,所以需要将数据复制过去.
static
int __init flush_buffer(void
*bufv, unsigned
len)
{
char *buf
= (char
*) bufv;
int written;
int origLen
= len;
if
(message)
return
-1;
while
((written
= write_buffer(buf,
len))
< len
&&
!message)
{
char c
= buf[written];
if
(c ==
'0')
{
buf
+= written;
len
-= written;
state
= Start;
}
else if
(c == 0)
{
buf
+= written;
len
-= written;
state
= Reset;
}
else
error("junk in compressed archive");
}
return origLen;
}
在init/intramfs.c中
static
int __init populate_rootfs(void)
{
unpack_to_rootfs(__initramfs_start, __initramfs_size);
//1. initramfs的解压
if
(initrd_start) {
unpack_to_rootfs((char
*)initrd_start, initrd_end
- initrd_start);
//2.initrd的解压
free_initrd();
}
}
rootfs_initcall(populate_rootfs);
//这个相当于module_init在系统初始化时会调用
1. initramfs的解压
unpack_to_rootfs(__initramfs_start, __initramfs_size);
rootfs_initcall(populate_rootfs);
--> populate_rootfs
--> unpack_to_rootfs
在init/initramfs.c中
static char
* __init unpack_to_rootfs(char
*buf, unsigned
len)
{
int i;
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;
//如果开头以字符'0'开始,说明这是cpio格式的ram disk,不用解压直接用复制
if
(*buf
==
'0'
&&
!(this_header
& 3))
{
state
= Start;
written
= write_buffer(buf,
len);
buf
+= written;
len
-= written;
continue;
}
}
dir_utime();
kfree(name_buf);
kfree(symlink_buf);
kfree(header_buf);
return message;
}
在initramfs.cpio中打包了3个文件(2个目录 1个字符设备文件):
/dev 目录
/dev/console 文件
/root 目录
dir
/dev 0755 0 0
nod /dev/console 0600 0 0 c 5 1
dir /root 0700 0 0
下面来看一下它们是如何依次解出来的:
buf=__initramfs_start, len=__initramfs_size
static
int __init write_buffer(char
*buf, unsigned
len)
{
count =
len;
victim = buf;
while
(!actions[state]())
;
return len
- count;
}
1.1 do_start
因为在initramfs.cpio的文件长度都为0,所以没有do_copy的过程
write_buffer
--> do_start
在init/initramfs.c中
static
int __init do_start(void)
{
//实际作用是将collect指针移动到打包的cpio每一个文件头处
read_into(header_buf, 110, GotHeader);
return 0;
}
注意:这个名虽然叫read_into而且第一个参数又是buf,但实际上这个buf没有起到任何作用
static void __init read_into(char
*buf, unsigned size, enum state
next)
{
if
(count >= size)
{
collected
= victim;
eat(size);
state =
next;
//下一步要执行do_header
}
}
1.2 do_header解析110字节的头
write_buffer
--> do_start
--> do_header
static
int __init do_header(void)
{
if
(memcmp(collected,
"070707", 6)==0)
{
error("incorrect cpio method used: use -H newc option");
return 1;
}
if
(memcmp(collected,
"070701", 6))
{
error("no cpio magic");
return 1;
}
parse_header(collected);
//从101个字节的头中解析出inod mode uid gid等
next_header
= this_header + N_ALIGN(name_len)
+ body_len;
//移到下一个文件的头处
next_header
= (next_header
+ 3)
& ~3; //cpio的头部都是4字节对齐的
state = SkipIt;
if
(name_len <= 0
|| name_len
> PATH_MAX)
return 0;
if
(S_ISLNK(mode))
{
if
(body_len
> PATH_MAX)
return 0;
collect
= collected = symlink_buf;
remains
= N_ALIGN(name_len)
+ body_len;
next_state
= GotSymlink;
state = Collect;
return 0;
}
//注意下面这个 !body_len,目录的body_len为0设备文件的body_len也为0
//所以这儿代表的是,所有非链接文件
if
(S_ISREG(mode)
||
!body_len)
read_into(name_buf, N_ALIGN(name_len),
GotName);
//这个实际的作用是,将指针移动到下一个文件的头处
return 0;
//并将状态改为GotName,即要调用do_name
}
1.3 do_name建立目录文件
write_buffer
--> do_start
--> do_header
--> do_name
进行到此处,系统中己存在/与/root两个目录(都是虚拟的),此时再把打包在cpio里面的文件解析到系统的相应位置上.
static
int __init do_name(void)
{
state = SkipIt;
next_state
= Reset;
if
(strcmp(collected,
"TRAILER!!!")
== 0)
{ //判断是不是结尾
free_hash();
return 0;
}
clean_path(collected, mode);
//把原先有的路径去掉, 相当于rmdir /dev 或 rm /dev/console
if
(S_ISREG(mode))
{
int ml
= maybe_link();
if
(ml >= 0)
{
int openflags
= O_WRONLY|O_CREAT;
if
(ml != 1)
openflags
|= O_TRUNC;
wfd
= sys_open(collected, openflags, mode);
//如果是普通文件打开sys_open
if
(wfd >= 0)
{
sys_fchown(wfd, uid, gid);
//设置权限等
sys_fchmod(wfd, mode);
if
(body_len)
sys_ftruncate(wfd, body_len);
vcollected
= kstrdup(collected, GFP_KERNEL);
state
= CopyFile; //最后调用do_copy将文件内容复制过来
}
}
}
else if
(S_ISDIR(mode))
{ // 以/dev为例
sys_mkdir(collected, mode);
// 创建 /dev目录
sys_chown(collected, uid, gid);
// 设置所有者
sys_chmod(collected, mode);
// 设置权限
dir_add(collected, mtime);
// 更改/dev目录的mtime
}
else if
(S_ISBLK(mode)
|| S_ISCHR(mode)
|| S_ISFIFO(mode)
|| S_ISSOCK(mode))
{
if
(maybe_link()
== 0)
{ // 以/dev/console为例
sys_mknod(collected, mode, rdev);
// 创建 /dev/console结点
sys_chown(collected, uid, gid);
//
[b]设置所有者[/b]
sys_chmod(collected, mode);
//
[b]设置权限[/b]
do_utime(collected, mtime); //
更改时间戳
}
}
return 0;
}
1.3 do_skip
write_buffer
--> do_start
--> do_header
--> do_name
--> do_skip
static
int __init do_skip(void)
{
if
(this_header + count
< next_header)
{
dbmsg();
eat(count);
return 1;
}
else {
dbmsg();
eat(next_header
- this_header);
state = next_state;
return 0;
}
}
1.5 do_reset
write_buffer
--> do_start
--> do_header
--> do_name
--> do_skip
--> do_reset
static
int __init do_reset(void)
{
dbmsg();
while(count
&&
*victim
==
'\0')
eat(1);
if
(count &&
(this_header
& 3))
error("broken padding");
return 1;
}
2.initrd的解压
2.1 initrd的起始地址的获取
make menuconfig中
General setup --->
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
start_kernel
-->setup_arch
在arch/arm/kernel/setup.c中
void __init setup_arch(char
**cmdline_p)
{
struct machine_desc
*mdesc;
mdesc = setup_machine_fdt(__atags_pointer);
if
(!mdesc)
mdesc = setup_machine_tags(machine_arch_type); //读取内核参数
//uboot的参数: init=/init initrd=0x62000000,0x00130000
//指定了initrd在内存的起始地址0x62000000,长度0x130000
parse_early_param();
arm_memblock_init(&meminfo, mdesc);
//将物理地址转为虚地址
}
start_kernel
-->setup_arch
--> arm_memblock_init
在arch/arm/mm/init.c中
void __init arm_memblock_init(struct meminfo
*mi, struct machine_desc
*mdesc)
{
#ifdef CONFIG_BLK_DEV_INITRD
if
(phys_initrd_size)
{
memblock_reserve(phys_initrd_start, phys_initrd_size);
initrd_start
= __phys_to_virt(phys_initrd_start);
//将物理地址0x62000000转为虚地址
initrd_end
= initrd_start + phys_initrd_size;
//end地址+size=0x00130000
}
#endif
}
unpack_to_rootfs((char
*)initrd_start, initrd_end
- initrd_start);
其中initrd_start是uboot传入的参数0x62000000的虚地址
里面的内容是烧入板子的boot.img去掉头8字节与尾4个字节,即out/target/product/rk3188/ramdisk.img
注: boot.img的生成
目录out/target/product/rk30sdk/root存在
a.将root下的每个文件加上cpio头+每个文件的内容,打包成cpios格式
b. 将这个cpio文件用gzip压缩后写到文件ramdisk.img中
c. mkkrnlimg会对ramdisk.img加上8个字节的头标志,尾部加上4个字节
2.2 解压并释放initrd中的文件目录
rootfs_initcall(populate_rootfs);
--> populate_rootfs
--> unpack_to_rootfs
在init/initramfs.c中
static char
* __init unpack_to_rootfs(char
*buf, unsigned
len)
{
int i;
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);
if
(!header_buf
||
!symlink_buf ||
!name_buf)
panic("can't allocate buffers");
state = Start;
this_header
= 0;
message =
NULL;
while
(!message
&&
len)
{
loff_t saved_offset
= this_header;
if
(*buf
==
'0'
&&
!(this_header
& 3))
{
//不是cpio格式,zip压缩过的开头不为字符'0'
continue;
}
this_header
= 0;
//以开头的0x1f, 0x8b判断是zip压缩的,找到gunzip
decompress
= decompress_method(buf,
len,
&compress_name);
//调用压缩函数进行解压缩,解压后调用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;
}
do_start
do_header
do_name
do_copy
do_utime
do_skip
do_reset
这儿的wite_buffer,比initramfs的write_buffer多了一个do_copy的过程
因为initramfs中只有名,没有数据.initrd有数据,所以需要将数据复制过去.
static
int __init flush_buffer(void
*bufv, unsigned
len)
{
char *buf
= (char
*) bufv;
int written;
int origLen
= len;
if
(message)
return
-1;
while
((written
= write_buffer(buf,
len))
< len
&&
!message)
{
char c
= buf[written];
if
(c ==
'0')
{
buf
+= written;
len
-= written;
state
= Start;
}
else if
(c == 0)
{
buf
+= written;
len
-= written;
state
= Reset;
}
else
error("junk in compressed archive");
}
return origLen;
}
相关文章推荐
- Java和Android中,代码块、static静态代码块的执行顺序
- android 按钮的文字显示不全
- Android基于各官方API实现分享功能(不使用第三方集成工具)
- android——获取ImageView上面显示的图片bitmap对象
- android aidl 及binder 框架(一) Parcelable
- android中activity,window,view之间的关系
- Android性能优化之使用SparseArray代替HashMap
- Android Fragment——创建Fragment
- [转载]正确使用Android性能分析工具——TraceView
- Android学习笔记之Adapter
- androidstudio启动的时候报错,启动不了
- Android常用框架
- Android 学习笔记
- android handler 内存泄露
- ActiveAndroid数据库框架简单应用和配置
- android AsyncTask和Handler对比
- 详解 Android 的 Activity 组件
- Leo仿【DOTA视频站】项目实践【三】---- 获取DOTA2视频已经XListView实现上拉加载更多、下拉刷新
- Android - 文件读写操作 总结
- Android之——史上最简单旋转菜单实现效果