網站首頁 編程語言 正文
正文
上一篇文章:
Android 10 啟動分析之Init篇 (一)
在前文提到,init進程會在在Trigger 為init的Action中,啟動servicemanager服務,這篇文章我們就來具體分析一下servicemanager服務,它到底做了哪些事情。
servicemanager服務的源碼位于/frameworks/native/cmds/servicemanager/service_manager.c
,我們將從這個類的入口開始看起。
int main(int argc, char** argv) { struct binder_state *bs; union selinux_callback cb; char *driver; if (argc > 1) { driver = argv[1]; } else { //啟動時默認無參數,走這個分支 driver = "/dev/binder"; } //打開binder驅動,并設置mmap的內存大小為128k bs = binder_open(driver, 128*1024); ... if (binder_become_context_manager(bs)) { ALOGE("cannot become context manager (%s)\n", strerror(errno)); return -1; } cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); #ifdef VENDORSERVICEMANAGER cb.func_log = selinux_vendor_log_callback; #else cb.func_log = selinux_log_callback; #endif selinux_set_callback(SELINUX_CB_LOG, cb); #ifdef VENDORSERVICEMANAGER sehandle = selinux_android_vendor_service_context_handle(); #else sehandle = selinux_android_service_context_handle(); #endif selinux_status_open(true); if (sehandle == NULL) { ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n"); abort(); } if (getcon(&service_manager_context) != 0) { ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n"); abort(); } /* binder_loop已封裝如下步驟: while (1) { /* read data */ /* parse data, and process */ /* reply */ } */ binder_loop(bs, svcmgr_handler); return 0; }
從main函數中可以看出,它主要做了三件事情:
- 打開/dev/binder設備,并在內存中映射128K的空間。
- 通知Binder設備,把自己變成context_manager,其他用戶進程都通過0號句柄訪問ServiceManager。
- 進入循環,不停的去讀Binder設備,看是否有對service的請求,如果有的話,就去調用svcmgr_handler函數回調處理請求。
我們再來看看svcmgr_handler函數的實現:
int svcmgr_handler(struct binder_state *bs, struct binder_transaction_data_secctx *txn_secctx, 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; uint32_t dumpsys_priority; struct binder_transaction_data *txn = &txn_secctx->transaction_data; if (txn->target.ptr != BINDER_SERVICE_MANAGER) return -1; if (txn->code == PING_TRANSACTION) return 0; ... switch(txn->code) { case SVC_MGR_GET_SERVICE: case SVC_MGR_CHECK_SERVICE: s = bio_get_string16(msg, &len); if (s == NULL) { return -1; } handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid, (const char*) txn_secctx->secctx); if (!handle) break; 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; dumpsys_priority = bio_get_uint32(msg); if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority, txn->sender_pid, (const char*) txn_secctx->secctx)) return -1; break; case SVC_MGR_LIST_SERVICES: { uint32_t n = bio_get_uint32(msg); uint32_t req_dumpsys_priority = bio_get_uint32(msg); if (!svc_can_list(txn->sender_pid, (const char*) txn_secctx->secctx, txn->sender_euid)) { ALOGE("list_service() uid=%d - PERMISSION DENIED\n", txn->sender_euid); return -1; } si = svclist; // walk through the list of services n times skipping services that // do not support the requested priority while (si) { if (si->dumpsys_priority & req_dumpsys_priority) { if (n == 0) break; n--; } 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; }
我們先來認識一下binder的數據傳輸載體binder_transaction_data
:
struct binder_transaction_data { union { /* 當binder_transaction_data是由用戶空間的進程發送給Binder驅動時, handle是該事務的發送目標在Binder驅動中的信息,即該事務會交給handle來處理; handle的值是目標在Binder驅動中的Binder引用。*/ __u32 handle; /* 當binder_transaction_data是有Binder驅動反饋給用戶空間進程時, ptr是該事務的發送目標在用戶空間中的信息,即該事務會交給ptr對應的服務來處理; ptr是處理該事務的服務的服務在用戶空間的本地Binder對象。*/ binder_uintptr_t ptr; } target; // 該事務的目標對象(即,該事務數據包是給該target來處理的) // 只有當事務是由Binder驅動傳遞給用戶空間時,cookie才有意思,它的值是處理該事務的ServerC++層的本地Binder對象 binder_uintptr_t cookie; // 事務編碼。如果是請求,則以BC_開頭;如果是回復,則以BR_開頭。 __u32 code; /* General information about the transaction. */ __u32 flags; //表示事務發起者的pid和uid。 pid_t sender_pid; uid_t sender_euid; // 數據大小 binder_size_t data_size; //數據偏移量 binder_size_t offsets_size; //data是一個共用體,當通訊數據很小的時,可以直接使用buf[8]來保存數據。當夠大時,只能用指針buffer來描述一個申請的數據緩沖區。 union { struct { /* transaction data */ binder_uintptr_t buffer; binder_uintptr_t offsets; } ptr; __u8 buf[8]; } data; };
可以看到,svcmgr_handler函數中對binder data的事務編碼進行了判斷,并分別對SVC_MGR_GET_SERVICE(SVC_MGR_CHECK_SERVICE)
、SVC_MGR_ADD_SERVICE
、SVC_MGR_LIST_SERVICES
三種類型的事務編碼做了業務處理。
獲取服務
case SVC_MGR_CHECK_SERVICE: s = bio_get_string16(msg, &len); ptr = do_find_service(bs, s, len); if (!ptr) break; bio_put_ref(reply, ptr); return 0;
do_find_service函數中主要執行service的查找,并把找到的服務句柄寫入reply,返回給客戶端。
uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid, const char* sid) { struct svcinfo *si = find_svc(s, len); ... return si->handle; }
我們繼續看find_svc函數:
struct svcinfo *find_svc(const uint16_t *s16, size_t len) { struct svcinfo *si; for (si = svclist; si; si = si->next) { if ((len == si->len) && !memcmp(s16, si->name, len * sizeof(uint16_t))) { return si; } } return NULL; }
svclist 是一個單向鏈表,儲存了所有向servicemanager注冊的服務信息。find_svc遍歷svclist鏈表,通過服務名稱作為索引條件,最終找到符合條件的服務。
注冊服務
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; dumpsys_priority = bio_get_uint32(msg); if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority, txn->sender_pid, (const char*) txn_secctx->secctx)) return -1;
我們繼續看do_add_service函數中做了哪些事情。
在該函數中,首先會去檢查客戶端是否有權限注冊service,如果沒有權限就直接返回,不能注冊。
if (!svc_can_register(s, len, spid, sid, uid)) { ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n", str8(s, len), handle, uid); return -1; }
然后會去檢查該service是否已經注冊過了,如果已經注冊過,那么就不能再注冊了。
si = find_svc(s, len); if (si) { if (si->handle) { ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n", str8(s, len), handle, uid); svcinfo_death(bs, si); } si->handle = handle; }
再判斷內存是否足夠。
si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t)); if (!si) { ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n", str8(s, len), handle, uid); return -1; }
如果都沒什么問題,會注冊該service,并加入到svcList鏈表中。
綜上所述,servicemanager主要負責查詢和注冊其他的系統服務,是系統服務的管理者。
文章的最后,留給大家一個問題進行思考:
為什么Android需要設計servicemanager做中轉來添加和獲取系統服務,而不直接讓客戶端去獲取服務端句柄?
原文鏈接:https://juejin.cn/post/6999537375768477727
相關推薦
- 2022-04-28 python實現簡單的學生成績管理系統_python
- 2022-06-06 詳解如何自定義Dubbo Filter(含dubbo2.7.X及以上版本和2.6.X及以下版本兩種寫
- 2022-10-07 python?中pass和match使用方法_python
- 2022-05-18 C++?qt實現打開關閉狀態按鈕的代碼_C 語言
- 2022-08-16 Hive導入csv文件示例_數據庫其它
- 2023-04-04 Python筆記之Scipy.stats.norm函數使用解析_python
- 2022-05-11 C++程序代碼優化的方法實例大全_C 語言
- 2022-04-20 Prism區域管理器IRegionManager用法介紹_實用技巧
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支