rk3188--4.android用initrd文件系统启动流程

本文深入探讨了Linux系统启动过程中initrd与initramfs的功能与工作原理,详细解释了如何解压并加载initrd与initramfs中的文件系统,包括initramfs的cpio打包与解包过程,以及initrd的gzip压缩与解压,最终实现文件系统的初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


在init/intramfs.c中
  1. static int __init populate_rootfs(void)
  2. {
  3.     unpack_to_rootfs(__initramfs_start, __initramfs_size) //1. initramfs的解压
  4.     if (initrd_start) {
  5.         unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start); //2.initrd的解压
  6.         free_initrd();
  7.     }
  8. }
  9. 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中
  1. static char * __init unpack_to_rootfs(char *buf, unsigned len)
  2. {
  3.     int i;
  4.     int written, res;
  5.     decompress_fn decompress;
  6.     const char *compress_name;
  7.     static __initdata char msg_buf[64];
  8.     header_buf = kmalloc(110, GFP_KERNEL);
  9.     symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL);
  10.     name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL);
  11.     state = Start;
  12.     this_header = 0;
  13.     message = NULL;
  14.     while (!message && len) {
  15.         loff_t saved_offset = this_header;
  16.         //如果开头以字符'0'开始,说明这是cpio格式的ram disk,不用解压直接用复制
  17.         if (*buf == '0' && !(this_header & 3)) {
  18.             state = Start;
  19.             written = write_buffer(buf, len);
  20.             buf += written;
  21.             len -= written;
  22.             continue;
  23.         }       
  24.     }
  25.     dir_utime();
  26.     kfree(name_buf);
  27.     kfree(symlink_buf);
  28.     kfree(header_buf);
  29.     return message;
  30. }
在initramfs.cpio中打包了3个文件(2个目录 1个字符设备文件):
        /dev   目录
               /dev/console 文件
        /root    目录
  1. dir /dev 0755 0 0
  2. nod /dev/console 0600 0 0 c 5 1
  3. dir /root 0700 0 0
下面来看一下它们是如何依次解出来的:
    buf=__initramfs_start, len=__initramfs_size
  1. static int __init write_buffer(char *buf, unsigned len)
  2. {
  3.     count = len;
  4.     victim = buf;

  5.     while (!actions[state]())
  6.         ;
  7.     return len - count;
  8. }
1.1 do_start
因为在initramfs.cpio的文件长度都为0,所以没有do_copy的过程
write_buffer
    --> do_start
在init/initramfs.c中
  1. static int __init do_start(void)
  2. {
  3.     //实际作用是将collect指针移动到打包的cpio每一个文件头处
  4.     read_into(header_buf, 110, GotHeader);
  5.     return 0;
  6. }
注意:这个名虽然叫read_into而且第一个参数又是buf,但实际上这个buf没有起到任何作用
  1. static void __init read_into(char *buf, unsigned size, enum state next)
  2. {
  3.     if (count >= size) {
  4.         collected = victim;
  5.         eat(size);
  6.         state = next;      //下一步要执行do_header
  7.     }
  8. }
1.2 do_header解析110字节的头
write_buffer

    --> do_start
        --> do_header
  1. static int __init do_header(void)
  2. {
  3.     if (memcmp(collected, "070707", 6)==0) {
  4.         error("incorrect cpio method used: use -H newc option");
  5.         return 1;
  6.     }
  7.     if (memcmp(collected, "070701", 6)) {
  8.         error("no cpio magic");
  9.         return 1;
  10.     }
  11.     parse_header(collected);      //从101个字节的头中解析出inod mode uid gid等
  12.     next_header = this_header + N_ALIGN(name_len) + body_len;   //移到下一个文件的头处
  13.     next_header = (next_header + 3) & ~3;              //cpio的头部都是4字节对齐的
  14.     state = SkipIt;
  15.     if (name_len <= 0 || name_len > PATH_MAX)
  16.         return 0;
  17.     if (S_ISLNK(mode)) {
  18.         if (body_len > PATH_MAX)
  19.             return 0;
  20.         collect = collected = symlink_buf;
  21.         remains = N_ALIGN(name_len) + body_len;
  22.         next_state = GotSymlink;
  23.         state = Collect;
  24.         return 0;
  25.     }
  26.     //注意下面这个 !body_len,目录的body_len为0设备文件的body_len也为0
  27.     //所以这儿代表的是,所有非链接文件
  28.     if (S_ISREG(mode) || !body_len)                 
  29.         read_into(name_buf, N_ALIGN(name_len), GotName);  //这个实际的作用是,将指针移动到下一个文件的头处
  30.     return 0;                                             //并将状态改为GotName,即要调用do_name
  31. }

