Android AMS 进阶一

Android AMS 进阶一

Android启动流程

一.Android init进程启动

1.init简介

init是一个进程,他是Linux系统中用户空间的第一个进程,而Android是基于Linux内核的。所以init进程也是Android系统中用户空间的第一个进程,其进程号为1。
它的主要职责是创建Zygote和属性服务等。

2.init进程启动之前

在init进程启动之前还有如下几个启动流程,偏Kernel这块我也不是很懂。大致如下:

A.启动电源:当电源键按下时,引导芯片代码开始从ROM开始执行。加载引导程序Bootloader到RAM中,然后执行;
B.引导程序Bootloader:主要作用是将系统OS拉起来并允许;
C.linux内核启动:内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。当内核完成系统设置,首先会在系统文件中寻找"init"文件,然后启动root进程或者系统的第一个进程。

3.init进程启动流程

init进程的入口函数是init.cpp中的main函数:

int main(int argc, char** argv) {
   
    .....
    if (is_first_stage) {
   
        boot_clock::time_point start_time = boot_clock::now();
        //1.创建一些文件夹并挂载它们
        // Clear the umask.
        umask(0);
        clearenv();
        setenv("PATH", _PATH_DEFPATH, 1);
        // Get the basic filesystem setup we need put together in the initramdisk
        // on / and then we'll let the rc file figure out the rest.
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
   .....
   }
   ......
   //2.初始化和属性相关的资源
    property_init();
   ....
   //3.启动属性服务
    start_property_service();
   ....
   //4.解析init.rc配置文件
   LoadBootScripts(am, sm);
   .....
}

1.属性服务

Android提供一种机制:属性服务(Property Service)。应用可以通过这个属性机制,设置或者查询属性。

  1. property_init 初始化属性值

实现方法是在system/core/init/property_service.cpp:

void property_init() {
   
    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
    CreateSerializedPropertyInfo();
    if (__system_property_area_init()) {
   
        LOG(FATAL) << "Failed to initialize property area";
    }
    if (!property_info_area.LoadDefaultPath()) {
   
        LOG(FATAL) << "Failed to load serialized property info file";
    }
}

A.先来看__system_property_area_init函数,从字面意思上看这个函数是用来做初始化的,实现是在/bionic/libc/bionic/system_property_api.cpp:

__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_property_area_init() {
   
  bool fsetxattr_failed = false;
  return system_properties.AreaInit(PROP_FILENAME, &fsetxattr_failed) && !fsetxattr_failed ? 0 : -1;
}

可以看到最终返回时会去调用AreaInit函数,实现在/bionic/libc/system_properties/system_properties.cpp:

bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed) {
   
  if (strlen(filename) > PROP_FILENAME_MAX) {
   
    return false;
  }
  strcpy(property_filename_, filename);

  contexts_ = new (contexts_data_) ContextsSerialized();
  if (!contexts_->Initialize(true, property_filename_, fsetxattr_failed)) {
   
    return false;
  }
  initialized_ = true;
  return true;
}

B.然后来看LoadDefaultPath函数,函数是加载默认property info路径。实现是在/system/core/property_service/libpropertyinfoparser/property_info_parser.cpp中:

bool PropertyInfoAreaFile::LoadDefaultPath() {
   
  return LoadPath("/dev/__properties__/property_info");
}

bool PropertyInfoAreaFile::LoadPath(const char* filename) {
   
  //dev是一种虚拟内存文件系统
  int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);

  struct stat fd_stat;
  if (fstat(fd, &fd_stat) < 0) {
   
    close(fd);
    return false;
  }

  if ((fd_stat.st_uid != 0) || (fd_stat.st_gid != 0) ||
      ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) ||
      (fd_stat.st_size < static_cast<off_t>(sizeof(PropertyInfoArea)))) {
   
    close(fd);
    return false;
  }

  auto mmap_size = fd_stat.st_size;

  //将文件映射为共享进程空间内存,使其可以与操作内存方式一致
  void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
  if (map_result == MAP_FAILED) {
   
    close(fd);
    return false;
  }

  auto property_info_area = reinterpret_cast<PropertyInfoArea*>(map_result);
  if (property_info_area->minimum_supported_version() > 1 ||
      property_info_area->size() != mmap_size) {
   
    munmap(map_result, mmap_size);
    close(fd);
    return false;
  }

  close(fd);
  mmap_base_ = map_result;
  mmap_size_ = mmap_size;
  return true;
}
  1. start_property_service 启动属性服务

