Android源码下载:https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/

? ? ? 源码编译可参考【牛肉面大神之作】:http://blog.csdn.net/cjpx00008/article/details/60474883

  【开篇说明】

  在【Android启示录】中,提到了主要的分析对象和分享内容,抛开Android内核级的知识点,学习Android第一步便是“init”,作为天字第一号进程,代码羞涩难懂,但是也极其重要,熟悉init的原理对后面Zygote -- SystemServer -- 核心服务等一些列源码的研究是有很大作用的,所以既然说研究Android源码,就先拿init “庖丁解牛”!

  【正文开始】

  Init进程,它是一个由内核启动的用户级进程,当Linux内核启动之后,运行的第一个进程是init,这个进程是一个守护进程,确切的说,它是Linux系统中用户控件的第一个进程,所以它的进程号是1。它的生命周期贯穿整个linux 内核运行的始终, linux中所有其它的进程的共同始祖均为init进程,可以通过“adb shell ps | grep init”查看进程号。

  Android init进程的入口文件在system/core/init/init.cpp中,由于init是命令行程序,所以分析init.cpp首先应从main函数开始:

Java代码
  1. int?main(int?argc,?char**?argv)?{????//?入口函数main??
  2. ????if?(!strcmp(basename(argv[0]),?"ueventd"))?{??
  3. ????????return?ueventd_main(argc,?argv);??
  4. ????}??
  5. ??
  6. ????if?(!strcmp(basename(argv[0]),?"watchdogd"))?{??
  7. ????????return?watchdogd_main(argc,?argv);??
  8. ????}??
  9. ??
  10. ????//?Clear?the?umask.??
  11. ????umask(0);????//?清除屏蔽字(file?mode?creation?mask),保证新建的目录的访问权限不受屏蔽字影响。??
  12. ????add_environment("PATH",?_PATH_DEFPATH);??
  13. ??????
  14. ????bool?is_first_stage?=?(argc?==?1)?||?(strcmp(argv[1],?"--second-stage")?!=?0);????//?判断是否是系统启动的第一阶段,只有启动参数中有--second-stage才为第二阶段??
  15. ????//?Get?the?basic?filesystem?setup?we?need?put?together?in?the?initramdisk???
  16. ????//?on?/?and?then?we'll?let?the?rc?file?figure?out?the?rest.???
  17. ????if?(is_first_stage)?{???
  18. ????????mount("tmpfs",?"/dev",?"tmpfs",?MS_NOSUID,?"mode=0755");????????????????????????//?挂载tmpfs文件系统???
  19. ????????mkdir("/dev/pts",?0755);???
  20. ????????mkdir("/dev/socket",?0755);???
  21. ????????mount("devpts",?"/dev/pts",?"devpts",?0,?NULL);?????????????????????????????????//?挂载devpts文件系统???
  22. ????????#define?MAKE_STR(x)?__STRING(x)???
  23. ????????mount("proc",?"/proc",?"proc",?0,?"hidepid=2,gid="?MAKE_STR(AID_READPROC));?????//?挂载proc文件系统???
  24. ????????mount("sysfs",?"/sys",?"sysfs",?0,?NULL);???????????????????????????????????????//?挂载sysfs文件系统???
  25. ????}??

  以上代码主要做的工作就是:【创建文件系统目录并挂载相关的文件系统】

Java代码
  1. int?main(int?argc,?char**?argv)?{??
  2. ????/*?01.?创建文件系统目录并挂载相关的文件系统?*/??
  3. ????/*?02.?屏蔽标准的输入输出/初始化内核log系统?*/??
  4. ????//?We?must?have?some?place?other?than?/?to?create?the?device?nodes?for??
  5. ????//?kmsg?and?null,?otherwise?we?won't?be?able?to?remount?/?read-only??
  6. ????//?later?on.?Now?that?tmpfs?is?mounted?on?/dev,?we?can?actually?talk??
  7. ????//?to?the?outside?world.??
  8. ????open_devnull_stdio();????//?重定向标准输入输出到/dev/_null_??-->??定义在system/core/init/Util.cpp中??
  9. ????//?init进程通过klog_init函数,提供输出log信息的设备??-->??定义在system/core/libcutils/Klog.c中??
  10. ????klog_init();??????//?对klog进行初始化???????????
  11. ????klog_set_level(KLOG_NOTICE_LEVEL);??//?NOTICE?level??

  继续分析源码,接下来要做的就是初始化属性域:

Java代码
  1. int?main(int?argc,?char**?argv)?{??
  2. ????/*?01.?创建文件系统目录并挂载相关的文件系统?*/??
  3. ????/*?02.?屏蔽标准的输入输出/初始化内核log系统?*/??
  4. ????/*?03.?初始化属性域?*/??
  5. ????NOTICE("init?%s?started!\n",?is_first_stage???"first?stage"?:?"second?stage");??
  6. ????if?(!is_first_stage)?{??????//?引入SELinux机制后,通过is_first_stage区分init运行状态??
  7. ????????//?Indicate?that?booting?is?in?progress?to?background?fw?loaders,?etc.??
  8. ????????close(open("/dev/.booting",?O_WRONLY?|?O_CREAT?|?O_CLOEXEC,?0000));??????/*?检测/dev/.booting文件是否可读写、创建等*/??
  9. ????????property_init();????????//?初始化属性域?-->?定义于system/core/init/Property_service.cpp??
  10. ??
  11. ????????//?If?arguments?are?passed?both?on?the?command?line?and?in?DT,??
  12. ????????//?properties?set?in?DT?always?have?priority?over?the?command-line?ones.??
  13. ????????process_kernel_dt();??
  14. ????????process_kernel_cmdline();?????//?处理内核命令行??
  15. ????????//?Propagate?the?kernel?variables?to?internal?variables??
  16. ????????//?used?by?init?as?well?as?the?current?required?properties.??
  17. ????????export_kernel_boot_props();??
  18. ????}??

  看一下property_init方法:位于system/core/init/Property_service.cpp中

Java代码
  1. void?property_init()?{??
  2. ????if?(__system_property_area_init())?{?????????//?调用此函数初始化属性域??
  3. ????????ERROR("Failed?to?initialize?property?area\n");??
  4. ????????exit(1);??
  5. ????}??
  6. }??

? ? ? 继续分析main函数:

Java代码
  1. int?main(int?argc,?char**?argv)?{??
  2. ????/*?01.?创建文件系统目录并挂载相关的文件系统?*/??
  3. ????/*?02.?屏蔽标准的输入输出/初始化内核log系统?*/??
  4. ????/*?03.?初始化属性域?*/??
  5. ????/*?04.?完成SELinux相关工作?*/??
  6. ????//?Set?up?SELinux,?including?loading?the?SELinux?policy?if?we're?in?the?kernel?domain.??
  7. ????selinux_initialize(is_first_stage);?????//?调用selinux_initialize启动SELinux??

  详细看一下selinux_initialize()函数:

Java代码
  1. static?void?selinux_initialize(bool?in_kernel_domain)?{?????//?区分内核态和用户态??
  2. ????Timer?t;??????//使用Timer计时,计算selinux初始化耗时??
  3. ??
  4. ????selinux_callback?cb;??
  5. ????cb.func_log?=?selinux_klog_callback;??????????????//?用于打印Log的回调函数??
  6. ????selinux_set_callback(SELINUX_CB_LOG,?cb);??
  7. ????cb.func_audit?=?audit_callback;????????????????????//?用于检查权限的回调函数??
  8. ????selinux_set_callback(SELINUX_CB_AUDIT,?cb);??
  9. ??
  10. ????if?(in_kernel_domain)?{????????//?内核态处理流程,第一阶段in_kernel_domain为true????
  11. ????????INFO("Loading?SELinux?policy...\n");????????//?该行log打印不出,INFO级别???
  12. ????????//?用于加载sepolicy文件。该函数最终将sepolicy文件传递给kernel,这样kernel就有了安全策略配置文件??
  13. ????????if?(selinux_android_load_policy()?0)?{??
  14. ????????????ERROR("failed?to?load?policy:?%s\n",?strerror(errno));??
  15. ????????????security_failure();??
  16. ????????}??
  17. ??
  18. ????????bool?kernel_enforcing?=?(security_getenforce()?==?1);??????//?内核中读取的信息??
  19. ????????bool?is_enforcing?=?selinux_is_enforcing();????????????????//?命令行中得到的信息??
  20. ????????if?(kernel_enforcing?!=?is_enforcing)?{??
  21. ????????//?用于设置selinux的工作模式。selinux有两种工作模式:??
  22. ????????????//?1、”permissive”,所有的操作都被允许(即没有MAC),但是如果违反权限的话,会记录日志??
  23. ????????????//?2、”enforcing”,所有操作都会进行权限检查。在一般的终端中,应该工作于enforing模式??
  24. ????????????if?(security_setenforce(is_enforcing))?{????????//设置selinux的模式,是开还是关??
  25. ????????????????ERROR("security_setenforce(%s)?failed:?%s\n",??
  26. ??????????????????????is_enforcing???"true"?:?"false",?strerror(errno));??
  27. ????????????????security_failure();????//?将重启进入recovery?mode??
  28. ????????????}??
  29. ????????}??
  30. ??
  31. ????????if?(write_file("/sys/fs/selinux/checkreqprot",?"0")?==?-1)?{??
  32. ????????????security_failure();??
  33. ????????}??
  34. ??
  35. ????????NOTICE("(Initializing?SELinux?%s?took?%.2fs.)\n",??
  36. ???????????????is_enforcing???"enforcing"?:?"non-enforcing",?t.duration());???//输出selinux的模式,与初始化耗时??
  37. ????}?else?{???
  38. ????????selinux_init_all_handles();?//如果启动第二阶段,调用该函数?????
  39. ????}???
  40. }??

  回到main函数中继续分析:

Java代码
  1. int?main(int?argc,?char**?argv)?{??
  2. ????/*?01.?创建文件系统目录并挂载相关的文件系统?*/??
  3. ????/*?02.?屏蔽标准的输入输出/初始化内核log系统?*/??
  4. ????/*?03.?初始化属性域?*/??
  5. ????/*?04.?完成SELinux相关工作?*/??
  6. ????/*?05.?重新设置属性?*/??
  7. ????//?If?we're?in?the?kernel?domain,?re-exec?init?to?transition?to?the?init?domain?now??
  8. ????//?that?the?SELinux?policy?has?been?loaded.??
  9. ????if?(is_first_stage)?{??
  10. ????????if?(restorecon("/init")?==?-1)?{????//?按selinux?policy要求,重新设置init文件属性??
  11. ????????????ERROR("restorecon?failed:?%s\n",?strerror(errno));??
  12. ????????????security_failure();??
  13. ????????}??
  14. ????????char*?path?=?argv[0];??
  15. ????????char*?args[]?=?{?path,?const_cast<char*>("--second-stage"),?nullptr?};?????//设置参数--second-stage??
  16. ???
  17. ??
  18. ????if?(execv(path,?args)?==?-1)?{????????//?执行init进程,重新进入main函数??
  19. ????????????ERROR("execv(\"%s\")?failed:?%s\n",?path,?strerror(errno));??
  20. ????????????security_failure();??
  21. ????????}??
  22. ????}??
  23. ??
  24. ????//?These?directories?were?necessarily?created?before?initial?policy?load??
  25. ????//?and?therefore?need?their?security?context?restored?to?the?proper?value.??
  26. ????//?This?must?happen?before?/dev?is?populated?by?ueventd.??
  27. ????NOTICE("Running?restorecon...\n");??
  28. ????restorecon("/dev");??
  29. ????restorecon("/dev/socket");??
  30. ????restorecon("/dev/__properties__");??
  31. ????restorecon("/property_contexts");??
  32. ??
  33. ????restorecon_recursive("/sys");??
  34. ??
  35. ????epoll_fd?=?epoll_create1(EPOLL_CLOEXEC);?????????//?调用epoll_create1创建epoll句柄??
  36. ????if?(epoll_fd?==?-1)?{??
  37. ????????ERROR("epoll_create1?failed:?%s\n",?strerror(errno));??
  38. ????????exit(1);??
  39. ????}??

  接着往下分析:

Java代码
  1. int?main(int?argc,?char**?argv)?{??
  2. ????/*?01.?创建文件系统目录并挂载相关的文件系统?*/??
  3. ????/*?02.?屏蔽标准的输入输出/初始化内核log系统?*/??
  4. ????/*?03.?初始化属性域?*/??
  5. ????/*?04.?完成SELinux相关工作?*/·??
  6. ????/*?05.?重新设置属性?*/??
  7. ????/*?06.?创建epoll句柄?*/??
  8. ????/*?07.?装载子进程信号处理器?*/??
  9. ????signal_handler_init();???????//?装载子进程信号处理器??

  Note:init是一个守护进程,为了防止init的子进程成为僵尸进程(zombie process),需要init在子进程结束时获取子进程的结束码,通过结束码将程序表中的子进程移除,防止成为僵尸进程的子进程占用程序表的空间(程序表的空间达到上限时,系统就不能再启动新的进程了,会引起严重的系统问题)。

  细化signal_handler_init()函数:

Java代码
  1. void?signal_handler_init()?{????????//?函数定位于:system/core/init/Singal_handler.cpp??
  2. ????//?在linux当中,父进程是通过捕捉SIGCHLD信号来得知子进程运行结束的情况??
  3. ????//?Create?a?signalling?mechanism?for?SIGCHLD.??
  4. ????int?s[2];??
  5. ????//?利用socketpair创建出已经连接的两个socket,分别作为信号的读、写端??
  6. ????if?(socketpair(AF_UNIX,?SOCK_STREAM?|?SOCK_NONBLOCK?|?SOCK_CLOEXEC,?0,?s)?==?-1)?{??
  7. ????????ERROR("socketpair?failed:?%s\n",?strerror(errno));??
  8. ????????exit(1);??
  9. ????}??
  10. ??
  11. ????signal_write_fd?=?s[0];??
  12. ????signal_read_fd?=?s[1];??
  13. ??
  14. ????//?Write?to?signal_write_fd?if?we?catch?SIGCHLD.??
  15. ????struct?sigaction?act;??
  16. ????memset(&act,?0,?sizeof(act));??
  17. ????//?信号处理器为SIGCHLD_handler,其被存在sigaction结构体中,负责处理SIGCHLD消息??
  18. ????act.sa_handler?=?SIGCHLD_handler;???????//?信号处理器:SIGCHLD_handler??
  19. ????act.sa_flags?=?SA_NOCLDSTOP;????????????//?仅当进程终止时才接受SIGCHLD信号??
  20. ????//?调用信号安装函数sigaction,将监听的信号及对应的信号处理器注册到内核中??
  21. ????sigaction(SIGCHLD,?&act,?0);??
  22. ????//?相对于6.0的代码,进一步作了封装,用于终止出现问题的子进程??
  23. ????ServiceManager::GetInstance().ReapAnyOutstandingChildren();??
  24. ??
  25. ????register_epoll_handler(signal_read_fd,?handle_signal);????????//?定义在system/core/init/Init.cpp??
  26. }??

  Linux进程通过互相发送接收消息来实现进程间的通信,这些消息被称为“信号”。每个进程在处理其它进程发送的信号时都要注册处理者,处理者被称为信号处理器。

  注意到sigaction结构体的sa_flags为SA_NOCLDSTOP。由于系统默认在子进程暂停时也会发送信号SIGCHLD,init需要忽略子进程在暂停时发出的SIGCHLD信号,因此将act.sa_flags 置为SA_NOCLDSTOP,该标志位表示仅当进程终止时才接受SIGCHLD信号。

  观察SIGCHLD_handler具体工作:

Java代码
  1. static?void?SIGCHLD_handler(int)?{??
  2. ????/*?init进程是所有进程的父进程,当其子进程终止产生SIGCHLD信号时,SIGCHLD_handler对signal_write_fd执行写操作,由于socketpair的绑定关系,这将触发信号对应的signal_read_fd收到数据。*/??
  3. ????if?(TEMP_FAILURE_RETRY(write(signal_write_fd,?"1",?1))?==?-1)?{??
  4. ????????ERROR("write(signal_write_fd)?failed:?%s\n",?strerror(errno));??
  5. ????}??
  6. }??

  在装在信号监听器的最后,有如下函数:register_epoll_handler(signal_read_fd, handle_signal);

Java代码
  1. void?register_epoll_handler(int?fd,?void?(*fn)())?{????????//?回到init.cpp中??
  2. ????epoll_event?ev;??
  3. ????ev.events?=?EPOLLIN;??
  4. ????ev.data.ptr?=?reinterpret_cast<void*>(fn);??
  5. ????//?epoll_fd增加一个监听对象fd,fd上有数据到来时,调用fn处理??
  6. ????//?当epoll句柄监听到signal_read_fd中有数据可读时,将调用handle_signal进行处理。??
  7. ????if?(epoll_ctl(epoll_fd,?EPOLL_CTL_ADD,?fd,?&ev)?==?-1)?{??
  8. ????????ERROR("epoll_ctl?failed:?%s\n",?strerror(errno));??
  9. ????}??
  10. }??

  【小结】

  当init进程调用signal_handler_init后,一旦收到子进程终止带来的SIGCHLD消息后,将利用信号处理者SIGCHLD_handler向signal_write_fd写入信息; epoll句柄监听到signal_read_fd收消息后,将调用handle_signal进行处理。

Android启动篇 — init原理(一)

  查看handle_signal函数:

Java代码
  1. static?void?handle_signal()?{??????//?-->?位于system/core/init/signal_handler.cpp中??
  2. ????//?Clear?outstanding?requests.??
  3. ????char?buf[32];??
  4. ????read(signal_read_fd,?buf,?sizeof(buf));??
  5. ??
  6. ????ServiceManager::GetInstance().ReapAnyOutstandingChildren();??
  7. }??

  从代码中可以看出,handle_signal只是清空signal_read_fd中的数据,然后调用ServiceManager::GetInstance().ReapAnyOutstandingChildren()。

  继续分析:

Java代码
  1. //?定义于system/core/init/service.cpp中,是一个单例对象。??
  2. ServiceManager::ServiceManager()?{?????//?默认private属性??
  3. }??
  4. ??
  5. ServiceManager&?ServiceManager::GetInstance()?{??
  6. ????static?ServiceManager?instance;??
  7. ????return?instance;??
  8. }??
  9. void?ServiceManager::ReapAnyOutstandingChildren()?{??
  10. ????while?(ReapOneProcess())?{????//?实际调用了ReapOneProcess函数??
  11. ????}??
  12. }??

  接下来看下ReapOneProcess这个函数:

Java代码
  1. bool?ServiceManager::ReapOneProcess()?{??
  2. ????int?status;??
  3. ????//用waitpid函数获取状态发生变化的子进程pid??
  4. ????//waitpid的标记为WNOHANG,即非阻塞,返回为正值就说明有进程挂掉了??
  5. ????pid_t?pid?=?TEMP_FAILURE_RETRY(waitpid(-1,?&status,?WNOHANG));??
  6. ????if?(pid?==?0)?{??
  7. ????????return?false;??
  8. ????}?else?if?(pid?==?-1)?{??
  9. ????????ERROR("waitpid?failed:?%s\n",?strerror(errno));??
  10. ????????return?false;??
  11. ????}??
  12. ????//?利用FindServiceByPid函数,找到pid对应的服务。??
  13. ????//?FindServiceByPid主要通过轮询解析init.rc生成的service_list,找到pid与参数一直的svc??
  14. ????Service*?svc?=?FindServiceByPid(pid);??
  15. ??????
  16. ????std::string?name;??
  17. ????if?(svc)?{??
  18. ????????name?=?android::base::StringPrintf("Service?'%s'?(pid?%d)",??
  19. ???????????????????????????????????????????svc->name().c_str(),?pid);??
  20. ????}?else?{??
  21. ????????name?=?android::base::StringPrintf("Untracked?pid?%d",?pid);??
  22. ????}??
  23. ??
  24. ????if?(WIFEXITED(status))?{??
  25. ????????NOTICE("%s?exited?with?status?%d\n",?name.c_str(),?WEXITSTATUS(status));??
  26. ????}?else?if?(WIFSIGNALED(status))?{??
  27. ????????NOTICE("%s?killed?by?signal?%d\n",?name.c_str(),?WTERMSIG(status));?????????//?输出服务结束原因??
  28. ????}?else?if?(WIFSTOPPED(status))?{??
  29. ????????NOTICE("%s?stopped?by?signal?%d\n",?name.c_str(),?WSTOPSIG(status));??
  30. ????}?else?{??
  31. ????????NOTICE("%s?state?changed",?name.c_str());??
  32. ????}??
  33. ??
  34. ????if?(!svc)?{??
  35. ????????return?true;??
  36. ????}??
  37. ??
  38. ????if?(svc->Reap())?{?????????????????//?结束服务,相对于6.0作了进一步的封装,重启一些子进程,不做具体分析??
  39. ????????waiting_for_exec?=?false;??
  40. ????????RemoveService(*svc);???????????//?移除服务对应的信息??
  41. ????}??
  42. ??
  43. ????return?true;??
  44. }??

  继续分析main()函数:

Java代码
  1. int?main(int?argc,?char**?argv)?{??
  2. ????/*?01.?创建文件系统目录并挂载相关的文件系统?*/??
  3. ????/*?02.?屏蔽标准的输入输出/初始化内核log系统?*/??
  4. ????/*?03.?初始化属性域?*/??
  5. ????/*?04.?完成SELinux相关工作?*/·??
  6. ????/*?05.?重新设置属性?*/??
  7. ????/*?06.?创建epoll句柄?*/??
  8. ????/*?07.?装载子进程信号处理器?*/??
  9. ????/*?08.?启动匹配属性的服务端*/??
  10. ????property_load_boot_defaults();??????//?进程调用property_load_boot_defaults进行默认属性配置相关的工作??
  11. ????export_oem_lock_status();??
  12. ??
  13. ????std::string?bootmode?=?property_get("ro.bootmode");??????//?获取启动模式??
  14. ????if?(strncmp(bootmode.c_str(),?"ffbm",?4)?==?0){??
  15. ????property_set("ro.logdumpd","0");??
  16. ????}else{??
  17. ????property_set("ro.logdumpd","1");??
  18. ????}??
  19. ????start_property_service();??????//?启动属性服务??

  看下property_load_boot_defaults()函数:位于system/core/init/Property_service.cpp中

Java代码
  1. //?property_load_boot_defaults实际上就是调用load_properties_from_file解析配置文件???????
  2. /*?09.?设置默认系统属性?*/??
  3. //?然后根据解析的结果,设置系统属性??
  4. void?property_load_boot_defaults()?{??
  5. ????load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT,?NULL);??
  6. }??

  接着继续分析main:

Java代码
  1. int?main(int?argc,?char**?argv)?{??
  2. ????/*?01.?创建文件系统目录并挂载相关的文件系统?*/??
  3. ????/*?02.?屏蔽标准的输入输出/初始化内核log系统?*/??
  4. ????/*?03.?初始化属性域?*/??
  5. ????/*?04.?完成SELinux相关工作?*/·??
  6. ????/*?05.?重新设置属性?*/??
  7. ????/*?06.?创建epoll句柄?*/??
  8. ????/*?07.?装载子进程信号处理器?*/??
  9. ????/*?08.?设置默认系统属性?*/??
  10. ????/*?09.?启动配置属性的服务端?*/??
  11. ????/*?10.?匹配命令和函数之间的对应关系?*/??
  12. ????const?BuiltinFunctionMap?function_map;??????????//?system/core/init/builtins.cpp??
  13. ????Action::set_function_map(&function_map);????????//?在Action中保存function_map对象,记录了命令与函数之间的对应关系??

  【结尾】

  由于init涉及的知识点是相当多,代码之间的逻辑也是极其复杂,我在看别人的博客过程中,最反感一篇博客要看很久,往往因为琐事而放弃坚持(确切的说,随手把网页关掉了),所以我就分章节分析,尽量少源码多讲解。

  接下来,在Android启动篇 — init原理(二)中将详细分析init.rc的解析过程。

本文发布:Android开发网
本文地址:http://www.jizhuomi.com/android/course/707.html
2017年8月28日
发布:鸡皇冠现金官网hg6388|首页 分类:Android开发教程 浏览: 评论:0