1.3 do_name建立目录文件
write_buffer
    --> do_start
        --> do_header
            --> do_name
进行到此处,系统中己存在/与/root两个目录(都是虚拟的),此时再把打包在cpio里面的文件解析到系统的相应位置上.
  1. static int __init do_name(void)
  2. {
  3.     state = SkipIt;
  4.     next_state = Reset;
  5.     if (strcmp(collected, "TRAILER!!!") == 0) {   //判断是不是结尾
  6.         free_hash();
  7.         return 0;
  8.     }
  9.     clean_path(collected, mode);         //把原先有的路径去掉, 相当于rmdir /dev 或 rm /dev/console
  10.     if (S_ISREG(mode)) {                        
  11.         int ml = maybe_link();
  12.         if (ml >= 0) {
  13.             int openflags = O_WRONLY|O_CREAT;
  14.             if (ml != 1)
  15.                 openflags |= O_TRUNC;
  16.             wfd = sys_open(collected, openflags, mode);    //如果是普通文件打开sys_open

  17.             if (wfd >= 0) {
  18.                 sys_fchown(wfd, uid, gid);                 //设置权限等
  19.                 sys_fchmod(wfd, mode);
  20.                 if (body_len)
  21.                     sys_ftruncate(wfd, body_len);
  22.                 vcollected = kstrdup(collected, GFP_KERNEL);
  23.                 state = CopyFile;                         //最后调用do_copy将文件内容复制过来
  24.             }
  25.         }
  26.     } else if (S_ISDIR(mode)) {                      // 以/dev为例
  27.         sys_mkdir(collected, mode);                  // 创建 /dev目录
  28.         sys_chown(collected, uid, gid);              // 设置所有者
  29.         sys_chmod(collected, mode);                  // 设置权限
  30.         dir_add(collected, mtime);                   // 更改/dev目录的mtime
  31.     } else if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) {
  32.         if (maybe_link() == 0) {                     // 以/dev/console为例 
  33.             sys_mknod(collected, mode, rdev);        // 创建 /dev/console结点
  34.             sys_chown(collected, uid, gid);          // 设置所有者
  35.             sys_chmod(collected, mode);              // 设置权限
  36.             do_utime(collected, mtime);              // 更改时间戳
  37.         }
  38.     }
  39.     return 0;
  40. }
1.3 do_skip
write_buffer
    --> do_start
        --> do_header
            --> do_name
                --> do_skip
  1. static int __init do_skip(void)
  2. {
  3.     if (this_header + count < next_header) {
  4.         dbmsg();
  5.         eat(count);
  6.         return 1;
  7.     } else {
  8.         dbmsg();
  9.         eat(next_header - this_header);
  10.         state = next_state;
  11.         return 0;
  12.     }
  13. }
1.5 do_reset
write_buffer

    --> do_start
        --> do_header
            --> do_name
                --> do_skip
                    --> do_reset
  1. static int __init do_reset(void)
  2. {
  3.     dbmsg();
  4.     while(count && *victim == '\0')
  5.         eat(1);
  6.     if (count && (this_header & 3))
  7.         error("broken padding");
  8.     return 1;
  9. }
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中
  1. void __init setup_arch(char **cmdline_p)
  2. {
  3.     struct machine_desc *mdesc;
  4.     mdesc = setup_machine_fdt(__atags_pointer); 
  5.     if (!mdesc)
  6.         mdesc = setup_machine_tags(machine_arch_type);  //读取内核参数
  7.     //uboot的参数: init=/init initrd=0x62000000,0x00130000
  8.     //指定了initrd在内存的起始地址0x62000000,长度0x130000
  9.     parse_early_param();  
  10.     arm_memblock_init(&meminfo, mdesc); //将物理地址转为虚地址
  11. }
start_kernel
    -->setup_arch
        --> arm_memblock_init
