目前正在调试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] [] panic+0x8c/0x19f
[ 5.115211] [] init_post+0xbf/0xbf
[ 5.120352] [] kernel_init+0x153/0x15a
[ 5.125864] [] kernel_thread_helper+0x4/0x10
[ 5.131895] [] ? start_kernel+0x375/0x375
[ 5.137666] [] ? 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:
initrd 的英文含义是 boot loader iniTIalized RAM disk,就是由 boot loader 初始化的内存盘。在 linux内核启动前, boot loader 会将存储介质中的 initrd 文件加载到内存,内核启动时会在访问真正的文件系统前先访问该内存中的 initrd 文件系统。
内核在启动过程中会解压initrd ,软后挂载initrd为根目录,然后执行对应的脚本。
分cpio-initrd和 image-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主要完成三件事:
- alloc_vfsmnt创造一个新的struct mount结构
- 在mount_fs函数里调用特定文件系统的mount回调函数构造一个root dentry,包含特定文件系统的super block信息
- 用第二步得到的结果完成对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 ---->
1.
其最后调用的是 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
kernel_init()是用户空间第一个进程,和ramdisk相关的有ramfs文件系统类型准备;ramdisk解压;启动ramdisk_execute_command来替代当前进程。
回到上述问题:目前看思路是没有问题的,目前还是怀疑内存分配的问题导致!!修改之后就好了