BusyBox는 여러 명령을 하나의 실행 파일에서 제공하는 멀티콜 바이너리로, Alpine의 기본 wget도 BusyBox를 통해 실행됨
Alpine 컨테이너의 /usr/bin/wget은 실제 바이너리가 아니라 /bin/busybox를 가리키는 심볼릭 링크였음
BusyBox는 실행 시 argv[0]에서 호출 이름을 읽고, 경로의 마지막 이름으로 실행할 애플릿을 결정함
각 애플릿은 이름으로 찾아 해당 main 함수로 진입하며, wget은 wget.c에 구현되고 최종적으로 wget_main이 실행됨
busybox --list로 컴파일된 명령을 확인할 수 있고, Alpine 예시에서는 304개가 표시되며 각 유틸리티는 축소된 버전처럼 보임
BusyBox의 동작 방식
BusyBox는 여러 명령을 하나의 실행 파일에서 제공하는 멀티콜 바이너리(multicall binary) 구조를 사용함
Alpine 컨테이너에서 /usr/bin/wget은 실제 실행 파일이 아니라 /bin/busybox를 가리키는 심볼릭 링크였음
docker run --rm -it alpine sh
/ # which wget
/usr/bin/wget
/ # ls -lah /usr/bin/wget
lrwxrwxrwx 1 root root 12 Apr 15 04:51 /usr/bin/wget -> /bin/busybox
/usr/bin에는 130개가 넘는 실행 파일이 하나의 바이너리에서 나온 형태로 보이며, BusyBox의 멀티콜 바이너리 구조와 연결됨
BusyBox는 실행 시 argv[0]에서 호출된 이름을 가져오고, 경로의 마지막 이름만 뽑아 어떤 애플릿(applet) 을 실행할지 결정함
applet_name = argv[0];
if (applet_name[0] == '-')
applet_name++;
applet_name = bb_basename(applet_name);
busybox ls -1처럼 명시적으로 애플릿 이름을 넘겨도 동작하며, 존재하지 않는 이름을 주면 applet not found가 출력됨
/ # busybox ls -1
bin
dev
etc
home
/ # busybox meheh
meheh: applet not found
애플릿 구성과 설치 방식
BusyBox는 이름으로 애플릿을 찾은 뒤 해당 애플릿의 main 함수를 실행함
int applet = find_applet_by_name(name);
// ...
run_applet_no_and_exit(applet, name, argv);
// ...
xfunc_error_retval = applet_main[applet_no](argc, argv);
각 애플릿은 별도 C 파일을 가지며, wget은 wget.c 에 구현돼 있음
애플릿별 설정은 코드 주석 형태로 정의되어 있으며, WGET 설정에는 wget (41 kb), 기본 활성화, HTTP와 FTP 서버에서 파일을 비대화식으로 내려받는 유틸리티라는 도움말, 빌드 대상 wget.o가 포함됨
//config:config WGET
//config: bool "wget (41 kb)"
//config: default y
//config: help
//config: wget is a utility for non-interactive download of files from HTTP
//config: and FTP servers.
//applet:IF_WGET(APPLET(wget, BB_DIR_USR_BIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_WGET) += wget.o
최종적으로 wget 애플릿은 wget_main으로 진입함
int wget_main(int argc UNUSED_PARAM, char **argv)