Framework篇 - init.rc 与 ServiceManager 的启动和获取

2023-10-20 16:59

本文主要是介绍Framework篇 - init.rc 与 ServiceManager 的启动和获取,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文源代码基于 Android 7.0。

 

目录:

  1. init.rc 和 init.c
  2. ServiceManager 的启动流程
  3. 获取 ServiceManager

 

 

1. init.rc 和 init.c

 

  • 1.1 init.rc

Linux 的重要特征之一就是一切都是以文件的形式存在的,例如,一个设备通常与一个或多个设备文件对应。这些与内核空间交互的文件都在用户空间,所以在 Linux 内核装载完,需要首先建立这些文件所在的目录。而完成这些工作的程序就是 init。Init 是一个命令行程序,其主要工作之一就是建立这些与内核空间交互的文件所在的目录。当 Linux 内核加载完后,要做的第一件事就是调用 init 程序。也就是说,init 是用户空间执行的第一个程序。

尽管 init 完成的工作不算很多,不过代码还是非常复杂的。Init 程序并不是由一个源代码文件组成的,而是由一组源代码文件的目标文件链接而成的。需要明白的是,init.rc 只是语法文件,并不是程序,真正的入口则是上面提到的 system/core/init/init.c。

init.rc 有两个,分别位于: 

  • ./system/core/rootdir/init.rc 
  • ./bootable/recovery/etc/init.rc 

这两个 init.rc 使用场景不一样,一个是刷机用到的,也就是进入 recorvery 模式,一个是正常启动用到的。

下面是 init.rc:

