linux内核-构建initramfs-x86_64

1 为什么要initramfs?

知道为什么后,往往事情干起来就…

2 制作简易initramfs

制作简易版本,我们以hello为基础,熟练制作过程。简易版本目录结构和制作过程介绍的比较详细,后面有的过程就不具体介说明了。

2.1 目录结构

为了归纳文件,我创建了一个initramfs-hello目录,在initramfs-hello目录下又创建了一个inird,和在initrd目录下又创建bin和lib64目录。

mkdir initramfs-hello
mkdir initramfs-hello/inird
mkdir initramfs-hello/inird/bin
mkdir initramfs-hello/inird/lib64
2.2 创建init文件和添加内容

在initramfs-hello/inird目录下创建init文件并编辑。

touch initramfs-hello/inird/init
vi initramfs-hello/inird/init

在init文件里面添加内容如下。export这栏也可以不写。

#!/bin/bash
echo "Hello Linux"
export PATH=/sbin:/bin:/usr/sbin:/usr/bin 
exec /bin/bash

添加执行权限

chmod +x initramfs-hello/inird/init
2.3 移植bash和库文件

移植bash

cp -d /bin/bash initramfs-hello/inird/bin

使用ldd命令查看bash依赖库

ldd /bin/bash

移植bash依赖库文件

cp -d /lib64/libtinfo.so.5 initramfs-hello/inird/lib64
cp -d /lib64/libtinfo.so.5.9 initramfs-hello/inird/lib64
cp -d /lib64/libdl.so.2 initramfs-hello/inird/lib64
cp -d /lib64/libdl-2.17.so initramfs-hello/inird/lib64
cp -d /lib64/libc.so.6 initramfs-hello/inird/lib64
cp -d /lib64/libc-2.17.so initramfs-hello/inird/lib64
cp -d /lib64/ld-linux-x86-64.so.2 initramfs-hello/inird/lib64
cp -d /lib64/ld-2.17.so initramfs-hello/inird/lib64
2.4 制作打包文件

在initramfs-hello目录下写了个cpio打包命令的文件generate-initramfs。此文件为测试方便,免得每次都要输入很长的命令。文件内容如下:

cd inird
find . | cpio -o -H newc | gzip > ../initramfs-hello.img

生成的initramfs-hello.img文件就是我们需要使用的initramfs文件。

2.5 说明

(1) 把initramfs-hello.img拷贝到启动目录,准备使用。
(2) 这里主要讲生成initrams镜像文件,其它配置不详细写出来,只写文字备注,以及注意事项。
(3) 需要在配置内核,并且要支持initramfs。
(4) 需要在grab.cfg里面添加配置initrd。
(5) 需要移植bash命令以及相关库到根目录盘。
(5) 制定脚本的解释器的字符串“#!/bin/bash”,一定要从第一个行的左侧第一个字符开始。因为内核中的脚本加载器将根据脚本文件的前面两个字符判断使用什么解释器。

3 制作完整版initramfs文件

在移植initramfs文件的根目录下创建init文件,init中没有模块相关的内容,所以前面需要的内容全面都打包到bzImage里面了,此是一个完整版了,我们不能再命名initramfs-hello了,而使用initramfs-rootfs。

3.1 编写init文件

init文件添加如下内容:

#!/bin/bash
echo "Hello Linux"
export PATH=/sbin:/bin:/usr/sbin:/usr/bin
export ROOTMNT=/root
export ROFLAG=-r
mount -n -t devtmpfs udev /dev
mount -n -t proc proc /proc
mount -n -t sysfs sysfs /sys

for x in $(cat /proc/cmdline);do
	case $x in
		root=*)
			ROOT=${x#root=}
		;;
		ro)
			ROFLAG=-r
		;;
		rw)
			ROFLAG=-w
		;;
	esac
done

mount $(ROFLAG) ${ROOT} ${ROOTMNT}
#exec /bin/bash

mount -n --move /dev ${ROOTMNT}/dev
mount -n --move /proc ${ROOTMNT}/proc
mount -n --move /sys ${ROOTMNT}/sys

exec pro_switch_root ${ROOTMNT} /sbin/init

当前需要启动硬盘上面的根文件系统,所以写了pro_switch_root取代/bin/bash,busybox里面也有根文件切换的命令switch_root。此时我们没有使用busybox里面的命令,而时参考了深度探索Linux操作系统,自己写的切换文件,为了区别,我们在前面加了pro_。当执行pro_switch_root时,前面需要加上exec,保证PID一直为1,我们参考的书中,王柏生大神没有添加此项,可能是漏写了。

3.2 编写pro_switch_root.c文件
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mount.h>

int switch_root_delete_dir(char* dir);

void switch_root_delete(char* directory)
{
	// 返回值:成功返回0;失败返回-1,其错误存在全局变量errno中.
	if(unlink(directory)){

		// 一个目录
		if(errno == EISDIR){
			printf("unlink: EISDIR!\n");
			if(!switch_root_delete_dir(directory)){

				//  使用rmdir函数时,目录必须为空,否则调用失败,函数返回-1.成功时,函数返回0.
				rmdir(directory);
			}
		}
	}else
	{
		printf("unlink: delete file success.\n");
	}
}

/*
* 删除目录
*
*/
int switch_root_delete_dir(char* directory)
{
	DIR *dir;
	struct dirent *d;
	struct stat st1, st2;
	char path[PATH_MAX];

	if(lstat(directory, &st1)){
		return errno;
	}

	if(!(dir = opendir(directory))){
		return errno;
	}
	while((d=readdir(dir))){

		// 跳过目录下的.和..文件
		if(d->d_name[0] == '.'
			&& (d->d_name[1] == '\0' || (d->d_name[1] == '.' && d->d_name[2] == '\0'))){
			printf("skip %s/%s\n", directory, d->d_name);
			continue;
		}
		printf("file name: %s/%s\n", directory, d->d_name);

		sprintf(path, "%s/%s", directory, d->d_name);
		lstat(path, &st2);
		// t2.st_dev文件所在设备的ID
		// 这两个设备ID有可能相等 ???
		if(st2.st_dev != st1.st_dev){
			continue;
		}
		switch_root_delete(path);
	}

	closedir(dir);
	return 0;
}

int main(int argc, char* argv[])
{
	pid_t pid = getpid();
	// 此时检查进程的pid是否是1
	printf("[CHD] PID:%d\n", pid);

	int console_fd;
	chdir(argv[1]);
	switch_root_delete_dir("/");
	mount(".", "/", NULL, MS_MOVE, NULL);
	chroot(".");
	chdir("/");
	console_fd = open("/dev/console", O_RDWR);
	dup2(console_fd, 0);
	dup2(console_fd, 1);
	dup2(console_fd, 2);
	close(console_fd);

	// execlp函数会从PATH环境变量所指得目录中查找符合参数file的文件名,找到后便执行该文件
	// 然后将第二个以后的参数当作该文件的argv[0]、argv[1]......,最后一个参数必须用空指针(NULL)结束。
	execlp(argv[2], argv[2], NULL);

	return 0;
}
3.3. 编写makefile文件,编译pro_switch_root.c文件
pro_switch_root: pro_switch_root.c:
clean:
	rm -rf *.o
	rm -rf pro_switch_root
3.4 移植相关文件

移植pro_switch_root到initramfs根目录的bin文件夹中。
在initramfs根目录创建bin, lib64, proc, sys, dev目录。
移植 bash, ls, mount, umount到initramfs根目录的bin目录。
移植相关库文件到initramfs根目录的lib64目录。

参考

1 深度探索Linux操作系统
2 还有其它网络博客,记不清了,抱歉。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值