当前位置: 移动技术网 > 移动技术>移动开发>Android > Android Toybox简述

Android Toybox简述

2020年08月10日  | 移动技术网移动技术  | 我要评论
toybox加粗样式

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) return 127;
  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;

  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上进行微调。综合上面的来看,实现的逻辑比较简单,通过一个统一的入口,在映射表中查找对应的命令,通过函数指针调起具体命令的逻辑,最终完成命令的执行。

本文地址:https://blog.csdn.net/m0_46250244/article/details/107799539

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网