Linux内核开发_3_busybox

Busybox简介

busybox是一个开源项目,集合了Linux的常用命令,且体积非常小,一般被用于嵌入式Linux上。

busybox在编写初期的思想就是简洁,小巧,强悍,并且设计时从系统资源考虑,节省内存,动态编译busybox工具包总大小才几百kb,如果是静态编译则才1MB左右,非常适合用于嵌入式开发,与发行版不同,发行版使用的不是busybox,一些流行发行版一般使用的都是GNU fileutils、shellutils,这些工具链里包含了对文件,还有一些shell支持的命令,就是我们常用的ls之类的命令,都属于GNU的开源项目。

下载Busybox

你可以在它的官网下载到有关Busybox的所有开源版本的源代码:

https://busybox.net/downloads/

将Busybox制作成根文件系统

前几篇文章讲了如何使用Initramf作为根文件系统,但是效果并不能达到我们的要求,这篇文章使用busybox做一个完整的根文件系统,并且不将它作为临时根文件系统,而是把它当作一个实际根文件系统,供Linux内核使用。

根文件系统简述

在Linux中,根文件系统就是Linux下“/”目录,Linux把文件系统挂载到这个目录下,这个目录下一般拥有Linux运行时需要的一些设备文件,如块设备,tty,字符设备文件等。

编译Busybox

这里我下载的是1.27.0版本的

wget https://busybox.net/downloads/busybox-1.27.0.tar.bz2

这里跟大家说一个tar解压命令的小技巧,原本这种解压我们需要使用对应的选项,二次解压后才能得到原文件,这里我们不需要指定这些,tar命令会自动帮我们分析

tar -xf busybox-1.27.0.tar.bz2

-x是解压的意思,-f是文件的意思,我们不需要指定解压格式,tar会自动帮我们分析

busybox目录结构说明:

applets

主要是实现applets框架的文件

applets_sh

一些有用的脚本,例如:dos2unix、unix2dos等

archival

与压缩有关的命令源文件,例如:bzip2、gzip等

configs

自带的一些默认配置文件

console-tools

与控制台相关的一些命令,例如:setconsole

coreutils

常用的核心命令,例如:cat、rm等

editors

常用的编辑命令,例如:vi、diff等

findutils

用于查找的命令,例如:find、grep等

init

init进程的实现源文件

networking

与网络相关的命令,例如:telnetl、arp等

shell

与shell相关的实现,例如:ash、msh等

util-linux

Linux下常用的命令,主要是与文件系统相关的,例如:mkfs_ext2等


配置busybox

和Linux一样,busybox使用menuconfig配置,可以在里面去裁剪一些不需要的模块,这里我们使用这个命令然后选择静态编译,因为在我们编译的Linux内核里,并不自带GLIBC库

sudo make menuconfig

选择“Busybox Setting"

选择”Build BusyBox as a static binary(no shared libs)” 按下“Y”

然后保存退出,使用make install编译

sudo make install

编译完成后会生成很多目录,其中_install目录里就是我们需要的bin文件,除了bin文件还包含了一些根文件系统所具备的一些设备文件

使用ls命令查看一下:

包含了一些基本的文件目录,如usr,bin这些Linux内核运行时的一些目录,当然,只有这些目录是不能算上一个完整的根文件系统目录的,还需要/dev /tty等目录。

创建文件系统

busybox编译完成了,上面也说了_install文件夹下的所有文件,要被拿来制作成文件系统,那么怎么制作呢?

很简单,在Linux上如果要对软盘或者U盘进行操作,那么就需要挂载,这里我们可以使用dd命令先创建一个软盘,作为我们的根文件系统的镜像文件,然后将它挂载到一个目录下,最后我们把busybox下_install目录copy到镜像文件挂载的目录下,这样镜像文件里就有了一个基础根文件系统所需要的设备文件目录,然后在卸载挂载,这样一个文件系统的镜像就已经制作完成了。

实践:

1.创建镜像文件

dd if=/dev/zero of=rootfs.img bs=4096 count=1024

这里我们创建一个4MB大小的镜像文件

然后使用ext3格式格式化这个镜像文件,前几篇文章说过,Linux子系统VFS负责与文件系统进行交互,但是VFS只认特定的格式,如NFS,EXT等,所以这里我们格式化一下,注意如果你的Linux内核不支持ext3,需要在make menuconfig时,选择file system里把这一选项按下Y,否则VFS会报内核恐慌