"【import <filename>一个init配置文件,扩展当前配置。】"
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.${ro.zygote}.rc
import /init.trace.rc"【触发条件early-init,在early-init阶段调用以下行】"
on early-init# Set init and its forked children's oom_adj.write /proc/1/oom_score_adj -1000"【打开路径为<path>的一个文件,并写入一个或多个字符串】"# Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls.write /sys/fs/selinux/checkreqprot 0# Set the security context for the init process.# This should occur before anything else (e.g. ueventd) is started."【这段脚本的意思是init进程启动之后就马上调用函数setcon将自己的安全上下文设置为“u:r:init:s0”,即将init进程的domain指定为init。】"setcon u:r:init:s0# Set the security context of /adb_keys if present."【恢复指定文件到file_contexts配置中指定的安全上线文环境】"restorecon /adb_keys"【执行start ueventd的命令。ueventd是一个service后面有定义】 "start ueventd"【mkdir <path> [mode] [owner] [group]   //创建一个目录<path>,可以选择性地指定mode、owner以及group。如果没有指定,默认的权限为755,并属于root用户和root组。】"# create mountpointsmkdir /mnt 0775 root systemon init"【设置系统时钟的基准,比如0代表GMT,即以格林尼治时间为准】"sysclktz 0"【设置kernel日志等级】"
loglevel 6 ####write /proc/bootprof "INIT: on init start" ####"【symlink <target> <path>    //创建一个指向<path>的软连接<target>。】"# Backward compatibilitysymlink /system/etc /etcsymlink /sys/kernel/debug /d# Right now vendor lives on the same filesystem as system,# but someday that may change.symlink /system/vendor /vendor"【创建一个目录<path>,可以选择性地指定mode、owner以及group。】"# Create cgroup mount point for cpu accountingmkdir /acctmount cgroup none /acct cpuacctmkdir /acct/uid"【mount <type> <device> <dir> [ <mountoption> ]   //在目录<dir>挂载指定的设备。<device> 可以是以 mtd@name 的形式指定一个mtd块设备。<mountoption>包括 ro、rw、remount、noatime、 ...】"# Create cgroup mount point for memorymount tmpfs none /sys/fs/cgroup mode=0750,uid=0,gid=1000mkdir /sys/fs/cgroup/memory 0750 root systemmount cgroup none /sys/fs/cgroup/memory memorywrite /sys/fs/cgroup/memory/memory.move_charge_at_immigrate 1"【chown <owner> <group> <path>   //改变文件的所有者和组。】""【后面的一些行因为类似,就省略了】".....# Healthd can trigger a full boot from charger mode by signaling this
# property when the power button is held.
on property:sys.boot_from_charger_mode=1"【停止指定类别服务类下的所有已运行的服务】"class_stop charger"【触发一个事件,将该action排在某个action之后(用于Action排队)】"trigger late-init# Load properties from /system/ + /factory after fs mount.
on load_all_props_action"【从/system,/vendor加载属性。默认包含在init.rc】"load_all_props# Indicate to fw loaders that the relevant mounts are up.
on firmware_mounts_complete"【删除指定路径下的文件】"rm /dev/.booting# Mount filesystems and start core system services.
on late-init"【触发一个事件。用于将一个action与另一个 action排列。】"trigger early-fstrigger fstrigger post-fstrigger post-fs-data# Load properties from /system/ + /factory after fs mount. Place# this in another action so that the load will be scheduled after the prior# issued fs triggers have completed.trigger load_all_props_action# Remove a file to wake up anything waiting for firmware.trigger firmware_mounts_completetrigger early-boottrigger booton post-fs..."【一些创造目录,建立链接,更改权限的操作,这里省略】"on post-fs-data..."【一些创造目录,建立链接,更改权限的操作,这里省略】""【恢复指定文件到file_contexts配置中指定的安全上线文环境】"restorecon /data/mediaserver"【将系统属性<name>的值设置为<value>,即以键值对的方式设置系统属性】"# Reload policy from /data/security if present.setprop selinux.reload_policy 1"【以递归的方式恢复指定目录到file_contexts配置中指定的安全上下文中】"# Set SELinux security contexts on upgrade or policy update.restorecon_recursive /data# If there is no fs-post-data action in the init.<device>.rc file, you# must uncomment this line, otherwise encrypted filesystems# won't work.# Set indication (checked by vold) that we have finished this action#setprop vold.post_fs_data_done 1on boot"【初始化网络】"# basic network initifup lo"【设置主机名为localhost】"hostname localhost"【设置域名localdomain】"domainname localdomain"【设置资源限制】"# set RLIMIT_NICE to allow priorities from 19 to -20setrlimit 13 40 40"【这里省略了一些chmod,chown,等操作,不多解释】"...# Define default initial receive window size in segments.setprop net.tcp.default_init_rwnd 60"【重启core服务】"class_start coreon nonencryptedclass_start mainclass_start late_starton property:vold.decrypt=trigger_default_encryptionstart defaultcryptoon property:vold.decrypt=trigger_encryptionstart surfaceflingerstart encrypton property:sys.init_log_level=*loglevel ${sys.init_log_level}on chargerclass_start chargeron property:vold.decrypt=trigger_reset_mainclass_reset mainon property:vold.decrypt=trigger_load_persist_propsload_persist_propson property:vold.decrypt=trigger_post_fs_datatrigger post-fs-dataon property:vold.decrypt=trigger_restart_min_frameworkclass_start mainon property:vold.decrypt=trigger_restart_frameworkclass_start mainclass_start late_starton property:vold.decrypt=trigger_shutdown_frameworkclass_reset late_startclass_reset mainon property:sys.powerctl=*powerctl ${sys.powerctl}# system server cannot write to /proc/sys files,
# and chown/chmod does not work for /proc/sys/ entries.
# So proxy writes through init.
on property:sys.sysctl.extra_free_kbytes=*write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}# "tcp_default_init_rwnd" Is too long!
on property:sys.sysctl.tcp_def_init_rwnd=*write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd}"【守护进程】"
## Daemon processes to be run by init.
##
service ueventd /sbin/ueventdclass corecriticalseclabel u:r:ueventd:s0"【日志服务进程】"
service logd /system/bin/logdclass coresocket logd stream 0666 logd logdsocket logdr seqpacket 0666 logd logdsocket logdw dgram 0222 logd logdseclabel u:r:logd:s0"【Healthd是android4.4之后提出来的一种中介模型,该模型向下监听来自底层的电池事件,向上传递电池数据信息给Framework层的BatteryService用以计算电池电量相关状态信息】"
service healthd /sbin/healthdclass corecriticalseclabel u:r:healthd:s0"【控制台进程】"
service console /system/bin/sh"【为当前service设定一个类别.相同类别的服务将会同时启动或者停止,默认类名是default】"class core"【服务需要一个控制台】"console"【服务不会自动启动,必须通过服务名显式启动】"disabled"【在执行此服务之前切换用户名,当前默认的是root.自Android M开始,即使它要求linux capabilities,也应该使用该选项.很明显,为了获得该功能,进程需要以root用户运行】"user shellseclabel u:r:shell:s0on property:ro.debuggable=1start console# adbd is controlled via property triggers in init.<platform>.usb.rc
service adbd /sbin/adbd --root_seclabel=u:r:su:s0class core"【创建一个unix域下的socket,其被命名/dev/socket/<name>. 并将其文件描述符fd返回给服务进程.其中,type必须为dgram,stream或者seqpacke,user和group默认是0.seclabel是该socket的SELLinux的安全上下文环境,默认是当前service的上下文环境,通过seclabel指定】"socket adbd stream 660 system systemdisabledseclabel u:r:adbd:s0# adbd on at boot in emulator
on property:ro.kernel.qemu=1start adbd"【内存管理服务,内存不够释放内存】"
service lmkd /system/bin/lmkdclass corecriticalsocket lmkd seqpacket 0660 system system"【ServiceManager是一个守护进程,它维护着系统服务和客户端的binder通信。
在Android系统中用到最多的通信机制就是Binder,Binder主要由Client、Server、ServiceManager和Binder驱动程序组成。其中Client、Service和ServiceManager运行在用户空间,而Binder驱动程序运行在内核空间。核心组件就是Binder驱动程序了,而ServiceManager提供辅助管理的功能,无论是Client还是Service进行通信前首先要和ServiceManager取得联系。而ServiceManager是一个守护进程,负责管理Server并向Client提供查询Server的功能。】"
service servicemanager /system/bin/servicemanagerclass coreuser systemgroup systemcriticalonrestart restart healthd"【servicemanager 服务启动时会重启zygote服务】"onrestart restart zygoteonrestart restart mediaonrestart restart surfaceflingeronrestart restart drm"【Vold是Volume Daemon的缩写,它是Android平台中外部存储系统的管控中心,是管理和控制Android平台外部存储设备的后台进程】"
service vold /system/bin/voldclass coresocket vold stream 0660 root mountioprio be 2"【Netd是Android系统中专门负责网络管理和控制的后台daemon程序】"
service netd /system/bin/netdclass mainsocket netd stream 0660 root systemsocket dnsproxyd stream 0660 root inetsocket mdns stream 0660 root systemsocket fwmarkd stream 0660 root inet"【debuggerd是一个daemon进程,在系统启动时随着init进程启动。主要负责将进程运行时的信息dump到文件或者控制台中】"
service debuggerd /system/bin/debuggerdclass mainservice debuggerd64 /system/bin/debuggerd64class main"【Android RIL (Radio Interface Layer)提供了Telephony服务和Radio硬件之间的抽象层】"
# for using TK init.modem.rc rild-daemon setting
#service ril-daemon /system/bin/rild
#    class main
#    socket rild stream 660 root radio
#    socket rild-debug stream 660 radio system
#    user root
#    group radio cache inet misc audio log"【提供系统 范围内的surface composer功能,它能够将各种应用 程序的2D、3D surface进行组合。】"
service surfaceflinger /system/bin/surfaceflingerclass coreuser systemgroup graphics drmrpconrestart restart zygote"【DRM可以直接访问DRM clients的硬件。DRM驱动用来处理DMA,内存管理,资源锁以及安全硬件访问。为了同时支持多个3D应用,3D图形卡硬件必须作为一个共享资源,因此需要锁来提供互斥访问。DMA传输和AGP接口用来发送图形操作的buffers到显卡硬件,因此要防止客户端越权访问显卡硬件。】"
#make sure drm server has rights to read and write sdcard ####
service drm /system/bin/drmserverclass mainuser drm# group drm system inet drmrpc ####group drm system inet drmrpc sdcard_r ####"【媒体服务,无需多说】"
service media /system/bin/mediaserverclass mainuser root ####
#   google default ####
#   user media    ####group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm media sdcard_r system net_bt_stack ####
#   google default ####
#   group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm ####ioprio rt 4"【设备加密相关服务】"
# One shot invocation to deal with encrypted volume.
service defaultcrypto /system/bin/vdc --wait cryptfs mountdefaultencrypteddisabled"【当服务退出时,不重启该服务】"oneshot# vold will set vold.decrypt to trigger_restart_framework (default# encryption) or trigger_restart_min_framework (other encryption)# One shot invocation to encrypt unencrypted volumes
service encrypt /system/bin/vdc --wait cryptfs enablecrypto inplace defaultdisabledoneshot# vold will set vold.decrypt to trigger_restart_framework (default# encryption)"【开机动画服务】"
service bootanim /system/bin/bootanimationclass coreuser graphics
#    group graphics audio ####group graphics media audio ####disabledoneshot"【在Android系统中,PackageManagerService用于管理系统中的所有安装包信息及应用程序的安装卸载,但是应用程序的安装与卸载并非PackageManagerService来完成,而是通过PackageManagerService来访问installd服务来执行程序包的安装与卸载的。】"
service installd /system/bin/installdclass mainsocket installd stream 600 system systemservice flash_recovery /system/bin/install-recovery.shclass mainseclabel u:r:install_recovery:s0oneshot"【vpn相关的服务】"
service racoon /system/bin/racoonclass mainsocket racoon stream 600 system system# IKE uses UDP port 500. Racoon will setuid to vpn after binding the port.group vpn net_admin inetdisabledoneshot"【android中有mtpd命令可以连接vpn】"
service mtpd /system/bin/mtpdclass mainsocket mtpd stream 600 system systemuser vpngroup vpn net_admin inet net_rawdisabledoneshotservice keystore /system/bin/keystore /data/misc/keystoreclass mainuser keystoregroup keystore drmrpc"【可以用dumpstate 获取设备的各种信息】"
service dumpstate /system/bin/dumpstate -sclass mainsocket dumpstate stream 0660 shell logdisabledoneshot"【mdnsd 是多播 DNS 和 DNS 服务发现的守护程序。】"
service mdnsd /system/bin/mdnsdclass mainuser mdnsrgroup inet net_rawsocket mdnsd stream 0660 mdnsr inetdisabledoneshot"【触发关机流程继续往下走】"
service pre-recovery /system/bin/uncryptclass maindisabled"【当服务退出时,不重启该服务】"oneshot

 

  • 1.2 init.c

