inird 导致系统挂掉down机问题
目前正在调试initrd,向其中添加initrd添加一些必要的动态库以及系统命令;使用chroot 命令测试也可以用。
但是放在设备上就是不能起来!
5.074563] registered taskstats version 1 [ 5.079539] Freeing unused kernel memory: 536k freed [ 5.084648] Failed to execute /init [ 5.088243] Kernel panic - not syncing: No init found. Try passing init= option to kernel. See Linux Documentation/init.txt for guidance. [ 5.100858] Pid: 1, comm: swapper Not tainted 2.6.39-gentoo-r3-nsfocus #19 [ 5.107865] Call Trace: [ 5.110352] [<ffffffff815f38eb>] panic+0x8c/0x19f [ 5.115211] [<ffffffff81002106>] init_post+0xbf/0xbf [ 5.120352] [<ffffffff819f4c4d>] kernel_init+0x153/0x15a [ 5.125864] [<ffffffff815f6fa4>] kernel_thread_helper+0x4/0x10 [ 5.131895] [<ffffffff819f4afa>] ? start_kernel+0x375/0x375 [ 5.137666] [<ffffffff815f6fa0>] ? gs_change+0x13/0x13
没有找到init进程,但是不对啊!!
chroot都能运行,也是有init进程的,应该是解压的问题了,怀疑是rootfs内存空间问题,于是将initrd.gz文件继续裁剪为5M就好了!!
但是当initrd.gz为36M时,还是存在问题,同时修改了ramdisk_size为 100M大小,还是存在问题!
分析 drivers/block/brd.c: 中的代码
unsigned long rd_size = CONFIG_BLK_DEV_RAM_SIZE; module_param(rd_size, ulong, 0444); MODULE_PARM_DESC(rd_size, "Size of each RAM disk in kbytes."); [...] /* Legacy boot options - nonmodular */ static int __init ramdisk_size(char *str) { rd_size = simple_strtol(str, NULL, 0); return 1; } __setup("ramdisk_size=", ramdisk_size);
ramdisk_size有个默认值;其在make menuconfig的时候可以设置;目前默认是16M大小;但是按道理
kernel /boot/bzImage loglevel=7 ramdisk_size=512000
这个内存应该是足够的啊!!为啥还有问题呢? 难道是设置为512M太大了不能分配??
来分析一下:
首先弄清楚initrd initramfs的区别
initrd:
cpio initrd (cpio包的gzip压缩文件)
内核将cpio initrd(由bootloader 将cpio initrd加载到内存) 释放到rootfs(/),结束内核对cpio initrd的操作
cpio initrd : bios->grub-kernel>cpio initrd(加载访问real rootfs的必备驱动等)->/init 脚本(加载real rootfs,并启动了init进程(/sbin/init)
initramfs:
作用和initrd类似,只是会和内核编译在一起,initramfs是经历过gzip压缩后的cpio格式数据文件,该文件被连接到内核的特殊数据段。
(内核+cpio包编译在一起然后一起进行内核压缩)内核文件包含了的一个cpio归档文件,该归档文件可能被外部的一个cpio包替换由initramfs里的/init 挂真实的根文件并启动init进程/sbin/init
initramfs和cpio-initrd的区别, initramfs是将cpio rootfs编译进内核一起压缩,而 cpio-initrd中cpio rootfs是不编译入内核,是外部
initramfs存放在哪里?
从vmlinux.lds.h文件可知,ramfs根据CONFIG_BLK_DEV_INITRD定义是否使用。INIT_RAM_FS存放ramfs相关内容,包括.init.ramfs和.init.ramfs.info两个段。
#define INIT_DATA_SECTION(initsetup_align) \ .init.data : AT(ADDR(.init.data) - LOAD_OFFSET) { \ INIT_DATA \ INIT_SETUP(initsetup_align) \ INIT_CALLS \ CON_INITCALL \ SECURITY_INITCALL \ INIT_RAM_FS \ } #ifdef CONFIG_BLK_DEV_INITRD #define INIT_RAM_FS \ . = ALIGN(4); \ VMLINUX_SYMBOL(__initramfs_start) = .; \ *(.init.ramfs) \ . = ALIGN(8); \ *(.init.ramfs.info) #else #define INIT_RAM_FS #endif
.init.ramfs和.init.ramfs.info两个段在initramfs_data.S中定义。
.section .init.ramfs,"a" __irf_start: .incbin __stringify(INITRAMFS_IMAGE)------------------原封不动的将INITRAMFS_IMAGE对应的二进制文件编译到当前文件中。 __irf_end: .section .init.ramfs.info,"a" .globl VMLINUX_SYMBOL(__initramfs_size) VMLINUX_SYMBOL(__initramfs_size): #ifdef CONFIG_64BIT .quad __irf_end - __irf_start #else .long __irf_end - __irf_start #endif
INITRAMFS_IMAGE从哪里来?
需要查看/usr/目录下Makefile。从Makefile中可知,以CONFIG_INITRAMFS_SOURCE对应的rootfs.cpio文件作为输入,调用gen_init_cpio和gen_initramfs_list.sh生成initramfs_data.cpio.gz文件。
然后INITRAMFS_IMAGE对应,/usr/initramfs_data.cpio$(suffix_y)文件。最终通过.incbin将INITRAMFS_IMAGE编译到initramfs_data.o文件中,即对应.init.ramfs段。
ramdisk的启动
ramfs是以压缩包的形式存放在__initramfs_start和__initramfs_size之间,在kernel_init()-->kernel_init_freeable()-->do_basic_setup()-->populate_rootfs()中调用unpack_to_rootfs()中解压。
kernel_init() -->kernel_init_freeable()-------------------------------在执行完do_basic_setup(),即完成各种initcall之后,判断ramdisk_execute_command命令。 -->free_initmem()---------------------------------------释放__init_begin到__init_end之间的内存。 -->do_basic_setup() -->populate_rootfs()---------------------------------解压__initramfs_start包含的ramdisk到rootfs中。
rootfs和ramfs文件系统
rootfs其实不是一种实际的文件系统,他根据实际情况可能使用ramfs或者tmpfs。
rootfs文件系统
内核启动时:start_kernel()-->vfs_caches_init()-->mnt_init()中,注册rootfs类型的文件系统。
void __init mnt_init(void) { unsigned u; int err; --------------------- /*sysfs依赖于kernfs,这里的初始化是为sysfs的初始化做准备, sysfsroot 节点分配来之kernfs cache高速分配*/ kernfs_init();//当该函数执行完成,就分配了kernfs_node_cache缓存 err = sysfs_init(); //初始化并注册sysfs到linux内核中,注意sysfs先于rootfs注册 if (err) printk(KERN_WARNING "%s: sysfs_init error: %d\n", __func__, err); fs_kobj = kobject_create_and_add("fs", NULL);///创建fs条目,并将其添加到/sys路径下 if (!fs_kobj) printk(KERN_WARNING "%s: kobj create error\n", __func__); init_rootfs();//注册rootfs ;1. init_mount_tree(); //挂载rootfs;2.在fs/namespace.c中 }
如何初始化fs init_rootfs
static struct file_system_type rootfs_fs_type = { .name = "rootfs", .mount = rootfs_mount,//--->在后面init_mount_tree 的时候会执行vfs_kern_mount--->mount_fs;最后被调用 .kill_sb = kill_litter_super, }; //rootfs和我们说的initrd不是一个概念,rootfs是一个文件系统类型 int __init init_rootfs(void) { ////1.1注册rootfs_fs_type,即把rootfs_fs_type添加到全局变量中 int err = register_filesystem(&rootfs_fs_type); if (err) return err; if (IS_ENABLED(CONFIG_TMPFS) && !saved_root_name[0] && (!root_fs_names || strstr(root_fs_names, "tmpfs"))) { err = shmem_init(); is_tmpfs = true; } else { err = init_ramfs_fs();//也将ramfs注册到系统中去 } if (err) unregister_filesystem(&rootfs_fs_type); return err; }
如何挂载?
static void __init init_mount_tree(void) { struct vfsmount *mnt; struct mnt_namespace *ns; struct path root; struct file_system_type *type; type = get_fs_type("rootfs"); if (!type) panic("Can't find rootfs type"); /*mount操作包括remount,move_mount, mount块设备,mountloop设备等,都由内核函数do_mount完成。 本质上,mount操作的过程就是通过 vfs_kern_mount新建一个vfsmount结构,然后将此结构和挂载点关联。 关联之后,目录查找时就能沿着vfsmount挂载点一级级向下查找文件了 */ mnt = vfs_kern_mount(type, 0, "rootfs", NULL);// 得到rootfs的vfsmount结构,设置里面的超级块等信息 -->2.1 put_filesystem(type); if (IS_ERR(mnt)) panic("Can't create rootfs"); ns = create_mnt_ns(mnt); // // 根据mnt新建一个mnt_namespace 创建一个命名空间 if (IS_ERR(ns)) panic("Can't allocate initial namespace"); init_task.nsproxy->mnt_ns = ns; //设置init_task的命名空间 get_mnt_ns(ns); root.mnt = mnt; root.dentry = mnt->mnt_root; mnt->mnt_flags |= MNT_LOCKED; /*/ 由于调init_mount_tree时init进程还没启动, 下面的current宏由最开始时设置的栈指针决定,应该就是init_task的taskstruct结构体。*/ set_fs_pwd(current->fs, &root);//设置init_task的当前目录 set_fs_root(current->fs, &root);//设置init_task的根目录 /*mount操作包括remount,move_mount, mount块设备,mountloop设备等,都由内核函数do_mount完成。 本质上,mount操作的过程就是通过 vfs_kern_mount新建一个vfsmount结构,然后将此结构和挂载点关联。 关联之后,目录查找时就能沿着vfsmount挂载点一级级向下查找文件了 */ }
2.1 rootfs mount的过程
/*vfs_kern_mount主要完成三件事: 1. alloc_vfsmnt创造一个新的struct mount结构 2. 在mount_fs函数里调用特定文件系统的mount回调函数构造一个root dentry,包含特定文件系统的super block信息 3. 用第二步得到的结果完成对struct mount的构造,返回vfsmnt结构。 par name:表示/dev/sda设备名称 */ struct vfsmount * vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data) { struct mount *mnt; struct dentry *root; if (!type) return ERR_PTR(-ENODEV); // alloc一个新的struct mount结构,并初始化里面一部分(如链表指针、mnt_devname等成员内容 mnt = alloc_vfsmnt(name); if (!mnt) return ERR_PTR(-ENOMEM); if (flags & MS_KERNMOUNT) mnt->mnt.mnt_flags = MNT_INTERNAL; /* 调用具体文件系统的mount回调函数type->mount,继续挂载操作 ;比如ext2_fs_type->ext2_mount ext4_fs_type->ext4_mount */ /* ext2_fs_type->ext2_mount-----> ext4_fs_type->ext4_mount ----> 其最后调用的是 mount_bdev 以及其对应的ext2/ext4_fill_super 回调 */ root = mount_fs(type, flags, name, data);// 调用具体的fs mount函数 2.1.1 if (IS_ERR(root)) { mnt_free_id(mnt); free_vfsmnt(mnt); return ERR_CAST(root); } // 完成mnt结构的最后赋值,并返回vfsmount结构 mnt->mnt.mnt_root = root;//mount关联根dentry (使用vfsmount关联) mnt->mnt.mnt_sb = root->d_sb;//mount关联超级块 mnt->mnt_mountpoint = mnt->mnt.mnt_root;// mount关联挂载点 mnt->mnt_parent = mnt;//父挂载指向自己 (临时指向 后面会设置 lock_mount_hash(); list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts); unlock_mount_hash(); return &mnt->mnt; }
2.1.1 调用具体文件系统的mount
struct dentry * mount_fs(struct file_system_type *type, int flags, const char *name, void *data) { struct dentry *root; struct super_block *sb; char *secdata = NULL; int error = -ENOMEM; ---------------------------------- // //file_system_type rootfs_fs_type 中定义.mount = rootfs_mount, root = type->mount(type, flags, name, data); if (IS_ERR(root)) { error = PTR_ERR(root); goto out_free_secdata; } sb = root->d_sb; BUG_ON(!sb); WARN_ON(!sb->s_bdi); sb->s_flags |= MS_BORN; --------------------- }
2.1.2 调用mount_nodev
static struct dentry *rootfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { static unsigned long once; void *fill = ramfs_fill_super;//后面会调用ramfs_fill_super,初始化super_block和root if (test_and_set_bit(0, &once)) return ERR_PTR(-ENODEV); if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) fill = shmem_fill_super; return mount_nodev(fs_type, flags, data, fill);//2.1.2 } struct dentry *mount_nodev(struct file_system_type *fs_type, int flags, void *data, int (*fill_super)(struct super_block *, void *, int)) { int error; struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL); if (IS_ERR(s)) return ERR_CAST(s); error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);//2.1.3 if (error) { deactivate_locked_super(s); return ERR_PTR(error); } s->s_flags |= MS_ACTIVE; return dget(s->s_root); }
2.1.3 初始化super_block(ramfs_fill_super)并初始化root
int ramfs_fill_super(struct super_block *sb, void *data, int silent) { struct ramfs_fs_info *fsi; struct inode *inode; int err; save_mount_options(sb, data); fsi = kzalloc(sizeof(struct ramfs_fs_info), GFP_KERNEL); sb->s_fs_info = fsi; if (!fsi) return -ENOMEM; err = ramfs_parse_options(data, &fsi->mount_opts); if (err) return err; sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = RAMFS_MAGIC; sb->s_op = &ramfs_ops; sb->s_time_gran = 1; //分配inode结构体并初始化inode->i_ino=1 inode = ramfs_get_inode(sb, NULL, S_IFDIR | fsi->mount_opts.mode, 0); //初始化root的dentry结构体 sb->s_root = d_make_root(inode); if (!sb->s_root) return -ENOMEM; return 0; } struct dentry *d_make_root(struct inode *root_inode) { struct dentry *res = NULL; if (root_inode) { static const struct qstr name = QSTR_INIT("/", 1); res = __d_alloc(root_inode->i_sb, &name); if (res) d_instantiate(res, root_inode); else iput(root_inode); } return res; }
所以:在内核启动是init_rootfs()首先根据参数来确定是使用tmpfs还是ramfs,然后在init_mount_tree()进行挂载;
后续在定义CONFIG_BLK_DEV_INITRD的情况下,调用populate_rootfs()将ramdisk解压到RAM中。
注意:加载initramfs, initramfs位于地址__initramfs_start处,是内核在编译过程中生成的,initramfs的是作为内核的一部分而存在的,
不是 boot loader加载的。前面提到了现在initramfs没有任何实质内容。一般都是外部的initrd是真正的实质的文件系统
在gzip解压后的数据经过复杂的mode跳转到不同函数处理buffer。
static __initdata int (*actions[])(void) = { [Start] = do_start, [Collect] = do_collect, [GotHeader] = do_header, [SkipIt] = do_skip, [GotName] = do_name, [CopyFile] = do_copy, [GotSymlink] = do_symlink, [Reset] = do_reset, };
最终还是通过内核中调用类似open()/write()/close()/mkdir()系统调用同样功能函数,创建完整的rootfs
ramdisk执行
kernel_init()是用户空间第一个进程,和ramdisk相关的有ramfs文件系统类型准备;ramdisk解压;启动ramdisk_execute_command来替代当前进程。
回到上述问题:目前看思路是没有问题的,目前还是怀疑内存分配的问题导致!!修改之后就好了

浙公网安备 33010602011771号