1.init簡介
init進程是Android系統(tǒng)中用戶空間的第一個進程析孽,作為第一個進程侨拦,它被賦予了很多極其重要的工作職責(zé)酱讶,比如創(chuàng)建zygote(孵化器)和屬性服務(wù)等裆赵。init進程是由多個源文件共同組成的,這些文件位于源碼目錄system/core/init之剧。本文將基于Android7.0源碼來分析Init進程郭卫。
2.引入init進程
說到init進程,首先要提到Android系統(tǒng)啟動流程的前幾步:
1.啟動電源以及系統(tǒng)啟動
當(dāng)電源按下時引導(dǎo)芯片代碼開始從預(yù)定義的地方(固化在ROM)開始執(zhí)行背稼。加載引導(dǎo)程序Bootloader到RAM贰军,然后執(zhí)行。
2.引導(dǎo)程序BootLoader
引導(dǎo)程序BootLoader是在Android操作系統(tǒng)開始運行前的一個小程序蟹肘,它的主要作用是把系統(tǒng)OS拉起來并運行词疼。
3.Linux內(nèi)核啟動
內(nèi)核啟動時,設(shè)置緩存帘腹、被保護存儲器贰盗、計劃列表,加載驅(qū)動阳欲。當(dāng)內(nèi)核完成系統(tǒng)設(shè)置舵盈,它首先在系統(tǒng)文件中尋找”init”文件,然后啟動root進程或者系統(tǒng)的第一個進程球化。
4.init進程啟動
3 init入口函數(shù)
- init的入口函數(shù)為main
- init的main方法做了很多事情
我們只需要關(guān)注主要的幾點- 在注釋1處調(diào)用 property_init來對屬性進行初始化
- 在注釋2處的 調(diào)用start_property_service啟動屬性服務(wù)秽晚,關(guān)于屬性服務(wù),后面會講到筒愚。
- 注釋3處 parser.ParseConfig(“/init.rc”)用來解析init.rc赴蝇。解析init.rc的文件為system/core/init/init_parse.cpp文件,接下來我們查看init.rc里做了什么巢掺。
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}
umask(0);
add_environment("PATH", _PATH_DEFPATH);
bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
//創(chuàng)建文件并掛載
if (is_first_stage) {
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
#define MAKE_STR(x) __STRING(x)
mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
mount("sysfs", "/sys", "sysfs", 0, NULL);
}
open_devnull_stdio();
klog_init();
klog_set_level(KLOG_NOTICE_LEVEL);
NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
if (!is_first_stage) {
// Indicate that booting is in progress to background fw loaders, etc.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
//初始化屬性相關(guān)資源
property_init();//1
process_kernel_dt();
process_kernel_cmdline();
export_kernel_boot_props();
}
...
//啟動屬性服務(wù)
start_property_service();//2
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
Parser& parser = Parser::GetInstance();
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
//解析init.rc配置文件
parser.ParseConfig("/init.rc");//3
...
while (true) {
if (!waiting_for_exec) {
am.ExecuteOneCommand();
restart_processes();
}
int timeout = -1;
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if (timeout < 0)
timeout = 0;
}
if (am.HasMoreCommands()) {
timeout = 0;
}
bootchart_sample(&timeout);
epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
if (nr == -1) {
ERROR("epoll_wait failed: %s\n", strerror(errno));
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}
return 0;
}
4 init.rc
- init.rc是一個配置文件
- 內(nèi)部由Android初始化語言編寫(Android Init Language)編寫的腳本句伶,它主要包含五種類型語句:Action、Commands陆淀、Services考余、Options和Import。
init.rc的配置代碼如下所示
system/core/rootdir/init.rc
on init
sysclktz 0
# Mix device-specific information into the entropy pool
copy /proc/cmdline /dev/urandom
copy /default.prop /dev/urandom
...
on boot
# basic network init
ifup lo
hostname localhost
domainname localdomain
# set RLIMIT_NICE to allow priorities from 19 to -20
setrlimit 13 40 40
...
這里只截取了一部分代碼倔约,其中#是注釋符號秃殉。on init和on boot是Action類型語句坝初,它的格式為:
on <trigger> [&& <trigger>]* //設(shè)置觸發(fā)器
<command>
<command> //動作觸發(fā)之后要執(zhí)行的命令
為了分析如何創(chuàng)建zygote浸剩,我們主要查看Services類型語句钾军,它的格式如下所示:
service <name> <pathname> [ <argument> ]* //<service的名字><執(zhí)行程序路徑><傳遞參數(shù)>
<option> //option是service的修飾詞,影響什么時候绢要、如何啟動services
<option>
...
需要注意的是在Android 7.0中對init.rc文件進行了拆分吏恭,每個服務(wù)一個rc文件。我們要分析的zygote服務(wù)的啟動腳本則在init.zygoteXX.rc中定義重罪,這里拿64位處理器為例樱哼,init.zygote64.rc的代碼如下所示。
system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
其中service用于通知init進程創(chuàng)建名zygote的進程剿配,這個zygote進程執(zhí)行程序的路徑為/system/bin/app_process64搅幅,后面的則是要傳給app_process64的參數(shù)。class main指的是zygote的class name為main呼胚,后文會用到它茄唐。
5.解析service
接下來我們來解析service,會用到兩個函數(shù)蝇更,一個是ParseSection沪编,它會解析service的rc文件,比如上文講到的init.zygote64.rc年扩,ParseSection函數(shù)主要用來搭建service的架子蚁廓。另一個是ParseLineSection,用于解析子項厨幻。代碼如下所示相嵌。
system/core/init/service.cpp
bool ServiceParser::ParseSection(const std::vector<std::string>& args,
std::string* err) {
if (args.size() < 3) {
*err = "services must have a name and a program";
return false;
}
const std::string& name = args[1];
if (!IsValidName(name)) {
*err = StringPrintf("invalid service name '%s'", name.c_str());
return false;
}
std::vector<std::string> str_args(args.begin() + 2, args.end());
//注釋1,根據(jù)參數(shù)况脆,構(gòu)造出一個service對象平绩,它的classname為”default”。
service_ = std::make_unique<Service>(name, "default", str_args);//1
return true;
}
bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
const std::string& filename, int line,
std::string* err) const {
return service_ ? service_->HandleLine(args, err) : false;
}
注釋1處漠另,根據(jù)參數(shù)捏雌,構(gòu)造出一個service對象,它的classname為”default”笆搓。當(dāng)解析完畢時會調(diào)用EndSection:
void ServiceParser::EndSection() {
if (service_) {
ServiceManager::GetInstance().AddService(std::move(service_));
}
}
接著查看AddService做了什么:
void ServiceManager::AddService(std::unique_ptr<Service> service) {
Service* old_service = FindServiceByName(service->name());
if (old_service) {
ERROR("ignored duplicate definition of service '%s'",
service->name().c_str());
return;
}
services_.emplace_back(std::move(service));//1
}
注釋1處的代碼將service對象加入到services鏈表中性湿。上面的解析過程總體來講就是根據(jù)參數(shù)創(chuàng)建出service對象,然后根據(jù)選項域的內(nèi)容填充service對象满败,最后將service對象加入到vector類型的services鏈表中肤频。,
6 init啟動zygote
講完了解析service算墨,接下來該講init是如何啟動service宵荒,在這里我們主要講解啟動zygote這個service。在zygote的啟動腳本中我們得知zygote的class name為main。在init.rc有如下配置代碼:
system/core/rootdir/init.rc
...
on nonencrypted
# A/B update verifier that marks a successful boot.
exec - root -- /system/bin/update_verifier nonencrypted
class_start main
class_start late_start
...
其中class_start是一個COMMAND报咳,對應(yīng)的函數(shù)為do_class_start侠讯。我們知道m(xù)ain指的就是zygote,因此class_start main用來啟動zygote暑刃。do_class_start函數(shù)在builtins.cpp中定義厢漩,如下所示。
system/core/init/builtins.cpp
static int do_class_start(const std::vector<std::string>& args) {
/* Starting a class does not start services
* which are explicitly disabled. They must
* be started individually.
*/
ServiceManager::GetInstance().
ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
return 0;
}
system/core/init/service.cpp
bool Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) {
return Start();
} else {
flags_ |= SVC_DISABLED_START;
}
return true;
}
接著查看Start方法岩臣,如下所示溜嗜。
bool Service::Start() {
flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
time_started_ = 0;
if (flags_ & SVC_RUNNING) {//如果Service已經(jīng)運行,則不啟動
return false;
}
bool needs_console = (flags_ & SVC_CONSOLE);
if (needs_console && !have_console) {
ERROR("service '%s' requires console\n", name_.c_str());
flags_ |= SVC_DISABLED;
return false;
}
//判斷需要啟動的Service的對應(yīng)的執(zhí)行文件是否存在架谎,不存在則不啟動該Service
struct stat sb;
if (stat(args_[0].c_str(), &sb) == -1) {
ERROR("cannot find '%s' (%s), disabling '%s'\n",
args_[0].c_str(), strerror(errno), name_.c_str());
flags_ |= SVC_DISABLED;
return false;
}
...
pid_t pid = fork();//1.fork函數(shù)創(chuàng)建子進程
if (pid == 0) {//運行在子進程中
umask(077);
for (const auto& ei : envvars_) {
add_environment(ei.name.c_str(), ei.value.c_str());
}
for (const auto& si : sockets_) {
int socket_type = ((si.type == "stream" ? SOCK_STREAM :
(si.type == "dgram" ? SOCK_DGRAM :
SOCK_SEQPACKET)));
const char* socketcon =
!si.socketcon.empty() ? si.socketcon.c_str() : scon.c_str();
int s = create_socket(si.name.c_str(), socket_type, si.perm,
si.uid, si.gid, socketcon);
if (s >= 0) {
PublishSocket(si.name, s);
}
}
...
//2.通過execve執(zhí)行程序
if (execve(args_[0].c_str(), (char**) &strs[0], (char**) ENV) < 0) {
ERROR("cannot execve('%s'): %s\n", args_[0].c_str(), strerror(errno));
}
_exit(127);
}
...
return true;
}
通過注釋1和2的代碼炸宵,我們得知在Start方法中調(diào)用fork函數(shù)來創(chuàng)建子進程,并在子進程中調(diào)用execve執(zhí)行system/bin/app_process谷扣,這樣就會進入framework/cmds/app_process/app_main.cpp的main函數(shù)焙压,如下所示。
frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
...
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);//1
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
return 10;
}
}
從注釋1處的代碼可以得知調(diào)用runtime(AppRuntime)的start來啟動zygote抑钟。
7 屬性服務(wù)
Windows平臺上有一個注冊表管理器涯曲,注冊表的內(nèi)容采用鍵值對的形式來記錄用戶、軟件的一些使用信息在塔。即使系統(tǒng)或者軟件重啟幻件,它還是能夠根據(jù)之前在注冊表中的記錄,進行相應(yīng)的初始化工作蛔溃。Android也提供了一個類似的機制绰沥,叫做屬性服務(wù)。
system/core/init/init.cpp
property_init();
start_property_service();
這兩句代碼用來初始化屬性服務(wù)配置并啟動屬性服務(wù)贺待。首先我們來學(xué)習(xí)服務(wù)配置的初始化和啟動徽曲。
屬性服務(wù)初始化與啟動
property_init函數(shù)具體實現(xiàn)的代碼如下所示。
system/core/init/property_service.cpp
void property_init() {
if (__system_property_area_init()) {
ERROR("Failed to initialize property area\n");
exit(1);
}
}
__system_property_area_init函數(shù)用來初始化屬性內(nèi)存區(qū)域麸塞。接下來查看start_property_service函數(shù)的具體代碼:
void start_property_service() {
//1創(chuàng)建非阻塞的socket
property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
0666, 0, 0, NULL);
if (property_set_fd == -1) {
ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
exit(1);
}
//注釋2處調(diào)用listen函數(shù)對property_set_fd進行監(jiān)聽秃臣,也就是屬性服務(wù);
//listen函數(shù)的第二個參數(shù)設(shè)置8意味著屬性服務(wù)最多可以同時為8個試圖設(shè)置屬性的用戶提供服務(wù)哪工。
listen(property_set_fd, 8);
//注釋3處的代碼將property_set_fd放入了epoll句柄中奥此,用epoll來監(jiān)聽property_set_fd:當(dāng)property_set_fd中有數(shù)據(jù)到來時,init進程將用handle_property_set_fd函數(shù)進行處理雁比。
register_epoll_handler(property_set_fd, handle_property_set_fd);//3
}
在linux新的內(nèi)核中稚虎,epoll用來替換select,epoll最大的好處在于它不會隨著監(jiān)聽fd數(shù)目的增長而降低效率偎捎。因為內(nèi)核中的select實現(xiàn)是采用輪詢來處理的蠢终,輪詢的fd數(shù)目越多序攘,自然耗時越多。
**屬性服務(wù)處理請求 **
從上文我們得知寻拂,屬性服務(wù)接收到客戶端的請求時程奠,會調(diào)用handle_property_set_fd函數(shù)進行處理:
system/core/init/property_service.cpp
static void handle_property_set_fd()
{
...
if(memcmp(msg.name,"ctl.",4) == 0) {
close(s);
if (check_control_mac_perms(msg.value, source_ctx, &cr)) {
handle_control_message((char*) msg.name + 4, (char*) msg.value);
} else {
ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
}
} else {
//檢查客戶端進程權(quán)限
if (check_mac_perms(msg.name, source_ctx, &cr)) {//1
//調(diào)用property_set函數(shù)對屬性進行修改
property_set((char*) msg.name, (char*) msg.value);//2
} else {
ERROR("sys_prop: permission denied uid:%d name:%s\n",
cr.uid, msg.name);
}
close(s);
}
freecon(source_ctx);
break;
default:
close(s);
break;
}
}
int property_set(const char* name, const char* value) {
int rc = property_set_impl(name, value);
if (rc == -1) {
ERROR("property_set(\"%s\", \"%s\") failed\n", name, value);
}
return rc;
}
property_set函數(shù)主要調(diào)用了property_set_impl函數(shù):
static int property_set_impl(const char* name, const char* value) {
size_t namelen = strlen(name);
size_t valuelen = strlen(value);
if (!is_legal_property_name(name, namelen)) return -1;
if (valuelen >= PROP_VALUE_MAX) return -1;
if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) {
if (selinux_reload_policy() != 0) {
ERROR("Failed to reload policy\n");
}
} else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
if (restorecon_recursive(value) != 0) {
ERROR("Failed to restorecon_recursive %s\n", value);
}
}
//從屬性存儲空間查找該屬性
prop_info* pi = (prop_info*) __system_property_find(name);
//如果屬性存在
if(pi != 0) {
//如果屬性以"ro."開頭,則表示是只讀兜喻,不能修改梦染,直接返回
if(!strncmp(name, "ro.", 3)) return -1;
//更新屬性值
__system_property_update(pi, value, valuelen);
} else {
//如果屬性不存在則添加該屬性
int rc = __system_property_add(name, namelen, value, valuelen);
if (rc < 0) {
return rc;
}
}
/* If name starts with "net." treat as a DNS property. */
if (strncmp("net.", name, strlen("net.")) == 0) {
if (strcmp("net.change", name) == 0) {
return 0;
}
//以net.開頭的屬性名稱更新后赡麦,需要將屬性名稱寫入net.change中
property_set("net.change", name);
} else if (persistent_properties_loaded &&
strncmp("persist.", name, strlen("persist.")) == 0) {
/*
* Don't write properties to disk until after we have read all default properties
* to prevent them from being overwritten by default values.
*/
write_persistent_property(name, value);
}
property_changed(name, value);
return 0;
}
property_set_impl函數(shù)主要用來對屬性進行修改朴皆,并對以ro、net和persist開頭的屬性進行相應(yīng)的處理泛粹。到這里遂铡,屬性服務(wù)處理請求的源碼就講到這。
8 init進程總結(jié)
- 講到這晶姊,總結(jié)起來init進程主要做了三件事:
- 1.創(chuàng)建一些文件夾并掛載設(shè)備
- 2.初始化和啟動屬性服務(wù)
- 3.解析init.rc配置文件并啟動zygote進程
參考
https://blog.csdn.net/itachi85/article/details/56669808
https://blog.csdn.net/itachi85/article/details/54783506