1. build system总体架构
@Makefile
Makefile
+-- include $(srctree)/scripts/Kbuild.include
#此处将Makefile.build引入主Makefile.
+-- build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj
+-- -include $(srctree)/arch/$(ARCH)/Makefile
#Makefile.build将各个目录下的Kbuild包含进来.
@scripts/Makefile.build
+-- src := $(obj)
+-- -include .config
+-- include scripts/Kbuild.include
+-- kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
+-- include $(if $(wildcard $(src)/Kbuild), $(src)/Kbuild, \
$(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, \
$(kbuild-dir)/Makefile \
) \
)
+-- include scripts/Makefile.lib
##################################################################################
2. 依赖关系和入口点
#busybox << busybox_unstripped
########################################
busybox: busybox_unstripped
ifeq ($(SKIP_STRIP),y)
$(Q)cp $< $@
else
$(Q)$(STRIP) -s --remove-section=.note --remove-section=.comment \
busybox_unstripped -o $@
# strip is confused by PIE executable and does not set exec bits
$(Q)chmod a+x $@
endif
#busybox_unstripped << busybox-all
########################################
busybox_unstripped: $(busybox-all) FORCE
$(call if_changed_rule,busybox__)
$(Q)rm -f .old_version
#busybox-all << $(core-y) $(libs-y)
########################################
# Build busybox
# ---------------------------------------------------------------------------
# busybox is build from the objects selected by $(busybox-init) and
# $(busybox-main). Most are built-in.o files from top-level directories
# in the kernel tree, others are specified in arch/$(ARCH)Makefile.
# Ordering when linking is important, and $(busybox-init) must be first.
#
# busybox
# ^
# |
# +-< $(busybox-init)
# | +--< init/version.o + more
# |
# +--< $(busybox-main)
# | +--< driver/built-in.o mm/built-in.o + more
# |
# +-< kallsyms.o (see description in CONFIG_KALLSYMS section)
#
# busybox version (uname -v) cannot be updated during normal
# descending-into-subdirs phase since we do not yet know if we need to
# update busybox.
# Therefore this step is delayed until just before final link of busybox -
# except in the kallsyms case where it is done just before adding the
# symbols to the kernel.
#
# System.map is generated to document addresses of all kernel symbols
busybox-all := $(core-y) $(libs-y)
#core-y << %/built-in.o
#libs-y << %/lib.a %/built-in.o
#由此可见,core-y和libs-y都是在各自基础上后缀一个built-in.o
##########################################
core-y := $(patsubst %/, %/built-in.o, $(core-y))
libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y := $(libs-y1) $(libs-y2)
#core-y <<
#libs-y <<
#core-y和libs-y实际上都是一个包含各种目录的字符串.
###########################################
core-y := \
applets/ \
libs-y := \
archival/ \
archival/libarchive/ \
console-tools/ \
coreutils/ \
coreutils/libcoreutils/ \
debianutils/ \
e2fsprogs/ \
editors/ \
findutils/ \
init/ \
libbb/ \
libpwdgrp/ \
loginutils/ \
mailutils/ \
miscutils/ \
modutils/ \
networking/ \
networking/libiproute/ \
networking/udhcp/ \
printutils/ \
procps/ \
runit/ \
selinux/ \
shell/ \
sysklogd/ \
util-linux/ \
util-linux/volume_id/ \
#由此可见,按照链接的顺序可以发现,最先执行的代码应该在applets/目录下
@scripts/Kbuild
obj-y :=
obj-y += applets.o
所以,入口点代码应该在applets.c里面
@applets/applets.c
#include "busybox.h"
#if ENABLE_BUILD_LIBBUSYBOX
int main(int argc UNUSED_PARAM, char **argv)
{
return lbb_main(argv);
}
#endif
3. 代码调用逻辑
int lbb_main(char **argv)
+-- lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv));
+-- applet_name = argv[0];
+-- applet_name = bb_basename(applet_name);
+-- parse_config_file();
+-- run_applet_and_exit(applet_name, argv);
+-- int applet = find_applet_by_name(name);
+-- if (applet >= 0)
run_applet_no_and_exit(applet, argv);
+-- if (strncmp(name, "busybox", 7) == 0)
exit(busybox_main(argv)); //busybox_main()...
+-- full_write2_str(applet_name);
+-- full_write2_str(": applet not found\n");
+-- xfunc_die();
static int busybox_main(char **argv)
+-- applet_name = bb_get_last_path_component_nostrip(argv[0]);
+-- run_applet_and_exit(applet_name, argv);
+-- int applet = find_applet_by_name(name); //从applet_name[]函数名数组中找到name对应的索引
+-- const char *p = applet_names;
+-- while (i < NUM_APPLETS) {
if (strcmp(name, p) == 0)
return i;
p += strlen(p) + 1;
i++;
}
+-- if (applet >= 0)
run_applet_no_and_exit(applet, argv);
//利用找到的索引在applet_main[]函数指针数组中找到函数指针并执行之
+-- exit(applet_main[applet_no](argc, argv));
+-- if (strncmp(name, "busybox", 7) == 0)
exit(busybox_main(argv));
以busybox的init为例进行分析:
//通过上面分析可知知道,busybox中使用了applet_names[]和applet_main两个数组来维护busybox应用程序名称和具体应用程序入口点
//之间的映射关系,通过应用程序名在applet_names[]找到应用程序对应的索引,在用这个索引在applet_main[]中找到应用程序的入口点.
const char applet_names[] ALIGN1 = ""
...
"ifconfig" "\0"
"init" "\0" //init...
"insmod" "\0"
...
};
int (*const applet_main[])(int argc, char **argv) = {
...
ifconfig_main,
init_main, //init_main...
modprobe_main,
...
};
#root@e4:/sbin# ls -l init
#lrwxrwxrwx 1 311263 named 14 Aug 31 17:12 init -> ../bin/busybox
#/sbin/init实际上是一个指向/bin/busybox的软链接
@init/init.c
int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int init_main(int argc UNUSED_PARAM, char **argv)
{
if (argv[1] && strcmp(argv[1], "-q") == 0) {
return kill(1, SIGHUP);
}
//设置信号处理函数
#if DEBUG_SEGV_HANDLER
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = handle_sigsegv;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGILL, &sa, NULL);
sigaction(SIGFPE, &sa, NULL);
sigaction(SIGBUS, &sa, NULL);
}
#endif
...
//解析/etc/inittab配置文件
parse_inittab();
...
}