1. 程式人生 > >Service與Android系統設計(4)-- ServiceManager

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);
    }
   ...
}}
  1. getSystemService()的實現。所有通過繼承Context類來建立自己的執行片段,都會呼叫getSystemService()來取得SystemService的引用。在這一方法裡,就是通過SYSTEM_SERVICE_MAP來取得Service所對應的Proxy物件。
  2. SYSTEM_SERVICE_MAP,也只不過是通過String標識出來的ServiceFetcher物件而已,為了加速查詢,使用了HashMap模板。
  3. 在Context物件裡,會通過registerService()方法,將ServiceFetcher物件填充到SYSTEM_SERVICE_MAP裡。
  4. ServiceFetcher類。這只是Context物件內,為了更好地組織Service而提供的一種中間型別,Service本身通過ServiceFetcher引用,於是可以靈活地替換其getService()或是createService()方法。
  5. 標準的getService()實現。在正常情況下,getService()只是從Cache中查詢是否已經建立好所需要的Service物件,如果有則直接返回這一物件,如果沒有,則建立所需要的Service物件,再返回。
  6. 標準的createService()實現。跟抽象類的實現類似,如果該ServiceFetcher上承載的Service沒有覆蓋createService()方法,則基本上可以認定是出錯了,此丟擲異常。所以ServiceFetcher在註冊時,要麼需要覆蓋getService()方法,否則必須要覆蓋更底層的createService()方法。
  7. 覆蓋getService()方法。因為上層只會通過getService()方法來往下進行訪問,於是覆蓋了getService()方法之後,則不需要再提供createService()了。這時我們就可以理解ServiceFetcher類的作用了,在系統實現上,Service有可能會通過getService()返回其例項,比如在SingleTon模式下構建的Service。我們不需要基於Service重構,而只需要在使用它的時候通過覆蓋getService()來進行靈活地重構,比如這裡的AccessibilityManager.getInstance()。
  8. 覆蓋createService()方法。這是更普遍的做法,每一個System Service,都會通過registerService()將createService()構造方法註冊到Context環境裡,這樣當應用程式呼叫getSystemService()時,在內部實際上會通過createService()來建立一個與Service對應的Proxy物件。我們前面分析過framework的構成,這樣的Proxy物件會是XXXManager的形式,於是我們實際上在createService()裡會建立ActivityManager之類的物件。
  9. 覆蓋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;
    }
  1. 每次通過ServiceManager的getService()方法取得一個SystemService的引用,實際上只是通過getIServiceManager()取回一個Proxy物件,然後再呼叫這個Proxy物件的getService()方法。
  2. 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;
}
  1. 從ServiceManagerNative,可以看到,它也是一個抽象類,繼承Binder,實現IServiceManager介面。於是它必然會實現asInterface()、asBinder()等Binder所需要的介面方法。但因為它本質上只是起到Proxy作用,作為一個抽象類,它並不可能被例項化,除非有一個非抽象類繼承它並實現它所缺少的方法。這種實現在程式碼層面上沒有意義,我們從後面的分析servicemanager的Native實現時也可以看得出來,於是我們雖然看到了onTransact()實現,但實際上它不起作用。
  2. 這是整個ServiceManagerNative類實現的最有意義的一行。雖然這一行與其他基於IBinder實現的遠端類沒什麼不同,但這一行提供了Proxy介面,這個Proxy介面,則是其他System Service所需要的訪問介面。在ServiceManagerNative類的asInterface()方法裡,我們會建立並返回一個Proxy物件ServiceManagerPoxy。
  3. 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在實現上的簡潔性。本質上,只進行了三步操作:

  1. binder_open(),在這一函式裡打開了binder驅動,然後通過mmap()系統呼叫直接映射了binder驅動提供的128 * 1024共128K位元組空間。這段空間使用者態程式設計時並不會用到,只是一種“偷”記憶體的技巧,binder驅動將使用這段使用者態記憶體,這樣使用binder驅動並不佔用任何系統記憶體,而Binder IPC所需要的記憶體,都存在於使用它的程序空間裡,一旦程序退出,而記憶體隨之被回收。
  2. binder_become_context_manager(),通過binder操作,標明自己是ContextManager。系統裡只有有唯一的Context Manager,而成為Context Manager則擁有了排程System Service執行的能力。
  3. 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資訊,然後再進行處理。於是,綜合得到的具體步驟是:

  1. struct binder_write_read。在一般程式設計時我們見不到這樣的資料結構,因為一般我們在程式設計上都是使用libinder封裝過的Binder傳輸。我們可以在後面的關於Binder的描述中看到,所有的Binder通訊,都是會用這樣的binder_write_read結構體,再通過ioctl()系統呼叫將這一結構體寫入binder,或是讀出來。
  2. 讓Binder進入讀迴圈。BC_ENTER_LOOPER,這一個命令操作到Binder驅動上,將使用Binder在當前程序得到的檔案描述符fd進入到迴圈監聽狀態,這種操作類似於TCP/IP程式設計時使用的bind()。需要注意的是,此時我們使用的一個32位元組的readbuf,但這readbuf在這裡並非用於讀,而只是借用過來發命令。
  3. 此時真正開始Binder資訊的讀取。而由於servicemanager本身所實現的RPC很簡單,只有兩種,於是可以假設,在通訊時資料量也會很小,於是這時只是通過binder_write_read結構體將只有32位元組的read_buf通知到Binder驅動,當系統裡其他任何部分呼叫到ServiceManager,都將迫使Binder驅動將該訪問資訊填寫到read_buf裡。
  4. 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結構,我們這裡也看一下其含義:

  1. target本身是一種union。target在本地則會是指向物件的指標,加上,而在遠端則是會是指向遠端物件的引用。
  2. cookie是輔助target的附加資訊,比如與target結合則具備了自動回收等後續處理能力
  3. code則是binder傳輸時的命令
  4. flag則是傳輸時指定的一些屬性值,像執行緒優先順序等
  5. sender_pid是傳送Binder命令的程序的pid,可用於程序是否存在的檢驗
  6. sender_euid,是傳送Binder命令的程序執行時使用的uid,可用於驗證程序的訪問許可權
  7. data_size,資料區的大小資訊
  8. offs_size, 資料區的偏移量資訊
  9. data,指向所需要操作的buffer,或是直接是內建的最多八位元組buffer
  10. 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;
}
  1. svcmgr_handler()函式,就跟我們Java環境裡的onTransact()方法一樣,但是因為是C環境,所以都是指標作為引數,寫得有點不直觀。這樣的程式設計風格,就有點C語言的進行面向物件式的程式設計技巧,binder_state和binder_txn用於Binder處理裡上下文環境判斷,而兩個分別用於收發的binder_io則是相當於Message封裝。
  2. 如果txn->handle,不是svcmgr_handle,也就是0,則屬於非法的servicemanager呼叫,於是直接退出。
  3. 在這裡我們可以體會C語言的面向物件式技巧,這裡取出要處理的binder_io裡的資訊,並不是直接使用字串指標操作,而是通過bio_get_*系列的封裝,類似於Java語言裡的Getter/Setter。於是,在這裡我們進一步對資料包的合法性進行驗證。
  4. 跟onTransact()方法一樣,這裡會使用switch語句來判斷命令是哪一種,我們可以看到servicemanager實際上很簡單,只需要支援三種型的Binder命令:get|check_service、add_service、list_service。
  5. SVC_MGR_GET_SERVICE和SVC_MGR_CHECK_SERVICE。雖然他們是兩個不同binder命令,但對於底層來說是一回,通過get_sevice成功,我們可以給check_service一個是否可以正常執行的應答。這一般會被使用service的部分來獲得Service的引用,對於內部實現來說,它只是在自己維護的一個svcinfo的單鏈表裡找到合適的service來響應服務請求。
  6. SVC_MGR_ADD_SERVICE。與get_service相反,這一命令則是將傳過來的service資訊新增到svcinfo的連結串列裡,然後get_service就可以被訪問到。
  7. 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環境共享。

相關推薦

ServiceAndroid系統設計4-- ServiceManager

System Service的驅動形式 --- ServiceManager 對於ServiceManager的使用,我們在應用程式程式設計時也會經常使用到,比如我們需要使用Sensor時,我們一般會做如下的呼叫: mSensorManager = (SensorMan

ServiceAndroid系統設計5-- libbinder

libbinder – Binder的Native實現 出於效能和程式碼統一性的角度考慮,Binder IPC並不Java和Native環境裡各實現一次,而只是分別在不同的執行環境裡提供使用的介面。使用Binder的Java程式碼,通過一些使用Binder的Java類之後

ServiceAndroid系統設計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、檢測要求 服務端程式至少需要做到以下功能: 檢測客戶端和當前伺服器端之間是否至