本文主要是介绍Framework篇 - init.rc 与 ServiceManager 的启动和获取,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
本文源代码基于 Android 7.0。
目录:
- init.rc 和 init.c
- ServiceManager 的启动流程
- 获取 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 的启动和获取的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!