mkfs.ext3 rootfs.img

创建一个目录作为挂载点:

mkdir rootfs

挂载:

sudo mount -o loop rootfs.img rootfs/

将busybox下的_install文件夹下的所有文件copy过来

sudo cp -a ../busybox-1.27.0/_install/* rootfs/.

ls看一下:

除了这些文件以外,我们还需要三个设备文件dev proc sys

这里介绍一些这三个文件的作用:

/sys/ 用于管理存储文件,文件相关管理 的,sys是运行在内存中的
/proc 用于管理进程,是对进程管理,运行的进程参数管理的
/dev 存放各种设备节点,除了设备节点还会存储socket,管道文件

这些是Linux内核运行时需要的三个设备节点,这里我们创建出来,具体这些文件的作用,我后面在写文件系统组成时会详细讲解,目前各位只需要知道这些文件在运行时,会被Linux内核里的一些文件管理系统挂载到上面去

注意这些文件需要创建在rootfs文件夹下哦

cd rootfs
sudo  mkdir dev proc sys
cd ..

基本一个文件系统的基础已经搭好了,那么这样我们就可以卸载挂载了,然后把这个挂载目录删除或者不删除都可以

sudo umount rootfs

那么到这一步,基本上一个简单的根文件系统已经搭好了,可以用作嵌入式,或者amd都可以。

运行

使用qemu指定我们的硬盘路径,然后模拟运行:

qemu-system-x86_64 -kernel /usr/src/linux-4.10.15/arch/x86/boot/bzImage\
                   -hda rootfs.img \
                   -append "root=/dev/sda init=/bin/ash"

这里来解释一下选项:

-hda 是指定硬盘文件

-append 这个选项不用多说了,第一篇文章有说过,这里说一下里面的运行

root=/dev/sda 这个是指定根文件系统挂载点,大家有没有发现其实我们刚刚创建时,只是创建了一个dev目录,但是并没有创建sda这个目录,这个不用担心,因为Linux内核运行时会在自动创建设备文件,并将文件系统挂载到这些文件上,这个sda就是文件系统挂载目录,这个设备文件必须指定为sda,经过测试结果是这样,查的资料是这个设备文件是指整个硬盘,还有sda1,sda2这些是指硬盘下的分区,至于更详细的资料,还在查阅读内核代码中。

最后面,的init可以不用指定,这个就是在根文件目录下的bin下找一个可执行文件,如果不指定的话Linux内核会自动寻找几个常用的init

这几行代码是位于Linux内核init/main.c文件下的kernel_init函数下,它会去依次使用try_to_run_init_process函数来调用不同的初始化函数,如果你的根文件系统下有对应的文件就ok,如果没有则会引起内核恐慌,找不到init程序。

与Linux2.6内核不同的是Linux2.6使用的是init_post,这个函数会发生两次调用,第一次是进入到init_post,完成相关初始化后在去依次创建这些进程。

但是到了我们所使用的内核时,只会发生一次函数调用,初始化前面就已经被完成了,就只会发生调用创建进程的函数,可以说得上是运行速度和资源上的提升了。

因为在内核里,一切都最好按照一个整体来设计,这样就可以省去不必要的接口调用,等函数调用时发生的堆栈保护等问题。

所以这也是为什么可以在Linux内核里看到大量的内联函数的原因。

运行结果:

注意运行后要按两下回车,因为运行时init已经打印出数据来了,但是Linux内核的一些工作是后续完成的,也会打印出来,会导致你可能看不到“#”这个输入交互字符,建议按两下回车,刷一下缓冲流,有的时候这会让人觉得是不是运行没有成功,哈哈我就是。

添加自己的调试程序

这里教大家一个小技巧,比如我写了一个调试程序,那么我想调试它怎么办?

我们可以先挂载镜像文件,然后把我们的文件打包到bin目录下就可以了,不过打包的文件要静态编译,这一点在第一章已经说过了,这里就不多说啦。

先随便写一个文件:

vim test.c
#include <stdio.h>

int main(){
    printf("test!");
    return 0;
}

gcc静态编译:

gcc -static -o test test.c

然后挂载一下镜像文件,在把test copy进去

sudo mount -o -loop rootfs.img rootfs
sudo cp test rootfs/bin/

然后运行,Linux内核时到bin目录下执行即可

记得把镜像文件分配大一点哦,不然会出现 No space left on device的错误。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章