system/core/init/init.c

主要分析 main 方法:

int main( int argc, char **argv )
{#创 建一些linux根文件系统中的目录mkdir( "/dev", 0755 );mkdir( "/proc", 0755 );mkdir( "/sys", 0755 );mount( "tmpfs", "/dev", "tmpfs", 0, "mode=0755" );mkdir( "/dev/pts", 0755 );mkdir( "/dev/socket", 0755 );mount( "devpts", "/dev/pts", "devpts", 0, NULL );mount( "proc", "/proc", "proc", 0, NULL );mount( "sysfs", "/sys", "sysfs", 0, NULL );#init的 标准输入,标准输出,标准错误文件描述符定向到__null__,意味着没有输入和输出,它的输入和输出全部写入到Log中open_devnull_stdio();#初始化 log 写入init进 信息log_init();#读取并 且解析init.rc文件(这个文件在根目录下)parse_config_file( "/init.rc" );#取得硬件 为打印我们的设备名fs100get_hardware_name();snprintf( tmp, sizeof(tmp), "/init.%s.rc", hardware );#读取并 且解析硬件相关的init脚本文件,parse_config_file( tmp );#触发在init脚本文件中名字为early-init的action,并且执行其commands,其实是: on early-initaction_for_each_trigger( "early-init", action_add_queue_tail );drain_action_queue();#初始化动态设备管理,设备文件有变化时反应给内核,后面具体解释device_fd = device_init(); # 初 始 化 设 备 管 理 务#加载启动动画,如果动画打开失败,则在屏幕上打印: A N D R O I D字样。if ( load_565rle_image( INIT_IMAGE_FILE ) ){fd = open( "/dev/tty0", O_WRONLY );if ( fd >= 0 ){const char *msg;msg = "\n""\n""\n"879         "\n""\n""\n""\n" /* console is 40 cols x 30 lines */"\n""\n""\n""\n""\n""\n""\n"/* "             A N D R O I D ";开机动画 */write( fd, msg, strlen( msg ) );close( fd );}}#触发 在init脚本文件中名字为init的action,并且执行其commands,其实是:on initaction_for_each_trigger( "init", action_add_queue_tail );drain_action_queue();#启动系统属性服务: system property serviceproperty_set_fd = start_property_service();#创建socket用来处理孤儿进程信号if ( socketpair( AF_UNIX, SOCK_STREAM, 0, s ) == 0 ){signal_fd   = s[0];signal_recv_fd  = s[1];fcntl( s[0], F_SETFD, FD_CLOEXEC );fcntl( s[0], F_SETFL, O_NONBLOCK );fcntl( s[1], F_SETFD, FD_CLOEXEC );fcntl( s[1], F_SETFL, O_NONBLOCK );}#触发 在init脚本文件中名字为early-boot和boot的action,并且执行其commands,其实是:on early-boot和on bootaction_for_each_trigger( "early-boot", action_add_queue_tail );action_for_each_trigger( "boot", action_add_queue_tail );drain_action_queue();#启动所有属性变化触发命令,其实是: on property:ro.xx.xx=xxqueue_all_property_triggers();drain_action_queue();#进入 死循环()for (;; ){#启 动所有init脚本中声明的service,#如 :266 service servicemanager /system/bin/servicemanager#user system#critical#onrestart restart zygote#onrestart restart mediarestart_processes();#多路监听设备管理,子进程运行状态,属性服务nr = poll( ufds, fd_count, timeout );if ( nr <= 0 )continue;if ( ufds[2].revents == POLLIN ){read( signal_recv_fd, tmp, sizeof(tmp) );while ( !wait_for_one_process( 0 ) );continue;}if ( ufds[0].revents == POLLIN )handle_device_fd( device_fd );if ( ufds[1].revents == POLLIN )handle_property_set_fd( property_set_fd );if ( ufds[3].revents == POLLIN )handle_keychord( keychord_fd );}return(0);
}

 

 

