在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); // 设置所有者
-
sys_chmod(collected, mode); // 设置权限
-
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;
-
}
阅读(5085) | 评论(0) | 转发(1) |