Service與Android系統設計(4)-- ServiceManager
System Service的驅動形式 --- ServiceManager
對於ServiceManager的使用,我們在應用程式程式設計時也會經常使用到,比如我們需要使用Sensor時,我們一般會做如下的呼叫:
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); mSensorManager.registerListener(this, mAccelerometer,SensorManager.SENSOR_DELAY_UI); @Override public void onSensorChanged(SensorEvent event) { if(event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) return; ... }
這樣的程式設計模式,API說明會告訴我們,每次系統里加速度計Sensor發生狀態變化時,就會觸發onSensorChanged()回撥方法被執行來處理Sensor的變動資訊。這裡就很好地說明了在系統範圍內使用Service的另一種模式,就是通過getSystemService(),取回一個System Service的引用例項,然後便可以呼叫Service實現的方法,比如我們例子裡的mSensorManager.registerListener()方法。
我們可以繼承跟蹤程式碼的實現,getSystemService()也並沒有什麼神奇的,它本質上只是建立一個Service列表的Cache而已,把ContextImpl.java與getSystemService()相關的實現抽出來,於是我們可以得到:
class ContextImpl extends Context { ... static class ServiceFetcher { 4 int mContextCacheIndex = -1; public Object getService(ContextImpl ctx) { 5 ArrayList<Object> cache = ctx.mServiceCache; Object service; synchronized (cache) { if (cache.size() == 0) { for (int i =0; i <sNextPerContextServiceCacheIndex; i++) { cache.add(null); } } else { service =cache.get(mContextCacheIndex); if (service != null) { return service; } } service = createService(ctx); cache.set(mContextCacheIndex,service); return service; } } public Object createService(ContextImpl ctx) { 6 throw new RuntimeException("Notimplemented"); } } private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP = new HashMap<String,ServiceFetcher>(); 2 private static int sNextPerContextServiceCacheIndex = 0; private static void registerService(String serviceName, ServiceFetcher fetcher) { 3 if(!(fetcher instanceof StaticServiceFetcher)) { fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++; } SYSTEM_SERVICE_MAP.put(serviceName, fetcher); } static { registerService(ACCESSIBILITY_SERVICE, new ServiceFetcher() { 7 public Object getService(ContextImpl ctx) { returnAccessibilityManager.getInstance(ctx); }}); registerService(ACTIVITY_SERVICE, new ServiceFetcher() { 8 public Object createService(ContextImpl ctx) { return newActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler()); }}); registerService(ALARM_SERVICE, new StaticServiceFetcher() { 9 public Object createStaticService() { IBinder b = ServiceManager.getService(ALARM_SERVICE); IAlarmManager service =IAlarmManager.Stub.asInterface(b); return new AlarmManager(service); }}); ... } @Override public Object getSystemService(String name) { 1 ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name); return fetcher == null ? null : fetcher.getService(this); } ... }}
- getSystemService()的實現。所有通過繼承Context類來建立自己的執行片段,都會呼叫getSystemService()來取得SystemService的引用。在這一方法裡,就是通過SYSTEM_SERVICE_MAP來取得Service所對應的Proxy物件。
- SYSTEM_SERVICE_MAP,也只不過是通過String標識出來的ServiceFetcher物件而已,為了加速查詢,使用了HashMap模板。
- 在Context物件裡,會通過registerService()方法,將ServiceFetcher物件填充到SYSTEM_SERVICE_MAP裡。
- ServiceFetcher類。這只是Context物件內,為了更好地組織Service而提供的一種中間型別,Service本身通過ServiceFetcher引用,於是可以靈活地替換其getService()或是createService()方法。
- 標準的getService()實現。在正常情況下,getService()只是從Cache中查詢是否已經建立好所需要的Service物件,如果有則直接返回這一物件,如果沒有,則建立所需要的Service物件,再返回。
- 標準的createService()實現。跟抽象類的實現類似,如果該ServiceFetcher上承載的Service沒有覆蓋createService()方法,則基本上可以認定是出錯了,此丟擲異常。所以ServiceFetcher在註冊時,要麼需要覆蓋getService()方法,否則必須要覆蓋更底層的createService()方法。
- 覆蓋getService()方法。因為上層只會通過getService()方法來往下進行訪問,於是覆蓋了getService()方法之後,則不需要再提供createService()了。這時我們就可以理解ServiceFetcher類的作用了,在系統實現上,Service有可能會通過getService()返回其例項,比如在SingleTon模式下構建的Service。我們不需要基於Service重構,而只需要在使用它的時候通過覆蓋getService()來進行靈活地重構,比如這裡的AccessibilityManager.getInstance()。
- 覆蓋createService()方法。這是更普遍的做法,每一個System Service,都會通過registerService()將createService()構造方法註冊到Context環境裡,這樣當應用程式呼叫getSystemService()時,在內部實際上會通過createService()來建立一個與Service對應的Proxy物件。我們前面分析過framework的構成,這樣的Proxy物件會是XXXManager的形式,於是我們實際上在createService()裡會建立ActivityManager之類的物件。
- 覆蓋createService()的另一種方法。對於System Service而言,雖然都在系統執行過程中一直存在,但有的會很忙,像Media、Audio、Graphic等,只要有Proxy,便可以響應其呼叫請求,有一部分則可能長期駐留後臺,只是偶爾響應一下請求,比如像我們例子裡看到AlarmManager。對於響應度不高的SystemService,一般都會在一個叫servicemanager的守護程序的監管之下,所以我們這裡會使用Stub.asInterface()介面方法申請物件,從而保證這一Service不被呼叫時則可以進入休眠。
在這個整體的執行流程裡,比較繞,而且深入程式碼分析時,我們也會看到在實現時的不一致,風格並非很嚴謹。但通過中間插入的這層ServiceFetcher,可以讓應用程式(或是某種需要使用getSystemService()方法的系統元件)很快找到合適的ServiceProxy端的初始化方法,快速建立起跟RemoteService的通訊。
除了getSystemService()方法與bindService()不同以外,在這時我們看不到兩者的任何區別。當然,我們在bindService()裡也會使用到onServiceConnected()回撥方法非同步地返回一個IBinder引用,但這也只是出於Service的生命週期考慮的結果。bindService()只是通過Intent找到合適的Service,而具體遠端Service的Binder引用,則是通過onServiceConnected()。所以本質上getSystemService()與bindService()只是形式上的區別,本質上是一回事,成功呼叫之後便可以進行一樣的RPC操作請求了。
我們可以注意到getService()與bindService()的一個重要區別,bindService()與unbindService()成對,而getService()只是單獨出現。於是,bindService()這種呼叫機制上的Service,總是在Bounded生命週期裡才能對外提供服務,可以做到按需啟動,不再需要時便會在合適的時間點被關閉。而getService()所操作的Service則沒有什麼生命週期,永遠在系統裡執行並提供服務,這裡也需要有種實體可以管理這些Service,當這些Service無人使用時,承載該Service的程序便會進入休眠中,這便是ServiceManager的Stub端所完成的功能。
ServiceManager在Java和Native環境裡各有其實現,但在Java端實際上只有Proxy端,而Native環境裡實現的servicemanager才具有完整的Proxy與Stub實現。
我們可以先來看ServiceManager.java的實現:
public finalclass ServiceManager {
private staticfinal String TAG = "ServiceManager";
private static IServiceManagersServiceManager;
private static IServiceManagergetIServiceManager() {
if(sServiceManager != null) {
return sServiceManager;
}
sServiceManager =ServiceManagerNative.asInterface(BinderInternal.getContextObject()); 2
return sServiceManager;
}
public static IBindergetService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
returngetIServiceManager().getService(name); 1
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
- 每次通過ServiceManager的getService()方法取得一個SystemService的引用,實際上只是通過getIServiceManager()取回一個Proxy物件,然後再呼叫這個Proxy物件的getService()方法。
- getIServiceManager(),實際上則是通過IServiceManager介面,訪問到一個ServiceManagerNative物件。
程式碼如此簡潔,於是我們可以跟蹤ServiceManagerNative的實現。從ServiceManagerNative類的實現上,我們也可以看到基於Binder收發兩端的實現,但實際上接收端沒有意義,也不會被執行到。程式碼如下:
package android.os;
import java.util.ArrayList;
public abstractclass ServiceManagerNative extends Binder implements IServiceManager 1
{
static public IServiceManagerasInterface(IBinder obj)
{
if(obj == null) {
return null;
}
IServiceManager in =
(IServiceManager)obj.queryLocalInterface(descriptor);
if(in != null) {
return in;
}
return new ServiceManagerProxy(obj); 2
}
publicServiceManagerNative()
{
attachInterface(this, descriptor);
}
public boolean onTransact(int code, Parcel data,Parcel reply,int flags)
{
...
}
public IBinderasBinder()
{
return this;
}
}
class ServiceManagerProxy implements IServiceManager {
publicServiceManagerProxy(IBinder remote) {
mRemote = remote;
}
public IBinderasBinder() {
return mRemote;
}
public IBindergetService(String name) throws RemoteException { 3
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeString(name);
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
return binder;
}
public IBindercheckService(String name)throws RemoteException {
…
return binder;
}
public void addService(Stringname, IBinder service,boolean allowIsolated)
throws RemoteException {
…
}
public String[]listServices() throws RemoteException {
ArrayList<String> services = new ArrayList<String>();
int n = 0;
while (true) {
…
}
return array;
}
public voidsetPermissionController(IPermissionController controller)
throws RemoteException {
…
}
private IBindermRemote;
}
- 從ServiceManagerNative,可以看到,它也是一個抽象類,繼承Binder,實現IServiceManager介面。於是它必然會實現asInterface()、asBinder()等Binder所需要的介面方法。但因為它本質上只是起到Proxy作用,作為一個抽象類,它並不可能被例項化,除非有一個非抽象類繼承它並實現它所缺少的方法。這種實現在程式碼層面上沒有意義,我們從後面的分析servicemanager的Native實現時也可以看得出來,於是我們雖然看到了onTransact()實現,但實際上它不起作用。
- 這是整個ServiceManagerNative類實現的最有意義的一行。雖然這一行與其他基於IBinder實現的遠端類沒什麼不同,但這一行提供了Proxy介面,這個Proxy介面,則是其他System Service所需要的訪問介面。在ServiceManagerNative類的asInterface()方法裡,我們會建立並返回一個Proxy物件ServiceManagerPoxy。
- ServiceManagerProxy物件裡實現就沒什麼特殊之處了。跟會通過Binder訪問到Remote Service的其他遠端方法一樣,會將本地需要呼叫的方法,將方法名與引數打包,將得到的命令通過Binder傳送出去,然後再等著遠端執行的返回。
此時,如果系統裡有某種實現,在標籤同是“android.os.IServiceManager”的Binder通訊管道上監聽,並響應getService()等方法的呼叫請求,這時整個基於ServiceManager的模型便完美了。我們可以繼續使用ActivityManagerService實現時同樣的技巧,繼承ServiceManagerNative並且實現一個onTransact()所需要的響應方法,但這樣的方式效能不夠好,不利於頻繁呼叫。
Java語言環境本身只是一種虛擬機器環境,Java虛擬機器在實現上強調的是對底層的封裝,並不會提供針對作業系統的某種功能,如果我們想實現對底層的訪問,則必須使用JNI來訪問。比如訪問Binder,如果把這樣的機制通過指令或是IO拓展的方式直接嵌入到Java語言裡,則會引發Android系統與Java語言的更大分裂。於是Binder本身是通過JNI拓展到Java語言裡的,這樣同時還達到一個高效的目的,雖然Java語言裡可以訪問到Binder的相應操作介面,但在底層實際上是通過C++實現的更高效的版本。既然Binder已經是C++實現,再通過JNI引入到Java環境裡,我們的ServiceManager的Stub實現就沒有必要到Java環境裡再重複一次了,可以直接在底層將IServiceManager介面所需要的遠端方法實現即可。這種方式更符合Java語法習慣、Native實現可以得到更高效ServiceManager,另外還提供了進一步實現NativeSerivce的可能性。既然底層是C++實現的,於是可以將Service的邏輯用C++寫出來,再通過Binder直接暴露到應用程式層即可。
System Service的Stub端 --- servicemanager程序
作為IServerManager的Stub端,它所需要完成的功能是提供getService()、addService()等方法,並通過這些遠端方法來控制什麼狀態下程序應該處於活躍狀態,而什麼時間點促使進進入休眠。到目前為此,它只需要給Java環境裡的ServiceManager類提供服務,但稍後面我們就會看到,它也需要提供同樣的服務介面給Native環境裡的用C++語言編寫的ServiceManager類。出於這樣需求,於是乾脆這一程式碼就用C語言來實現,以區別於使用服務的Java環境和C++環境裡的物件。
ServiceManager會是一個在init.rc裡定義的一個系統程序,在系統執行時全域性有效。在Android系統裡,唯一會與底層Binder驅動直接互動的,便是servicemanager程序(系統裡其他部分,都是通過libbinder封裝之後使用統一的訪問模型來進行)。監聽在Binder驅動 之上的servicemanager程序,相當於Android世界裡的“大內總管”。一方面,系統記憶體在的Service,並非全域性都知道,只有通過servicemanager才能查詢到;另一方面,所謂的System Service也需要有一種類似於RemoteService的收發自如的執行能力,被呼叫時便投入執行,而沒有被呼叫到時,雖不能被殺死掉,但也不會盲目的“空轉”執行。出於這樣的需求,便有servicemanager的實現框架。
無論出於什麼樣的設計需求,servicemanager都需要承當起service的管理功能,從一般的設計上來考察,或許這一實現會很複雜,但事實上並非如此。整個servicemanager的實現非常精練,加上binder通訊的處理過程,總共不超過一千行程式碼,而且是使用C語言寫出來的精練程式碼。Servicemanager的原始碼位於frameworks/base/cmds/servicemanager裡,通過service_manager.c實現主控部分,通過binder.c來實現與binder的通訊處理過程。
既然是C語言程式碼,我們可以先從main()方法分析起。
int main(int argc,char **argv)
{
struct binder_state*bs;
void *svcmgr =BINDER_SERVICE_MANAGER;
bs= binder_open(128*1024); 1
if(binder_become_context_manager(bs)) { 2
ALOGE("cannot becomecontext manager (%s)\n", strerror(errno));
return -1;
}
svcmgr_handle = svcmgr;
binder_loop(bs, svcmgr_handler); 3
return 0;
}
從這一個main()函式實現,我們可以看出servicemanager在實現上的簡潔性。本質上,只進行了三步操作:
- binder_open(),在這一函式裡打開了binder驅動,然後通過mmap()系統呼叫直接映射了binder驅動提供的128 * 1024共128K位元組空間。這段空間使用者態程式設計時並不會用到,只是一種“偷”記憶體的技巧,binder驅動將使用這段使用者態記憶體,這樣使用binder驅動並不佔用任何系統記憶體,而Binder IPC所需要的記憶體,都存在於使用它的程序空間裡,一旦程序退出,而記憶體隨之被回收。
- binder_become_context_manager(),通過binder操作,標明自己是ContextManager。系統裡只有有唯一的Context Manager,而成為Context Manager則擁有了排程System Service執行的能力。
- binder_loop()。在這一步裡,就跟Java裡實現的onTransact()一樣,從Binder通訊裡取出Binder命令,並響應其請求。
這種簡潔的實現,就使servicemanager這個程序有能力解析binder命令,然後根據不同命令排程不同程序進入執行時的活躍狀態,或是在不再被用到時進入到休眠狀態。我們把上述三個步驟開啟,就可以看到這一執行過程:
1 binder_open(),
開啟驅動並對映128K位元組空間。在後面對binder驅動的分析我們可以看到,這是android系統裡唯一一次使用這樣的方式來訪問binder,通過這樣的方式,則servicemanager可以直接操作binder驅動來分配的一段記憶體,而其他程序會通過ioctl的系統呼叫將操作請求,從使用者態拷貝到servicemanager的這段核心空間的記憶體。通過這種方式,減少了一次記憶體拷貝(servicemanager直接對映使用核心空間的binder區域)。
struct binder_state *binder_open(unsigned mapsize)
{
struct binder_state*bs;
bs= malloc(sizeof(*bs));
if (!bs) {
errno = ENOMEM;
return 0;
}
bs->fd = open("/dev/binder", O_RDWR);
if (bs->fd < 0) {
fprintf(stderr,"binder:cannot open device (%s)\n",
strerror(errno));
goto fail_open;
}
bs->mapsize = mapsize;
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd,0);
if (bs->mapped ==MAP_FAILED) {
fprintf(stderr,"binder:cannot map device (%s)\n",
strerror(errno));
goto fail_map;
}
/* TODO: check version */
return bs;
fail_map:
close(bs->fd);
fail_open:
free(bs);
return 0;
}
2 binder_become_context_manager(),
這就只是通過ioctl來操作一次binder驅動而已。
int binder_become_context_manager(struct binder_state *bs)
{
returnioctl(bs->fd, BINDER_SET_CONTEXT_MGR,0);
}
3 binder_loop(),
這一實現複雜一點,就是迴圈地從binder取回binder訊息,然後再通過傳入的回撥函式迴圈處理binder訊息。
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
structbinder_write_read bwr; 1
unsigned readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER; 2
binder_write(bs, readbuf, sizeof(unsigned));
for (;;) {
bwr.read_size = sizeof(readbuf); 3
bwr.read_consumed = 0;
bwr.read_buffer = (unsigned) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if(res < 0) {
ALOGE("binder_loop:ioctl failed (%s)\n", strerror(errno));
break;
}
res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func); 4
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驅動讀回IPC資訊,然後再進行處理。於是,綜合得到的具體步驟是:
- struct binder_write_read。在一般程式設計時我們見不到這樣的資料結構,因為一般我們在程式設計上都是使用libinder封裝過的Binder傳輸。我們可以在後面的關於Binder的描述中看到,所有的Binder通訊,都是會用這樣的binder_write_read結構體,再通過ioctl()系統呼叫將這一結構體寫入binder,或是讀出來。
- 讓Binder進入讀迴圈。BC_ENTER_LOOPER,這一個命令操作到Binder驅動上,將使用Binder在當前程序得到的檔案描述符fd進入到迴圈監聽狀態,這種操作類似於TCP/IP程式設計時使用的bind()。需要注意的是,此時我們使用的一個32位元組的readbuf,但這readbuf在這裡並非用於讀,而只是借用過來發命令。
- 此時真正開始Binder資訊的讀取。而由於servicemanager本身所實現的RPC很簡單,只有兩種,於是可以假設,在通訊時資料量也會很小,於是這時只是通過binder_write_read結構體將只有32位元組的read_buf通知到Binder驅動,當系統裡其他任何部分呼叫到ServiceManager,都將迫使Binder驅動將該訪問資訊填寫到read_buf裡。
- binder_parse()函式則會解析讀取到的Binder命令資訊。我們可以看到,readbuf, bwr.read_consumed, func,這三個引數將包含binder驅動裡讀取到的Binder命令、命令的長度,之後會跟所有C式的呼叫風格一樣,通過func回撥方法來處理這一個Binder命令。
再看binder_parse()函式,我們可以看到Binder會迴圈地讀取read_buf,根據不同命令作不同處理。在這個方法裡,我們看到全是BR_開頭的命令,在後面分析Binder執行原理時可以看到,BC是Binder Command的縮寫,BC_開頭的命令會全都是發出操作,而BR則是Bind Return的縮寫,BR_開頭的命令只會用於操作結果的返回,也就是說servicemanager進行的是被動地響應。而只有兩種情況會引發後續的處理:BR_TRANSACTION,會觸發Binder訊息的後續處理;而BR_DEAD_BINDER,則會觸發Binder相關資源的回收。
int binder_parse(struct binder_state *bs,struct binder_io *bio,
uint32_t *ptr, uint32_t size,binder_handler func)
{
int r = 1;
uint32_t *end = ptr + (size / 4);
while (ptr < end) {
uint32_t cmd = *ptr++;
switch(cmd) {
case BR_NOOP:
break;
case BR_TRANSACTION_COMPLETE:
break;
case BR_INCREFS:
case BR_ACQUIRE:
case BR_RELEASE:
case BR_DECREFS:
ptr += 2;
break;
case BR_TRANSACTION: {
struct binder_txn *txn = (void *) ptr;
if((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) {
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);
bio_init_from_txn(&msg,txn);
res = func(bs, txn, &msg,&reply);
binder_send_reply(bs,&reply, txn->data, res);
}
ptr += sizeof(*txn) / sizeof(uint32_t);
break;
}
case BR_REPLY: {
struct binder_txn *txn = (void*) ptr;
if((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) {
return -1;
}
binder_dump_txn(txn);
if(bio) {
bio_init_from_txn(bio, txn);
bio = 0;
}
ptr += (sizeof(*txn) / sizeof(uint32_t));
r = 0;
break;
}
case BR_DEAD_BINDER: {
struct binder_death *death = (void*) *ptr++;
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_parse()函式裡得到的Binder訊息會是BR_TRANSACTION,我們從後面對Binder驅動的描述可以看出,此時則會是通過proxy物件發過來的命令請求。我們從名字也大概可以看出一些端倪,BR_TRANSACTION,不是Binder傳送時的transact傳送,這必然會是onTransact(),用來處理Binder命令的接收處理。
由於Binder在傳輸時會使用很嚴整的結構以標識其IPC傳輸過程,於是,在servicemanager實現裡,使用了兩個新的結構體用於binder訊息的規整化,binder_txn與binder_io,binder_txn是binder訊息的包頭,跟核心態使用的binder_transaction_data結構一致,而binder_io則會進一步將binder傳輸時使用的buffer有效段位進一步準確地描述出來。
struct binder_txn
{
void *target; 1
void *cookie; 2
uint32_t code; 3
uint32_t flags; 4
uint32_t sender_pid; 5
uint32_t sender_euid; 6
uint32_t data_size; 7
uint32_t offs_size; 8
void *data; 9
void *offs; 10
};
Binder傳輸的是如此嚴整的資料結構,則在進行處理時提供了多種可能的處理功能,所以從servicemanager層面來看來,也不只是簡單地將資料讀出來,而會是通過不同的結構來進行訊息體的解析。雖然我們在後面的binder機制分析時還將看到binder_tranaction_data結構,我們這裡也看一下其含義:
- target本身是一種union。target在本地則會是指向物件的指標,加上,而在遠端則是會是指向遠端物件的引用。
- cookie是輔助target的附加資訊,比如與target結合則具備了自動回收等後續處理能力
- code則是binder傳輸時的命令
- flag則是傳輸時指定的一些屬性值,像執行緒優先順序等
- sender_pid是傳送Binder命令的程序的pid,可用於程序是否存在的檢驗
- sender_euid,是傳送Binder命令的程序執行時使用的uid,可用於驗證程序的訪問許可權
- data_size,資料區的大小資訊
- offs_size, 資料區的偏移量資訊
- data,指向所需要操作的buffer,或是直接是內建的最多八位元組buffer
- offs,buffer裡資料的具體偏移量
有了這樣資料結構,我們對BR_TRANSACTION的處理就比較容易理解了,在servicemanager裡,我們會有資料的收發處理,但對底層來說,都是binder_txn結構的資料格式,而對於上層處理,都是通過binder_io結構來完成。所以,在這時會將收到的buffer,通過bio_init_from_txn()轉換成binder_io,同時準備好應答的緩衝區,通過bio_init()來規整這一緩衝區。然後這兩個binder_io指向的訊息體會通過回撥函式func()進行處理,處理完則通過binder_send_reply()將func回撥函式處理得到的應答訊息寫回Binder,從而通知到呼叫servicemanager的地方。
回過頭去看binder_loop()函式,會發現其實處理binder的回撥函式func,是由service_manager.c裡的svcmgr_handler()函式來實現的。這樣實際在底層程式碼也完成了抽離,binder.c實現的是Binder通用處理,而servicemanager.c實現的則是專門的ServiceManager的處理邏輯。對於svcmgr_handler(),則跟Java環境裡的onTransact()區別不大了:
int svcmgr_handler(struct binder_state *bs, struct binder_txn*txn,
struct binder_io *msg, struct binder_io *reply) 1
{
struct svcinfo *si;
uint16_t *s;
unsigned len;
void *ptr;
uint32_t strict_policy;
int allow_isolated;
if (txn->target !=svcmgr_handle) 2
return -1;
strict_policy = bio_get_uint32(msg); 3
s= bio_get_string16(msg, &len);
if ((len != (sizeof(svcmgr_id) /2)) ||
memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
fprintf(stderr,"invalid id %s\n",str8(s));
return -1;
}
switch(txn->code) { 4
caseSVC_MGR_GET_SERVICE:
caseSVC_MGR_CHECK_SERVICE: 5
s = bio_get_string16(msg, &len);
ptr = do_find_service(bs, s, len, txn->sender_euid);
if(!ptr)
break;
bio_put_ref(reply, ptr);
return 0;
caseSVC_MGR_ADD_SERVICE: 6
s = bio_get_string16(msg, &len);
ptr = bio_get_ref(msg);
allow_isolated = bio_get_uint32(msg) ? 1 :0;
if(do_add_service(bs, s, len, ptr, txn->sender_euid, allow_isolated))
return -1;
break;
caseSVC_MGR_LIST_SERVICES: { 7
unsigned n = bio_get_uint32(msg);
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;
}
- svcmgr_handler()函式,就跟我們Java環境裡的onTransact()方法一樣,但是因為是C環境,所以都是指標作為引數,寫得有點不直觀。這樣的程式設計風格,就有點C語言的進行面向物件式的程式設計技巧,binder_state和binder_txn用於Binder處理裡上下文環境判斷,而兩個分別用於收發的binder_io則是相當於Message封裝。
- 如果txn->handle,不是svcmgr_handle,也就是0,則屬於非法的servicemanager呼叫,於是直接退出。
- 在這裡我們可以體會C語言的面向物件式技巧,這裡取出要處理的binder_io裡的資訊,並不是直接使用字串指標操作,而是通過bio_get_*系列的封裝,類似於Java語言裡的Getter/Setter。於是,在這裡我們進一步對資料包的合法性進行驗證。
- 跟onTransact()方法一樣,這裡會使用switch語句來判斷命令是哪一種,我們可以看到servicemanager實際上很簡單,只需要支援三種型的Binder命令:get|check_service、add_service、list_service。
- SVC_MGR_GET_SERVICE和SVC_MGR_CHECK_SERVICE。雖然他們是兩個不同binder命令,但對於底層來說是一回,通過get_sevice成功,我們可以給check_service一個是否可以正常執行的應答。這一般會被使用service的部分來獲得Service的引用,對於內部實現來說,它只是在自己維護的一個svcinfo的單鏈表裡找到合適的service來響應服務請求。
- SVC_MGR_ADD_SERVICE。與get_service相反,這一命令則是將傳過來的service資訊新增到svcinfo的連結串列裡,然後get_service就可以被訪問到。
- SVC_MGR_LIST_SERVICE。這一命令會將自己維護的svcinfo連結串列遍歷一次,然後將所儲存的service資訊返回給呼叫端。
通過引入servicemanager這樣一種機制,在我們系統設計上就得到方便,我們不再需要系統內部的bindService(),但又提供了類似於bindService()這樣按需啟動的功能。我們在系統裡實現的一個Remote Service,也就是SystemService,在它啟動時會呼叫add_service()函式將自己加入到servicemanager的svcinfo連結串列裡,然後進入休眠。雖然大部分情況下,我們都是使用Java來實現System Service,但由於在實現上是Java端傳送Binder命令,Native端實現的servicemanager程序來接收並處理Binder命令,於是本質上是一回事。serviamanger維護了svcinfo連結串列之後,實際上service在沒有被用到時,都是在休眠狀態。客戶端則可以通過get_service()來發送Binder訊息到servicemanager,此時servicemanager會告訴客戶端該service是否存在,並且在存在的情況下,返回service的IBinder引用到客戶端,並同時喚醒service,於是後續的流程上,客戶端就可以直接通過IBinder引用(也就是通過asInterface()方法得到的Proxy物件),直接跟service進行RPC互動了。如圖所示:
在某個Service物件完成自己的初始化後,就會呼叫add_service(),將自己加入到Service列表,然後就會在Binder驅動上休眠。應用程式會呼叫get_service(),嘗試跟某個SystemService建立互動,此時也在Binder驅動上休眠。當servicemanager做完合法性判斷之後,則會喚醒收發兩端的程序進入到互動過程裡。從這個意義上來說,servicemanager雖然程式碼如此簡單,但也起到了“大內總管”的職能,會管理System Service,並在合適點喚醒它們投入執行。
既然一個簡單的Service Manager,呼叫頻度不那麼高,也會直接通過C這樣的Native語言來編寫,get_service()/add_service()的呼叫頻度比Binder通訊要低多了,那我們Binder通訊本身也應該使用native實現了。另外,Java語言本身並不知道Binder的存在,為了支援Binder這種特殊的IPC,我們也應該使用Native編碼來匯入Binder IPC通訊,問題在於Native的程度會有多深。出於頻率上的考慮,Binder通訊大部分都使用Native實現,只是給Java環境提供一層封裝,供Java環境直接使用,這就是libbinder,由frameworks/base/libs/binder來實現。從Android 4.1開始,為了使NDK與Native實現相容,這一目錄已經換至frameworks/native/libs/binder,與NDK環境共享。相關推薦
Service與Android系統設計(4)-- ServiceManager
System Service的驅動形式 --- ServiceManager 對於ServiceManager的使用,我們在應用程式程式設計時也會經常使用到,比如我們需要使用Sensor時,我們一般會做如下的呼叫: mSensorManager = (SensorMan
Service與Android系統設計(5)-- libbinder
libbinder – Binder的Native實現 出於效能和程式碼統一性的角度考慮,Binder IPC並不Java和Native環境裡各實現一次,而只是分別在不同的執行環境裡提供使用的介面。使用Binder的Java程式碼,通過一些使用Binder的Java類之後
Service與Android系統設計(6)--- Native Service
Native Service Native Service,這是Android系統裡的一種特色,就是通過C++或是C程式碼寫出來的,供Java進行遠端呼叫的Remote Service,因為C/C++程式碼生成的是Native程式碼(機器程式碼),於是叫Native Se
基於中臺思想的物流系統設計(四):物流服務與物流詳情
一、概述 在物流系統中,中臺只負責物流訂單的流轉,具體的物流履行往往需要對接第三方快遞公司。由於第三方快遞公司的技術標準不一樣,因此我們需要對第三方快遞公司的介面進行封裝,這裡涉及到兩大類封裝,一個是下發請求的封裝,一個是接收回傳的物流詳情的封裝。對於下發快遞公司,我們不僅僅是介面層面的封裝,而是抽象出了
Android中的Service與程序間通訊(IPC)詳解
Service 什麼是Service 在後臺長期執行的沒有介面的元件。其他元件可以啟動Service讓他在後臺執行,或者繫結Service與它進行互動,甚至實現程序間通訊(IPC)。例如,可以讓服務在後臺處理網路互動,播放音樂,檔案I/O,或者與Cont
Linux系統程式設計(4)——檔案與IO之ioctl函式
ioctl是裝置驅動程式中對裝置的I/O通道進行管理的函式。所謂對I/O通道進行管理,就是對裝置的一些特性進行控制,例如串列埠的傳輸波特率、馬達的轉速等等。它的引數個數如下:int ioctl(int fd, int cmd, …);其中fd就是使用者程式開啟裝置時使用ope
基於RTP的h.264視頻傳輸系統設計(一)
-i 感謝 項目 頻率 算術 處理 rop sel 決定 一、H.264 的層次介紹 H.264 定義三個層次,每一個層次支持一組特定的編碼功能。而且按照各個層次指定所指定的功能。基礎層次(baselineprofile)支持 I 幀和 P 幀【1】的幀內和幀間
Android系統架構(一)
查詢 核心 手機 例如 ava 模塊 api 操作系統 運行 一、Android系統版本簡介 Android操作系統已占據了手機操作系統的大半壁江山,截至本文寫作時,Android操作系統系統版本及其詳細信息,已發生了變化,具體信息見下表,當然也可以訪問https:
微服務架構下的監控系統設計(一)——指標數據的采集展示
ans 定義數據 采集函數 健康 eset 中間件 松耦合 實例 叠代優化 前言微服務是一種架構風格,一個大型復雜軟件應用通常由多個微服務組成。系統中的各個微服務可被獨立部署,各個微服務之間是松耦合的。每個微服務僅關註於完成一件任務並很好地完成該任務。微服務之前很多單體應用
基於中臺思想的物流系統設計(二):構建物流訂單能力
一、引言 物流訂單能力作為基礎能力,需要設計一套穩定的訂單模型,以及一套能夠在高併發環境下持續可用的介面。這些介面作為原子介面,供上層業務複用。上層業務無論多麼複雜,通過這些原子介面,最終都會收斂到穩定的訂單模型中來,這也是區分基礎能力和產品服務的一個重要的邊界。 本文通過以下5點來介紹如何構建一套物流訂
聚合支付系統設計(二)
支付閘道器與非同步通知設計 支付閘道器 使用者下單成功後,要經過收銀臺發起支付流程,支付閘道器就是使用者發起支付流程的入口地址。支付閘道器需要接收訂單的部分資料(訂單號、待支付金額、商品描述資訊等)和交易資料(支付方式、交易起止時間、回撥地址等)以及簽名,支付閘道器接收到收銀臺的支付請求後,驗證
聚合支付系統設計(一)
商戶聚合支付系統設計(一) 產品概述與整體設計 背景 如今,網購已經滲透到人們日常生活中的方方面面,做為網購的載體,網際網路電商平臺發展如火如荼,支付功能做為其不可或缺的一部分,實現起來,也有各種各樣的方案。根據自己有限的認知,我主觀上把目前行業內的支付實現方案做以下歸
基於OpenCV3.0的車牌識別系統設計(二)--車牌提取
寫在前面的話 上一篇開篇博文寫好之後找女朋友看了一下,希望她提一點建設性建議。結果她很委婉的告訴我,寫的還行就是太表面了,告訴我要注意細節的描述與具體的實現過程與原理等等。其實我只是想騙她看一下增加一下點選量,順便知道我寫的部落格新手能不能看懂而已。結果她告訴我,她那麼聰明當然能看懂,別人就
基於OpenCV3.0的車牌識別系統設計(一)--系統綜述
寫在前面的話 車牌識別是影象處理技術的實際生活中一個非常重要的應用場景,目前車牌識別系統已經非常完善,識別準確率高達99%以上。作為學生,在學習影象處理時,自己搭建車牌識別系統是非常有價值的,作為入門專案有助於快速入門。並且在識
基於中臺思想的物流系統設計(三):構建物流地址能力
一、引言 在電商物流領域我們會涉及到地址,其中包括了基礎的四級地址和使用者填寫的地址。四級地址在整個從下單到收貨的業務流程中都會用到,因此設計的時候要考慮如何最大限度地提高QPS。使用者地址在下單的時候讓使用者填寫或者選擇,然後存在交易訂單和物流訂單上,後續的流程一般不會變,如果使用者需要修改地址,直接變
FPGA-08-任務五、十字路口交通控制燈器系統設計(一)
設計一個十字路口交通控制系統,其東西、南北兩個方向除了有紅、黃、綠燈指示是否允許通行外,還設有時間顯示,以倒計時方式顯示每一路允許通行的時間,綠燈、黃燈、紅燈的持續時間分別是45、5和50秒。當東西或南北兩路中任一道上出現特殊情況,例如有消防車,警車要去執行
FPGA-12-任務五、十字路口交通控制燈器系統設計(二)
完整功能: 實現主幹道和支幹道的紅綠燈,並實現時間顯示功能;(前兩位顯示東西的 後兩位顯示南北的) 實現綠燈,黃燈,紅燈的持續時間固定的交通控制功能; (狀態機切換三段的顯示 ) 當東西或南北兩路中任一道上出現特殊情況,交通控制系統應可由交警手動控制立即進入特
課設 - 基於FPGA的電子警察系統設計(流程)
本文以FPGA晶片為核心,來檢測運動車輛是否超速以及車輛是否闖紅燈。 通過攝像機採集到的影象以影象處理的方法進行處理,然後通過MATLAB軟體將採集到的圖片轉化成Verilog可識別的的數字程式碼,再加以幀間差分法、最小二乘法,對處理過後的影象進行進一步的
吳裕雄 資料探勘與分析案例實戰(4)——python資料處理工具:Pandas
# 匯入模組import pandas as pdimport numpy as np # 構造序列gdp1 = pd.Series([2.8,3.01,8.99,8.59,5.18])print(gdp1)# 取出gdp1中的第一、第四和第五個元素print('行號風格的序列:\n',gdp1[[0,3,
網路穿透與音視訊技術(4)——NAT對映檢測和常見網路穿越方法論(NAT檢測實踐1)
2.2、檢測過程實戰——伺服器端 要進行NAT對映檢測,按照上文提到的檢測方式,我們就需要一個服務端檢測程式。並將服務端檢測程式部署到具有兩個外網IP的硬體環境下。 2.2.1、檢測要求 服務端程式至少需要做到以下功能: 檢測客戶端和當前伺服器端之間是否至