2. ServiceManager 的启动流程

ServiceManager 是 Binder IPC 通信过程中的守护进程,本身也是一个 Binder 服务,但并没有采用 libbinder 中的多线程模型来与 Binder 驱动通信, 而是自行编写了 binder.c 直接和 Binder 驱动来通信,并且只有一个循环 binder_loop 来进行读取和处理事务,这样的好处是简单而高效。

ServiceManager 本身工作相对简单,其功能:查询和注册服务。对于 Binder IPC 通信过程中,其实更多的情形是 BpBinder 和BBinder 之间的通信, 比如 ActivityManagerProxy 和 ActivityManagerService 之间的通信等。

ServiceManager 是由 init 进程通过解析 init.rc 文件而创建的,其所对应的可执行程序 /system/bin/servicemanager, 所对应的源文件是 service_manager.c,进程名为 /system/bin/servicemanager。

 

  • 2.1 servicemanager.rc

native/cmds/servicemanager/servicemanager.rc

Android 7.0 中独立出了 servicemanager.rc: 

service servicemanager /system/bin/servicemanagerclass coreuser systemgroup system readproccriticalonrestart restart healthdonrestart restart zygoteonrestart restart audioserveronrestart restart mediaonrestart restart surfaceflingeronrestart restart inputflingeronrestart restart drmonrestart restart cameraserverwritepid /dev/cpuset/system-background/tasks
  • 2.2 service_manager.c 与 binder.c