init进程会启动一个属性服务器,而客户端只能通过与属性服务器交互来设置属性

void start_property_service() {
   
    selinux_callback cb;
    cb.func_audit = SelinuxAuditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    property_set("ro.property_service.version", "2");

    // //创建一个socket用来IPC通信
    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   false, 0666, 0, 0, nullptr);
    if (property_set_fd == -1) {
   
        PLOG(FATAL) << "start_property_service socket creation failed";
    }

    //对propert_set_fd进行监听。其中8代表属性服务最多可以同时对8个试图设置属性的用户提供服务
    listen(property_set_fd, 8);

	//将property_set_fd 放入到epoll句柄中,用epoll来监听property_set_fd ,
    //当属性服务器收到客户端的请求,init进程将用handle_property_set_fd函数来处理
    register_epoll_handler(property_set_fd, handle_property_set_fd);
}

接着看handle_property_set_fd函数:

static void handle_property_set_fd() {
   
    static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */

    //先接收TCP连接
    int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
    if (s == -1) {
   
        return;
    }

    ucred cr;
    socklen_t cr_size = sizeof(cr);
    
    //取出客户端进程的权限等属性
    if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
   
        close(s);
        PLOG(ERROR) << "sys_prop: unable to get SO_PEERCRED";
        return;
    }

    SocketConnection socket(s, cr);
    uint32_t timeout_ms = kDefaultSocketTimeout;

    uint32_t cmd = 0;
    if (!socket.RecvUint32(&cmd, &timeout_ms)) {
   
        PLOG(ERROR) << "sys_prop: error while reading command from the socket";
        socket.SendUint32(PROP_ERROR_READ_CMD);
        return;
    }

    switch (cmd) {
   
    case PROP_MSG_SETPROP: {
   
        char prop_name[PROP_NAME_MAX];
        char prop_value[PROP_VALUE_MAX];

        if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
            !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
   
          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
          return;
        }

        prop_name[PROP_NAME_MAX-1] = 0;
        prop_value[PROP_VALUE_MAX-1] = 0;

        const auto& cr = socket.cred();
        std::string error;
        //这里是关键函数
        uint32_t result =
            HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
        if (result != PROP_SUCCESS) {
   
            LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
                       << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
                       << error;
        }

        break;
      }

    case PROP_MSG_SETPROP2: {
   
        std::string name;
        std::string value;
        if (!socket.RecvString(&name, &timeout_ms) ||
            !socket.RecvString(&value, &timeout_ms)) {
   
          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket";
          socket.SendUint32(PROP_ERROR_READ_DATA);
          return;
        }

        const auto& cr = socket.cred();
        std::string error;
        uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
        if (result != PROP_SUCCESS) {
   
            LOG(ERROR) << "Unable to set property '" << name << "' to '" << value
                       << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
                       << error;
        }
        socket.SendUint32(result);
        break;
      }

    default:
        LOG(ERROR) << "sys_prop: invalid command " << cmd;
        socket.SendUint32(PROP_ERROR_INVALID_CMD);
        break;
    }
}

然后看其中的关键函数HandlePropertySet:

// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                           const std::string& source_context, const ucred& cr, std::string* error) {
   
    if (!IsLegalPropertyName(name)) {
   
        *error = "Illegal property name";
        return PROP_ERROR_INVALID_NAME;
    }

    //判断消息是否是ctl开头,若是则认为是控制消息
    if (StartsWith(name, "ctl.")) {
   
        if (!CheckControlPropertyPerms(name, value, source_context, cr)) {
   
            *error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4,
                                  value.c_str());
            return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
        }

        HandleControlMessage(name.c_str() + 4, value, cr.pid);
        return PROP_SUCCESS;
    }

    const char* target_context = nullptr;
    const char* type = nullptr;
    property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);

    //检查客户端进程是否有足够的权限
    if (!CheckMacPerms(name, target_context, source_context.c_str(), cr)) {
   
        *error = "SELinux permission check failed";
        return PROP_ERROR_PERMISSION_DENIED;
    }

    if (type == nullptr || !CheckType(type, value)) {
   
        *error = StringPrintf("Property type check failed, value doesn't match expected type '%s'",
                              (type ?: "(null)"));
        return PROP_ERROR_INVALID_VALUE;
    }

    // sys.powerctl is a special property that is used to make the device reboot.  We want to log
    // any process that sets this property to be able to accurately blame the cause of a shutdown.
    if (name == "sys.powerctl") {
   
        std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
        std::string process_cmdline;
        std::string process_log_string;
        if (ReadFileToString(cmdline_path, &process_cmdline)) {
   
            // Since cmdline is null deliminated, .c_str() conveniently gives us just the process
            // path.
            process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
        }
        LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
                  << process_log_string;
    }

    if (name == "selinux.restorecon_recursive") {
   
        return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
    }

    return PropertySet(name, value, error);
}

在函数实现的最后返回值会去调用PropertySet函数:

static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
   
    size_t valuelen = value.size();

    //针对属性值做处理
    if (!IsLegalPropertyName(name)) {
   
        *error = "Illegal property name";
        return PROP_ERROR_INVALID_NAME;
    }

    //property定义长度不能超过PROP_VALUE_MAX=92且需要以ro.开头
    if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
   
        *error = "Property value too long";
        return PROP_ERROR_INVALID_VALUE;
    }

    //属性值必须是UTF-8编码
    if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
   
        *error = "Value is not a UTF8 encoded string";
        return PROP_ERROR_INVALID_VALUE;
    }

    prop_info* pi = (prop_info*) __system_property_find(name.c_str());
    if (pi != nullptr) {
   
        // ro.* properties are actually "write-once".
        if (StartsWith(name, "ro.")) {
   
            *error = "Read-only property was already set";
            return PROP_ERROR_READ_ONLY_PROPERTY;
        }

        //会去调用bionic库中定义的方法来更新属性值
        __system_property_update(pi, value.c_str(), valuelen);
    } else {
   
        int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
        if (rc < 0) {
   
            *error = "__system_property_add failed";
            return PROP_ERROR_SET_FAILED;
        }
    }

    // Don't write properties to disk until after we have read all default
    // properties to prevent them from being overwritten by default values.
    if (persistent_properties_loaded && StartsWith(name, "persist.")) {
   
        WritePersistentProperty(name, value);
    }
    property_changed(name, value);
    return PROP_SUCCESS;
}

2.解析init.rc
  1. init.rc简介

init.rc是一个配置文件,内部由Android初始化语言编写(Android Init Language)编写的脚本,它主要包含五中类型语句:Action、Commands、Services、Options、Import。
init.rc文件是在init进程启动后执行的启动脚本,文件中记录着init进程需执行的操作。

A.以"on"关键字开头的动作列表 action list:

on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000
    # Disable sysrq from keyboard
    write /proc/sys/kernel/sysrq 0

Action类型语句格式:

on <trigger> [&& <trigger>]* // 设置触发器 
    <command> <command> // 动作触发之后要执行的命令

动作列表:用于创建所需目录,以及为某些特定文件指定权限。

B.以"service"关键字开头的服务列表 service list:

service ueventd /sbin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0
    shutdown critical

Service类型语句格式:

service <name> <pathname> [ <argument> ]*   // <service的名字><执行程序路径><传递参数>  
   <option>                                 // option是service的修饰词,影响什么时候、如何启动services  
   <option>  
   ...

服务列表:用来记录init进程需要启动的一些子进程,如上面代码所示,service关键字后的第一个字符串表示服务(子进程)的名称,第二个字符串表示服务的执行路径。

  1. init.rc解析过程

LoadBootScripts函数是用来解析init.rc文件的,来看他的实现,代码是在/system/core/init/init.cpp:

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
   
    //初始化Parser解析
    Parser parser = CreateParser(action_manager, service_list);

    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
   
        //开始解析init.rc
        parser.ParseConfig("/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
   
            late_import_paths.emplace_back("/system/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
   
            late_import_paths.emplace_back("/product/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
   
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/vendor/etc/init")) {
   
            late_import_paths.emplace_back("/vendor/etc/init");
        }
    } else {
   
        parser.ParseConfig(bootscript);
    }
}

