AndroidToybox简述
Android Q版本中,许多Linux命令以及Android⾃带的命令并不是都有⾃⾝的⼆进制⽂件存在/system/bin/⽬录下,⽽是集成在toybox 这个⼆进制⽂件中,通过toybox来执⾏对应的命令;可以看到下⾯的命令很多都是软链接到toybox上。
lrwxr-xr-x  1 root shell      6 2009-01-01 08:00 ln -> toybox
lrwxr-xr-x  1 root shell      6 2009-01-01 08:00 load_policy -> toybox
lrwxr-xr-x  1 root shell      6 2009-01-01 08:00 log -> toybox
在该⼯具对应源码下的README中,⾸⾏就是其⽤途的介绍,是⼀个集成多个Linux命令⾏的⼯具。
Toybox: all-in-one Linux command line.    //多合⼀Linux命令⾏。
源码在AOSP中的此⽬录下。
android/external/toybox/
接下来简单介绍下toybox是如何调起其他命令来执⾏的。⾸先看下对应的Android.bp⽂件,看下是如何
编译的,需要什么依赖。
cc_binary {
name: "toybox",
defaults: ["toybox-defaults"],
host_supported: true,
recovery_available: true,
shared_libs: toybox_libraries,
target: {
darwin: {
enabled: false,
},
},
}
从中可以看到依赖toybox-defaults,⽽这个依赖是在bp⽂件的开头,由于篇幅过长,这⾥不再列举,我们从这边看起只是为了到⼊⼝函数,其依赖中有main.c⽂件,并且有main函数,所以从这⾥⾯看起。
int main(int argc,char*argv[])
{
if(!*argv)return127;
else{
int stack;
toys.stacktop =&stack;
}
if(CFG_TOYBOX_ON_ANDROID)signal(SIGPIPE, SIG_DFL);
if(!CFG_TOYBOX_FORK){
if(0x80&**argv){
**argv &=0x7f;
toys.stacktop =0;
}
}
if(CFG_TOYBOX){//默认是1
// Call the multiplexer, adjusting this argv[] to be its' argv[1].
// (It will adjust it back before calling toy_exec().)
toys.argv = argv-1;
toybox_main();
}else{
// a single toybox command built standalone with no multiplexer
toy_singleinit(toy_list, argv);
toy_list->toy_main();
}
xexit();
}
main函数中⽐较简单,先判断参数是否异常,然后将参数放到toys结构体中,调⽤toybox_main()继续往下执⾏。在toybox_main中完成命令的执⾏,这些命令⼜都是存在toy_list这张映射表中。通过toy_exec中的toy_find可以到,查⽅式是通过⼆分法进⾏查,因为所有的命令在数组中是有序的,到后将指针赋值给which这个结构体指针。
void toybox_main(void)
{
static char*toy_paths[]={"usr/","bin/","sbin/",0};
int i, len =0;
if(toys.argv[1]){
toy_exec(toys.argv+1);//存在参数的话就寻参数中要执⾏的命令;
if(0<readlink(toys.argv[1], libbuf,sizeof(libbuf)))
toy_exec_which(toy_find(basename(libbuf)), toys.argv);
}
// For early error reporting
toys.which = toy_list;
if(toys.argv[1]&& toys.argv[1][0]!='-')unknown(toys.argv[1]);
// Output list of command.
//没有到对应的命令,则输出所有toy_list中包含的命令
for(i=1; i<ARRAY_LEN(toy_list); i++){
int fl = toy_list[i].flags;
if(fl & TOYMASK_LOCATION){
if(toys.argv[1]){
int j;
for(j=0; toy_paths[j]; j++)
if(fl &(1<<j)) len +=printf("%s", toy_paths[j]);
}
len +=printf("%s",toy_list[i].name);
if(++len >65) len =0;
xputc(len ?' ':'\n');
}
}
xputc('\n');
}
void toy_exec_which(struct toy_list *which,char*argv[])
{
// Return if we can't find it (which includes no multiplexer case),
if(!which)return;
if(!CFG_TOYBOX_NORECURSE && toys.stacktop)
if(labs((long)toys.stacktop-(long)&which)>6000)return;
if(toys.which &&(which->flags&TOYFLAG_ROOTONLY)&& toys.wasroot)return;简述android概述
toy_init(which, argv);
if(toys.which) toys.which->toy_main();//执⾏对应命令所在⼊⼝函数;
xexit();
}
void toy_init(struct toy_list *which,char*argv[])
{
...
// Continue to portion of init needed by standalone commands
toy_singleinit(which, argv);
}
static void toy_singleinit(struct toy_list *which,char*argv[])
{
toys.which = which;
toys.argv = argv;
...
}
toy_exec_which中会先做检查,然后在toy_init中完成命令的初始化,通过toy_singleinit将对应命令和命令的参数存放到toys中。
真正关键的部分为if (toys.which) toys.which->toy_main(),这⼀步会真正调起对应命令所在的⼊⼝函数,执⾏对应命令的逻辑;这⾥需要看下toy_main的定义,是在toy_list 结构体中的⼀个函数指针。在
main.c中定义了该结构体数组,是在newtoys.h声明的宏定义,拿df 这个命令来举例,toy_main指向了df_main这个函数,所以通过该⼊⼝可以真正调起df的逻辑⼊⼝,执⾏完之后就退出。其他的基本都类似。
struct toy_list toy_list[]={
#include"generated/newtoys.h"
};
//newtoys.h
USE_DF(NEWTOY(df,"HPkhit*a[-HPkh]", TOYFLAG_SBIN))
#define NEWTOY(name, opts, flags) {#name, name##_main, OPTSTR_##name, flags},
#define OLDTOY(name, oldname, flags) \
{#name, oldname##_main, OPTSTR_##oldname, flags},
extern struct toy_list {
char*name;//df
void(*toy_main)(void);//df_main
char*options;//OPTSTR_df
unsigned flags;//TOYFLAG_SBIN
} toy_list[];
后⾯各个命令的具体逻辑就不再赘述了,和Linux原⽣的命令原理相同,部分会在Android上进⾏微调。综合上⾯的来看,实现的逻辑⽐较简单,通过⼀个统⼀的⼊⼝,在映射表中查对应的命令,通过函数指针调起具体命令的逻辑,最终完成命令的执⾏。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。