native/cmds/servicemanager/service_manager.c

native/cmds/servicemanager/binder.c

看看 service_manager.c 的 main():

int main()
{// binder 状态struct binder_state *bs;// 第一步,打开binder驱动,申请128k字节大小的内存空间,会调用binder.c里面的binder_open函数bs = binder_open(128*1024);if (!bs) {ALOGE("failed to open binder driver\n");return -1;}// 成为上下文管理者if (binder_become_context_manager(bs)) {ALOGE("cannot become context manager (%s)\n", strerror(errno));return -1;}// selinux权限是否使用selinux_enabled = is_selinux_enabled();sehandle = selinux_android_service_context_handle();selinux_status_open(true);if (selinux_enabled > 0) {if (sehandle == NULL) {ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");// 无法获取sehandleabort();}if (getcon(&service_manager_context) != 0) {ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");// 无法获取service_manager上下文abort();}}union selinux_callback cb;cb.func_audit = audit_callback;selinux_set_callback(SELINUX_CB_AUDIT, cb);cb.func_log = selinux_log_callback;selinux_set_callback(SELINUX_CB_LOG, cb);// 进入无限循环,处理client端发来的请求// 进入循环读写操作,由main()方法传递过来的参数func指向svcmgr_handler。binder_loop(bs, svcmgr_handler);return 0;
}

ServiceManager 的启动过程总结为:

  • 打开 binder 驱动:binderopen;
  • 注册成为 binder 服务的大管家:binderbecomecontextmanager;
  • 进入无限循环,处理 client 端发来的请求:binder_loop。

binderopen (位于 binder.c)

// binder状态结构
struct binder_state
{int fd; // dev/binder的文件描述符void *mapped; // 指向mmap的内存地址size_t mapsize; // 分配的内存大小,默认为128KB
};// service_manager.c会调用binder.c里面的binder_open函数
// 打开binder驱动
struct binder_state *binder_open(size_t mapsize)
{// binder状态struct binder_state *bs;// binder版本struct binder_version vers;bs = malloc(sizeof(*bs));if (!bs) {errno = ENOMEM;return NULL;}// 通过系统调用陷入内核,打开Binder设备驱动// 打开/dev/binder,文件描述符bs->fd = open("/dev/binder", O_RDWR | O_CLOEXEC);if (bs->fd < 0) {fprintf(stderr,"binder: cannot open device (%s)\n",strerror(errno));goto fail_open;}// 通过系统调用,ioctl获取binder版本信息if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||(vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {// 内核空间与用户空间的binder不是同一版本fprintf(stderr,"binder: kernel driver version (%d) differs from user space version (%d)\n",vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);// goto 到fail_open代码块goto fail_open;}bs->mapsize = mapsize;// 通过系统调用,mmap内存映射,mmap必须是page的整数倍bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);if (bs->mapped == MAP_FAILED) {// binder设备内存无法映射fprintf(stderr,"binder: cannot map device (%s)\n",strerror(errno));goto fail_map;}return bs;fail_map:close(bs->fd);
fail_open:free(bs);return NULL;
}// 关闭binder
void binder_close(struct binder_state *bs)
{munmap(bs->mapped, bs->mapsize);close(bs->fd);free(bs);
}

binder_become_context_manager (位于 binder.c)