在arch/arm/mm/init.c中
  1. void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc)
  2. {
  3. #ifdef CONFIG_BLK_DEV_INITRD
  4.     if (phys_initrd_size) {
  5.         memblock_reserve(phys_initrd_start, phys_initrd_size);
  6.         initrd_start = __phys_to_virt(phys_initrd_start);     //将物理地址0x62000000转为虚地址
  7.         initrd_end = initrd_start + phys_initrd_size;         //end地址+size=0x00130000  
  8.     }
  9. #endif
  10. }
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中
  1. static char * __init unpack_to_rootfs(char *buf, unsigned len)
  2. {
  3.     int i;
  4.     int written, res;
  5.     decompress_fn decompress;
  6.     const char *compress_name;
  7.     static __initdata char msg_buf[64];
  8.     header_buf = kmalloc(110, GFP_KERNEL);
  9.     symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL);
  10.     name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL);

  11.     if (!header_buf || !symlink_buf || !name_buf)
  12.         panic("can't allocate buffers");

  13.     state = Start;
  14.     this_header = 0;
  15.     message = NULL;
  16.     while (!message && len) {
  17.         loff_t saved_offset = this_header;
  18.         if (*buf == '0' && !(this_header & 3)) {
  19.             //不是cpio格式,zip压缩过的开头不为字符'0'
  20.             continue;
  21.         }
  22.         this_header = 0;
  23.         //以开头的0x1f, 0x8b判断是zip压缩的,找到gunzip
  24.         decompress = decompress_method(buf, len, &compress_name);
  25.         //调用压缩函数进行解压缩,解压后调用flush_buffer拷贝到各个目录
  26.         decompress(buf, len, NULL, flush_buffer, NULL, &my_inptr, error);       
  27.         this_header = saved_offset + my_inptr;
  28.         buf += my_inptr;
  29.         len -= my_inptr;
  30.     }
  31.     dir_utime();
  32.     kfree(name_buf);
  33.     kfree(symlink_buf);
  34.     kfree(header_buf);
  35.     return message;
  36. }
do_start
do_header
do_name
do_copy
do_utime
do_skip
do_reset
这儿的wite_buffer,比initramfs的write_buffer多了一个do_copy的过程
因为initramfs中只有名,没有数据.initrd有数据,所以需要将数据复制过去.
  1. static int __init flush_buffer(void *bufv, unsigned len)
  2. {
  3.     char *buf = (char *) bufv;
  4.     int written;
  5.     int origLen = len;
  6.     if (message)
  7.         return -1;
  8.     while ((written = write_buffer(buf, len)) < len && !message) {
  9.         char c = buf[written];
  10.         if (c == '0') {
  11.             buf += written;
  12.             len -= written;
  13.             state = Start;
  14.         } else if (c == 0) {
  15.             buf += written;
  16.             len -= written;
  17.             state = Reset;
  18.         } else
  19.             error("junk in compressed archive");
  20.     }
  21.     return origLen;
  22. }
