Busybox源码简介

在嵌入式系统构建中,Busybox可用于构建轻量级的根文件系统,本文从源码结构和源码入口角度分析busybox,了解其背后的运作机制。
busybox版本:1.35.0
Busybox简介
(1-1)开源项目
Busybox是一个开源项目,遵循GPL v2协议。Busybox将众多的UNIX命令集合进了一个很小的可执行程序中,可以用来替代GNU fileuTIls、shelluTIls等工具集。Busybox中各种命令与相应的GNU工具相比,所能提供的选项比较少,但是对于一般的应用场景也足够了,特别是在嵌入式系统的设计中。
(1-2)程序本体较小
Busybox在设计过程中对文件大小进行了优化,并考虑了系统资源有限(比如内存等)的情况。与一般的GNU工具集动辄几M的体积相比,动态链接的Busybox只有几百K,即使是采用静态链接也只有1M左右。除此之外,Busybox按模块设计,可以很容易地加入、去除某些命令,或增减命令的某些选项。
(1-3)使用简单
如果使用Busybox来创建根文件系统,使用起来比较方便,只需要在/dev目录下创建必要的设备节点,在/etc目录下增加一些配置文件即可,当然如果Busybox是动态链接的,那么还需要在/lib目录下包含相关的运行库文件。
Busybox源码目录结构
在较老版本的Busybox中,对于Busybox的多个程序是全部塞进了一个名为uTIlity.c的文件中,后来更改了Busybox的整体源码结构和设计,将这些程序拆分成了各个工具模块。

Busybox源码简介

序号 目录名称 功能说明
1 applets 实现applets框架的文件。目录中包含了几个main()的文件
2 applets_sh 此目录包含了几个作为shell脚本实现的applet示例。在“make install”时不会被自动安装,需要使用时,手动处理
3 arch 包含用于不同体系架构的makefile文件。约束busybox在不同架构体系下的编译构建过程
4 archival 与压缩相关命令的实现源文件。
5 configs busybox自带的默认配置文件
6 console-tools 与控制台相关的一些命令
7 coreuTIls 常用的一些核心命令。例如chgrp、rm等
8 debianutils 针对Debian的套件。
9 e2fsprogs 针对Linux Ext2 FS prog的命令。例如chattr、lsattr
10 editors 常用的编辑命令。例如diff、vi等
11 findutils 用于查找的命令
12 include busybox项目的头文件
13 init init进程的实现源码目录
14 klibc-utils klibc命令套件
15 libbb 与busybox实现相关的库文件
16 libpwdgrp libpwdgrp相关的命令
17 loginutils 与用户管理相关的命令
18 mailutils 与mail相关的命令套件
19 miscutils 该文件下是一些杂项命令,针对特定应用场景
20 modutils 与模块相关的命令
21 networking 与网络相关的命令,例如arp
22 printutils Print相关的命令
23 procps 与内存、进程相关的命令
24 runit 与Runit实现相关的命令
25 shell 与shell相关的命令
26 sysklogd 系统日志记录工具相关的命令
27 util-linux Linux下常用的命令,主要与文件系统操作相关的命令。
Busybox程序主体
Busybox是在linux内核启动后加载运行的用户空间程序,在源码设计上是基于C语言完成设计和开发的。与常规程序一样,Busybox的入口同样是main(),定义在libbb/appletlib文件的末尾处。在函数开始处,使用ENABLE_BUILD_LIBBUSYBOX对函数名称进行了条件分支处理:如果ENABLE_BUILD_LIBBUSYBOX为真,则表示将Busybox以库的方式进行构建。
在函数体中,以条件宏定义进行代码的编译逻辑控制:
上述代码都调用了mallopt()函数,该函数用于设置内存的分配参数,由于默认值太大(为256KB),故此处调整内存分配大小,让出多余的内存。
接着,是一个由#if — #elif — #else — #endif控制的条件宏多分支判断结构语句,此处以Busybox的一般运行情况为例(在Linux内核启动后期,加载并运行Busybox构建出的init程序)。其执行逻辑如下:
首先Busybox是一个linux下的工具集合,本质则是一个个的命令,例如:ls、mv、cp等,在命令行我们输入想要执行的操作时,例如:mkdir iriczhao,则会将参数传递给Busybox,然后由他完成对应的操作。
在源码中,使用char * applet_name表示工具的名称(本质是字符串),首先会调用lbb_prepare()函数:
将会设置applet_name的值为“busybox“,用于执行ENABLE_FEATURE_INDIVIDUAL为真时的逻辑操作:
接着,会解析命令行传递的第一个参数:
例如,在命令行输入mkdir iriczhao命令,则会解析到mkdir命令传递给applet_name,至于后面的参数(此处是iriczhao)是如何传递的,后文会描述到。
如果配置了FEATURE_SUID_CONFIG宏定义,在parse_config_file()函数中还将从/etc/busybox.conf文件中解析关于busybox的配置参数。
在最后,则是busybox的重要函数:run_applet_and_exit(),该函数定义如下:
如果NUM_APPLETS大于0,则会执行对应的命令操作,并退出;否则,busybox将会报错:
从上述代码可知,在命令行键入命令后,实则起关键作者的函数是:find_applet_by_name()和run_applet_no_and_exit()。下文将继续分析。
Busybox程序运行剖析
在上一小节中,已经知道当我们在busybox的命令行下,键入命令后,执行具体操作的函数是:find_applet_by_name()和run_applet_no_and_exit()。
在编译构建源码并安装busybox后,在安装目录下的文件结构则是一个名为busybox的可执行程序和很多的链接,这些链接实则是我们在命令行键入的命令名称。如下图所示:

Busybox源码简介

从源码角度看,busybox中的命令都有一一对应的执行函数,其函数命名格式为xxx_main(),在源码设计上,其内部在/include/applet_tabls.h头文件中维护了一张命令表,定义如下(代码太长,有省略):
上述函数指针数组中的元素则是分布于busybox源码各个目录下命令入口函数。在代码执行逻辑中,首先会调用find_applet_by_name()函数,通过传入的命令名称获取在命令表中的数组下标。并将命令对应的下标applet、命令名称name和命令行参数字符串argv传递给run_applet_no_and_exit()函数(注:解释了上一小节中,命令行对应命令后面的参数是如何传递的),该函数定义如下:
在上述代码中,执行命令下的对应具体操作函数的语句是:
applet_main是命令表数组,applet_no是对应命令的数组下标,本质则是调用对应的applet_main命令表数组中的元素(函数指针),并将argc和argv作为参数给了对应的命令执行函数。
站在巨人的肩膀上,『敬畏』、『热情』
来源:嵌入式小生
声明:本站转载此文目的在于传递更多信息,并不代表赞同其观点和对其真实性负责。如涉及作品内容、版权和其它问题,请在30日内与本网联系,我们将在第一时间删除内容,本网站对此声明具有最终解释权。

编辑:乾坤芯,如若转载,请注明出处:https://www.qiankunxin.com/8852.html

(0)
上一篇 2022年10月12日 下午4:08
下一篇 2022年10月12日 下午4:38

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注