// 成为上下文的管理者,整个系统中只有一个这样的管理者。 通过ioctl()方法经过系统调用,对应于Binder驱动层的binder_ioctl()方法.
int binder_become_context_manager(struct binder_state *bs)
{// 通过ioctl,传递BINDER_SET_CONTEXT_MGR指令return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

 binder_loop (位于 binder.c):

// 开启binder循环,监听客户端请求
void binder_loop(struct binder_state *bs, binder_handler func)
{int res;struct binder_write_read bwr;uint32_t readbuf[32];bwr.write_size = 0;bwr.write_consumed = 0;bwr.write_buffer = 0;readbuf[0] = BC_ENTER_LOOPER;// 将BC_ENTER_LOOPER命令发送给binder驱动,让Service Manager进入循环// binder_write通过ioctl()将BC_ENTER_LOOPER命令发送给binder驱动,此时bwr只有write_buffer有数据,// 进入binder_thread_write()方法。 接下来进入for循环,执行ioctl(),此时bwr只有read_buffer有数据,// 那么进入binder_thread_read()方法。binder_write(bs, readbuf, sizeof(uint32_t));// 一个无限循环for (;;) {bwr.read_size = sizeof(readbuf);bwr.read_consumed = 0;bwr.read_buffer = (uintptr_t) readbuf;// 进入循环,不断地binder读写过程res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);if (res < 0) {ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));break;}// 解析binder信息,会交由binder_handler(func)处理, parse里面有transaction各种函数res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);if (res == 0) {ALOGE("binder_loop: unexpected reply?!\n");break;}if (res < 0) {ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));break;}}
}

binder_loop 中的 binder_parse (位于 binder.c): 

// for循环里的binder_parse
// 解析binder信息,此处参数ptr指向BC_ENTER_LOOPER,func指向svcmgr_handler。故有请求到来,则调用svcmgr_handler。
int binder_parse(struct binder_state *bs, struct binder_io *bio,uintptr_t ptr, size_t size, binder_handler func)
{int r = 1;uintptr_t end = ptr + (uintptr_t) size;while (ptr < end) {uint32_t cmd = *(uint32_t *) ptr;ptr += sizeof(uint32_t);
#if TRACEfprintf(stderr,"%s:\n", cmd_name(cmd));
#endifswitch(cmd) {// 无操作,退出循环case BR_NOOP:break;// transaction完成case BR_TRANSACTION_COMPLETE:break;case BR_INCREFS:case BR_ACQUIRE:case BR_RELEASE:case BR_DECREFS:
#if TRACEfprintf(stderr,"  %p, %p\n", (void *)ptr, (void *)(ptr + sizeof(void *)));
#endifptr += sizeof(struct binder_ptr_cookie);break;// binder transaction消息case BR_TRANSACTION: {struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;if ((end - ptr) < sizeof(*txn)) {ALOGE("parse: txn too small!\n");return -1;}binder_dump_txn(txn);if (func) {unsigned rdata[256/4];struct binder_io msg;struct binder_io reply;int res;bio_init(&reply, rdata, sizeof(rdata), 4);// 从txn解析出binder_io信息bio_init_from_txn(&msg, txn);res = func(bs, txn, &msg, &reply);if (txn->flags & TF_ONE_WAY) {binder_free_buffer(bs, txn->data.ptr.buffer);} else {// binder发送回复binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);}}ptr += sizeof(*txn);break;}case BR_REPLY: {struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;if ((end - ptr) < sizeof(*txn)) {ALOGE("parse: reply too small!\n");return -1;}binder_dump_txn(txn);if (bio) {bio_init_from_txn(bio, txn);bio = 0;} else {/* todo FREE BUFFER */}ptr += sizeof(*txn);r = 0;break;}// binder死亡消息case BR_DEAD_BINDER: {struct binder_death *death = (struct binder_death *)(uintptr_t) *(binder_uintptr_t *)ptr;ptr += sizeof(binder_uintptr_t);death->func(bs, death->ptr);break;}case BR_FAILED_REPLY:r = -1;break;case BR_DEAD_REPLY:r = -1;break;default:ALOGE("parse: OOPS %d\n", cmd);return -1;}}return r;
}

解析 binder 信息,此处参数 ptr 指向 BC_ENTER_LOOPER,func 指向 svcmgr_handler。故有请求到来,则调用svcmgr_handler。svcmgr_handler 位于 service_manager.c:

// 该方法的功能:查询服务,注册服务,以及列举所有服务
int svcmgr_handler(struct binder_state *bs,struct binder_transaction_data *txn,struct binder_io *msg,struct binder_io *reply)
{struct svcinfo *si;uint16_t *s;size_t len;uint32_t handle;uint32_t strict_policy;int allow_isolated;//ALOGI("target=%p code=%d pid=%d uid=%d\n",//      (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid);if (txn->target.ptr != BINDER_SERVICE_MANAGER)return -1;if (txn->code == PING_TRANSACTION)return 0;// Equivalent to Parcel::enforceInterface(), reading the RPC// header with the strict mode policy mask and the interface name.// Note that we ignore the strict_policy and don't propagate it// further (since we do no outbound RPCs anyway).strict_policy = bio_get_uint32(msg);s = bio_get_string16(msg, &len);if (s == NULL) {return -1;}if ((len != (sizeof(svcmgr_id) / 2)) ||memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {fprintf(stderr,"invalid id %s\n", str8(s, len));return -1;}if (sehandle && selinux_status_updated() > 0) {struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle();if (tmp_sehandle) {selabel_close(sehandle);sehandle = tmp_sehandle;}}switch(txn->code) {// 获取和检查服务case SVC_MGR_GET_SERVICE:case SVC_MGR_CHECK_SERVICE:// 根据名称查找相应服务,s为服务名s = bio_get_string16(msg, &len);if (s == NULL) {return -1;}handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);if (!handle)break;// 当找到服务的handle, 则调用bio_put_ref(reply, handle),将handle封装到reply.bio_put_ref(reply, handle);return 0;// 注册服务case SVC_MGR_ADD_SERVICE:// 服务名s = bio_get_string16(msg, &len);if (s == NULL) {return -1;}handle = bio_get_ref(msg);allow_isolated = bio_get_uint32(msg) ? 1 : 0;// 注册指定服务if (do_add_service(bs, s, len, handle, txn->sender_euid,allow_isolated, txn->sender_pid))return -1;break;// 获取服务列表case SVC_MGR_LIST_SERVICES: {uint32_t n = bio_get_uint32(msg);if (!svc_can_list(txn->sender_pid, txn->sender_euid)) {ALOGE("list_service() uid=%d - PERMISSION DENIED\n",txn->sender_euid);return -1;}si = svclist;while ((n-- > 0) && si)si = si->next;if (si) {bio_put_string16(reply, si->name);return 0;}return -1;}default:ALOGE("unknown code %d\n", txn->code);return -1;}bio_put_uint32(reply, 0);return 0;
}

可以看到包含注册服务和获取服务,关于 ServiceManager 如何注册和获取服务,将在下一篇文章中详细讲解。

ServiceManager 启动流程:

  • 打开 binder 驱动,并调用 mmap() 方法分配 128k 的内存映射空间:binder_open();
  • 通知 binder 驱动使其成为守护进程:binder_become_context_manager();
  • 验证 selinux 权限,判断进程是否有权注册或查看指定服务;
  • 进入循环状态,等待 Client 端的请求:binder_loop()。


注册服务的过程,根据服务名称,但同一个服务已注册,重新注册前会先移除之前的注册信息。


死亡通知:当 binder 所在进程死亡后,会调用 binder_release 方法,然后调用 binder_node_release,这个过程便会发出死亡通知的回调。


ServiceManager 最核心的两个功能为查询和注册服务:

  • 注册服务:记录服务名和 handle 信息,保存到 svclist 列表;
  • 查询服务:根据服务名查询相应的的 handle 信息。

 

 

 

3. 获取 ServiceManager

获取 ServiceManager 是通过 defaultServiceManager() 方法来完成,当进程注册服务 (addService) 或获取服务 (getService) 之前,都需要先调用 defaultServiceManager() 方法来获取 gDefaultServiceManager 对象。对于 gDefaultServiceManager 对象,如果存在则直接返回;如果不存在则创建该对象,创建过程包括调用 open() 打开 binder 驱动设备,利用 mmap() 映射内核的地址空间。

native/libs/binder/IServiceManager.cpp

native/libs/binder/ProcessState.cpp

native/libs/binder/BpBinder.cpp

native/libs/binder/Binder.cpp

// 命名空间 android
namespace android {
// 获取default ServiceManager
sp<IServiceManager> defaultServiceManager()
{// 如果已经存在,则直接返回if (gDefaultServiceManager != NULL) return gDefaultServiceManager;{// 加锁AutoMutex _l(gDefaultServiceManagerLock);// 一个循环, 每次睡眠1秒,如果没有获取到,则继续等,因为获取时,可能ServiceManager还没有创建完while (gDefaultServiceManager == NULL) {// 用于获取ProcessState对象(也是单例模式),每个进程有且只有一个ProcessState对象,存在则直接返回,不存在则创建// 用于获取BpBinder对象,对于handle=0的BpBinder对象,存在则直接返回,不存在才创建// interface_cast用于获取BpServiceManager对象gDefaultServiceManager = interface_cast<IServiceManager>(ProcessState::self()->getContextObject(NULL));if (gDefaultServiceManager == NULL)sleep(1);}}return gDefaultServiceManager;
}
// ...
}

获取 ServiceManager 对象采用单例模式,当 gDefaultServiceManager 存在,则直接返回,否则创建一个新对象。 
发现与一般的单例模式不太一样,里面多了一层 while 循环,这是 Google 在2013年1月 Todd Poynor 提交的修改。
当尝试创建或获取 ServiceManager 时,ServiceManager 可能尚未准备就绪,这时通过sleep 1 秒后,循环尝试获取直到成功。

native/libs/binder/ProcessState.cpp 的 self():

sp<ProcessState> ProcessState::self()
{Mutex::Autolock _l(gProcessMutex);if (gProcess != NULL) {return gProcess;}// 实例化ProcessStategProcess = new ProcessState;return gProcess;
}

ProcessState::self():用于获取 ProcessState 对象 (也是单例模式),每个进程有且只有一个 ProcessState 对象,存在则直接返回,不存在则创建。

ProcessState 构造器:

// 初始化ProcessState
// ProcessState的单例模式的惟一性,因此一个进程只打开binder设备一次,其中ProcessState的成员变量mDriverFD记录binder驱动的fd,用于访问binder设备。
ProcessState::ProcessState(): mDriverFD(open_driver()) // 打开Binder驱动, mVMStart(MAP_FAILED), mThreadCountLock(PTHREAD_MUTEX_INITIALIZER), mThreadCountDecrement(PTHREAD_COND_INITIALIZER), mExecutingThreadsCount(0), mMaxThreads(DEFAULT_MAX_BINDER_THREADS) // DEFAULT_MAX_BINDER_THREADS = 15,binder默认的最大可并发访问的线程数为16。, mStarvationStartTimeMs(0), mManagesContexts(false), mBinderContextCheckFunc(NULL), mBinderContextUserData(NULL), mThreadPoolStarted(false), mThreadPoolSeq(1)
{if (mDriverFD >= 0) {// mmap the binder, providing a chunk of virtual address space to receive transactions.// 采用内存映射函数mmap,给binder分配一块虚拟地址空间,用来接收事务// BINDER_VM_SIZE = (1*1024*1024) - (4096 *2), binder分配的默认内存大小为1M-8k。mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);if (mVMStart == MAP_FAILED) {// *sigh*ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");close(mDriverFD);mDriverFD = -1;}}LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.  Terminating.");
}


native/libs/binder/ProcessState.cpp 的 getContextObject():

// 获取handle=0的IBinder
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{return getStrongProxyForHandle(0);
}

defaultServiceManager  等价于 new BpServiceManager(new BpBinder(0));

ProcessState::self() 主要工作:

  • 调用 open(),打开 /dev/binder 驱动设备;
  • 再利用 mmap(),创建大小为 1M-8K 的内存地址空间;
  • 设定当前进程最大的最大并发 Binder 线程个数为16;


BpServiceManager 巧妙将通信层与业务层逻辑合为一体,通过继承接口 IServiceManager 实现了接口中的业务逻辑函数;
通过成员变量 mRemote = new BpBinder(0) 进行 Binder 通信工作;
BpBinder 通过 handler 来指向所对应 BBinder,在整个 Binder 系统中 handle=0 代表 ServiceManager 所对应的 BBinder。

 

这篇关于Framework篇 - init.rc 与 ServiceManager 的启动和获取的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/248433

相关文章

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

springboot3打包成war包,用tomcat8启动

1、在pom中,将打包类型改为war <packaging>war</packaging> 2、pom中排除SpringBoot内置的Tomcat容器并添加Tomcat依赖,用于编译和测试,         *依赖时一定设置 scope 为 provided (相当于 tomcat 依赖只在本地运行和测试的时候有效,         打包的时候会排除这个依赖)<scope>provided

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

用命令行的方式启动.netcore webapi

用命令行的方式启动.netcore web项目 进入指定的项目文件夹,比如我发布后的代码放在下面文件夹中 在此地址栏中输入“cmd”,打开命令提示符,进入到发布代码目录 命令行启动.netcore项目的命令为:  dotnet 项目启动文件.dll --urls="http://*:对外端口" --ip="本机ip" --port=项目内部端口 例: dotnet Imagine.M

Linux服务器Java启动脚本

Linux服务器Java启动脚本 1、初版2、优化版本3、常用脚本仓库 本文章介绍了如何在Linux服务器上执行Java并启动jar包, 通常我们会使用nohup直接启动,但是还是需要手动停止然后再次启动, 那如何更优雅的在服务器上启动jar包呢,让我们一起探讨一下吧。 1、初版 第一个版本是常用的做法,直接使用nohup后台启动jar包, 并将日志输出到当前文件夹n

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

SpringBoot项目是如何启动

启动步骤 概念 运行main方法,初始化SpringApplication 从spring.factories读取listener ApplicationContentInitializer运行run方法读取环境变量,配置信息创建SpringApplication上下文预初始化上下文,将启动类作为配置类进行读取调用 refresh 加载 IOC容器,加载所有的自动配置类,创建容器在这个过程

嵌入式Openharmony系统构建与启动详解

大家好,今天主要给大家分享一下,如何构建Openharmony子系统以及系统的启动过程分解。 第一:OpenHarmony系统构建      首先熟悉一下,构建系统是一种自动化处理工具的集合,通过将源代码文件进行一系列处理,最终生成和用户可以使用的目标文件。这里的目标文件包括静态链接库文件、动态链接库文件、可执行文件、脚本文件、配置文件等。      我们在编写hellowor

三相直流无刷电机(BLDC)控制算法实现:BLDC有感启动算法思路分析

一枚从事路径规划算法、运动控制算法、BLDC/FOC电机控制算法、工控、物联网工程师,爱吃土豆。如有需要技术交流或者需要方案帮助、需求:以下为联系方式—V 方案1:通过霍尔传感器IO中断触发换相 1.1 整体执行思路 霍尔传感器U、V、W三相通过IO+EXIT中断的方式进行霍尔传感器数据的读取。将IO口配置为上升沿+下降沿中断触发的方式。当霍尔传感器信号发生发生信号的变化就会触发中断在中断

Spring Framework系统框架

序号表示的是学习顺序 IoC(控制反转)/DI(依赖注入): ioc:思想上是控制反转,spring提供了一个容器,称为IOC容器,用它来充当IOC思想中的外部。 我的理解就是spring把这些对象集中管理,放在容器中,这个容器就叫Ioc这些对象统称为Bean 用对象的时候不用new,直接外部提供(bean) 当外部的对象有关系的时候,IOC给它俩绑好(DI) DI和IO