CreateParser函数:初始化ServiceParser用来解析 “service”块,ActionParser用来解析"on"块,ImportParser用来解析“import”块,“import”是用来引入一个init配置文件,来扩展当前配置的:

Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
   
    Parser parser;

    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));

    return parser;
}

ParseConfig函数。实现是在/system/core/init/parser.cpp中:

bool Parser::ParseConfig(const std::string& path) {
   
    size_t parse_errors;
    return ParseConfig(path, &parse_errors);
}

bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
   
    *parse_errors = 0;
    if (is_dir(path.c_str())) {
   
        return ParseConfigDir(path, parse_errors);
    }
    return ParseConfigFile(path, parse_errors);
}

bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
   
    LOG(INFO) << "Parsing directory " << path << "...";
    std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
    if (!config_dir) {
   
        PLOG(ERROR) << "Could not import directory '" << path << "'";
        return false;
    }
    dirent* current_file;
    std::vector<std::string> files;
    while ((current_file = readdir(config_dir.get()))) {
   
        // Ignore directories and only process regular files.
        if (current_file->d_type == DT_REG) {
   
            std::string current_path =
                android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
            files.emplace_back(current_path);
        }
    }
    // Sort first so we load files in a consistent order (bug 31996208)
    std::sort(files.begin(), files.end());
    for (const auto& file : files) {
   
        if (!ParseConfigFile(file, parse_errors)) {
   
            LOG(ERROR) << "could not import file '" << file << "'";
        }
    }
    return true;
}

bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
   
    LOG(INFO) << "Parsing file " << path << "...";
    android::base::Timer t;
    auto config_contents = ReadFile(path);
    if (!config_contents) {
   
        LOG(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
        return false;
    }

    config_contents->push_back('\n');  // TODO: fix parse_config.
    ParseData(path, *config_contents, parse_errors);
    for (const auto& [section_name, section_parser] : section_parsers_) {
   
        section_parser->EndFile();
    }

    LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
    return true;
}

可以看到在Pareser.cpp中一系列的调用,从ParseConfig函数到ParseConfigDir函数,再到ParseConfigFile函数。其中最终调用的是ParseData函数:

void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
   
    // TODO: Use a parser with const input and remove this copy
    std::vector<char> data_copy(data.begin(), data.end());
    data_copy.push_back('\0');

    parse_state state;
    state.line = 0;
    state.ptr = &data_copy[0];
    state.nexttoken = 0;

    SectionParser* section_parser = nullptr;
    int section_start_line = -1;
    std::vector<std::string> args;

    auto end_section = [&] {
   
        if (section_parser == nullptr) return;

        if (auto result = section_parser->EndSection(); !result) {
   
            (*parse_errors)++;
            LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
        }

        section_parser = nullptr;
        section_start_line = -1;
    };

    //无限循环
    for (;;) {
   
		//获取关键字类型
        switch (next_token(&state)) {
   
            case T_EOF:
                end_section();
                return;
            case T_NEWLINE:
                state.line++;
                if (args.empty()) break;
                // If we have a line matching a prefix we recognize, call its callback and unset any
                // current section parsers.  This is meant for /sys/ and /dev/ line entries for
                // uevent.
                for (const auto& [prefix, callback] : line_callbacks_) {
   
                    if (android::base::StartsWith(args[0], prefix)) {
   
                        end_section();

                        if (auto result = callback(std::move(args)); !result) {
   
                            (*parse_errors)++;
                            LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                        }
                        break;
                    }
                }
                //前面中CreateParser函数,我们针对service,on,import定义了对应的parser。
                //这里就是根据第一个参数,判断是否有对应的parser
                if (section_parsers_.count(args[0])) {
   
                    end_section();
                    //获取参数对应的parser
                    section_parser = section_parsers_[args[0]].get();
                    section_start_line = state.line;
                    //调用实际parser的ParseSection函数
                    if (auto result 
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值