<think>我们正在讨论如何检查 `initrd-switch-root.service` 的状态。根据用户的问题,他们可能遇到了系统启动或服务相关的问题,并希望了解如何查看该服务的状态详情。 在Linux系统中,使用systemd作为初始化系统时,我们可以使用`systemctl status`命令来查看服务的状态。但是,需要注意的是,`initrd-switch-root.service`是一个在初始内存磁盘(initrd)阶段运行的服务,它在系统启动的早期执行,并且一旦系统切换到真实的根文件系统,该服务就会退出。因此,在正常的系统运行环境中,我们可能无法直接查看到该服务的运行状态,因为它已经完成了任务并退出了。 然而,我们可以尝试以下方法来获取相关信息: 1. **使用`systemctl status`命令**:尽管该服务可能已经退出,但systemd可能仍然保留了一些状态信息。我们可以尝试运行: ```bash systemctl status initrd-switch-root.service ``` 2. **查看日志**:由于该服务在启动早期运行,我们可以使用`journalctl`命令来查看系统日志,特别是与该服务相关的日志。我们可以通过服务名称来过滤日志: ```bash journalctl -u initrd-switch-root.service ``` 3. **使用`-b`选项查看本次启动的日志**:为了只查看当前启动的日志,我们可以添加`-b`选项: ```bash journalctl -u initrd-switch-root.service -b ``` 4. **如果服务已经退出,状态可能显示为inactive**:这是正常现象,因为该服务仅在initrd阶段运行,然后退出。 5. **查看服务的详细信息**:我们可以使用`systemctl show`命令来查看该服务的属性: ```bash systemctl show initrd-switch-root.service ``` 6. **检查服务文件**:查看该服务的单元文件,了解其配置和依赖关系: ```bash systemctl cat initrd-switch-root.service ``` 注意:`initrd-switch-root.service`是一个特殊的服务,它负责从initrd切换到真实的根文件系统。在切换完成后,该服务就没有必要继续运行了。 如果用户遇到了启动问题,可能还需要检查其他相关服务的日志,比如`systemd-journald`服务是否正常启动,以确保日志被正确记录。 另外,根据引用[1]中的描述,`initrd-switch-root.service`的作用是切换到不同的根目录并在其下执行新的系统管理器进程。它通常在initrd的环境下运行,然后过渡到主系统的systemd进程(PID 1)。如果该服务执行失败,可能会导致系统无法正常启动。 因此,如果用户无法启动系统,可能需要进入救援模式或使用Live CD,然后挂载根文件系统,再查看日志文件(通常在`/var/log/journal`目录下)来获取更多信息。 最后,根据引用[2],在QEMU虚拟机中,我们可以使用`-initrd`参数指定initrd文件,使用`-append`指定内核参数,这些参数可能会影响initrd阶段的行为,包括`initrd-switch-root.service`的执行。 综上所述,我们提供了多种方法来检查`initrd-switch-root.service`的状态和日志,以帮助用户诊断问题。 §§相关问题§§ 1. 如何查看系统启动过程中的所有服务日志? 2. 如果系统无法启动,如何检查initrd阶段的日志? 3. `initrd-switch-root.service`服务失败的可能原因有哪些? 4. 如何调试系统启动过程中的服务失败问题?</think>### 如何检查 `initrd-switch-root.service` 的状态 在 Linux 系统上使用 `systemd` 时,可以通过以下步骤检查 `initrd-switch-root.service` 的状态: #### 1. **查看服务状态** ```bash systemctl status initrd-switch-root.service ``` - **输出说明**: - `Active: inactive (dead)` 是正常状态,因为该服务仅在系统启动时执行一次,完成根目录切换后会立即退出[^1]。 - 若显示错误(如 `failed`),表明切换根目录过程可能失败。 #### 2. **查看启动日志** ```bash journalctl -u initrd-switch-root.service -b ``` - **作用**:显示本次启动中该服务的详细日志,包括: - 根目录切换的目标路径(如 `/sysroot`) - 是否成功找到新的 `systemd` 管理器进程 - 错误信息(如挂载失败或路径错误)[^1][^2]。 #### 3. **查看服务详情** ```bash systemctl show initrd-switch-root.service ``` - **输出内容**:服务的配置参数、依赖关系、退出状态码等。 #### 4. **检查服务定义** ```bash systemctl cat initrd-switch-root.service ``` - **作用**:查看服务的单元文件(unit file),确认其执行逻辑和参数配置。 --- ### 常见问题分析 1. **服务显示 `inactive (dead)`** 这是预期行为,因为该服务仅在启动时短暂运行,完成后会自动退出。 2. **服务启动失败的可能原因**: - **路径错误**:指定的根目录(如 `/sysroot`)或 `systemd` 二进制文件不存在。 - **挂载问题**:目标根目录挂载失败(如磁盘损坏或驱动缺失)。 - **内核参数错误**:`initrd` 或根目录参数配置错误(参考 `-initrd` 和 `-append` 内核启动参数)[^2]。 --- ### 示例输出解析 ```bash ● initrd-switch-root.service - Switch Root Loaded: loaded (/usr/lib/systemd/system/initrd-switch-root.service; static) Active: inactive (dead) since Tue 2023-10-10 09:00:00 UTC; 5min ago Docs: man:systemd-initrd-switch-root.service(8) Process: 123 ExecStart=/usr/lib/systemd/systemd-initrd-switch-root /sysroot (code=exited, status=0/SUCCESS) Main PID: 123 (code=exited, status=0/SUCCESS) ``` - **关键信息**:`Process: 123 ExecStart=...` 显示执行路径和退出状态(`status=0/SUCCESS` 表示成功)。 --- ### 调试建议 若系统启动失败,需检查: 1. **内核日志**:使用 `dmesg | grep -i initrd` 查看启动阶段错误。 2. **Initrd 内容**:解压 `initrd` 镜像确认文件完整性: ```bash mkdir /tmp/initrd && cd /tmp/initrd zcat /boot/initrd.img-$(uname -r) | cpio -idmv ls -l usr/lib/systemd/system/initrd-switch-root.service ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值