首页 > 资讯 > 电子杂谈

深入浅出arm7_深入浅出arm7--lpc213x设计模式于数据分析

字号: T | T
2014-12-03  来源:深入浅出arm7   作者:深入浅出arm7  浏览次数: 26
[摘要]深入浅出arm7_深入浅出arm7--lpc213x设计模式于数据分析:深入浅出ARM7LPC213x/LPC214x(下册)的源代码在哪有?我昨天在长沙买了
 深入浅出arm7_深入浅出arm7--lpc213x设计模式于数据分析:

深入浅出ARM7—LPC213x/LPC214x(下册)的源代码在哪有?
我昨天在长沙买了深入浅出ARM7—LPC213x/LPC214x(下册)
很高兴,盼望已久。
可是书没有像原来宣传的那样有光盘。
我买了 EasyARM2131,带有上册书
可是光盘里没有usb通讯的部分源程序。

书上的例子要一行行输入吗?
如果哪位有,可否给我一份,或者告诉我下载的地方
sv@liwensoft.com, QQ:635477196
thank you
深入浅出arm7--lpc213x


    mOut.write(&tr, sizeof(tr));

//仅仅写到了Parcel中,Parcel好像没和/dev/binder设备有什么关联啊?

恩,那只能在另外一个地方写到binder设备中去了。难道是在?

    return NO_ERROR;

}

//说对了,就是在waitForResponse中

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)

{

    int32_t cmd;

    int32_t err;



while (1) {

//talkWithDriver,哈哈,应该是这里了

        if ((err=talkWithDriver()) < NO_ERROR) break;

        err = mIn.errorCheck();

        if (err < NO_ERROR) break;

        if (mIn.dataAvail() == 0) continue;

        //看见没?这里开始操作mIn了,看来talkWithDriver中

//把mOut发出去,然后从driver中读到数据放到mIn中了。

        cmd = mIn.readInt32();



        switch (cmd) {

        case BR_TRANSACTION_COMPLETE:

            if (!reply && !acquireResult) goto finish;

            break;

   .....

    return err;

}

status_t IPCThreadState::talkWithDriver(bool doReceive)

{

binder_write_read bwr;

   //中间东西太复杂了,不就是把mOut数据和mIn接收数据的处理后赋值给bwr吗?

    status_t err;

    do {

//用ioctl来读写

        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)

            err = NO_ERROR;

        else

            err = -errno;

  } while (err == -EINTR);

//到这里,回复数据就在bwr中了,bmr接收回复数据的buffer就是mIn提供的

        if (bwr.read_consumed > 0) {

            mIn.setDataSize(bwr.read_consumed);

            mIn.setDataPosition(0);

        }

return NO_ERROR;

}

好了,到这里,我们发送addService的流程就彻底走完了。

BpServiceManager发送了一个addService命令到BnServiceManager,然后收到回复。

先继续我们的main函数。

int main(int argc, char** argv)

{

    sp<ProcessState> proc(ProcessState::self());

    sp<IServiceManager> sm = defaultServiceManager();   

MediaPlayerService::instantiate();

---》该函数内部调用addService,把MediaPlayerService信息 add到ServiceManager中

    ProcessState::self()->startThreadPool();

    IPCThreadState::self()->joinThreadPool();

}

这里有个容易搞晕的地方:

MediaPlayerService是一个BnMediaPlayerService,那么它是不是应该等着

BpMediaPlayerService来和他交互呢?但是我们没看见MediaPlayerService有打开binder设备的操作啊!

这个嘛,到底是继续addService操作的另一端BnServiceManager还是先说

BnMediaPlayerService呢?

还是先说BnServiceManager吧。顺便把系统的Binder架构说说。

2.8 BnServiceManager

上面说了,defaultServiceManager返回的是一个BpServiceManager,通过它可以把命令请求发送到binder设备,而且handle的值为0。那么,系统的另外一端肯定有个接收命令的,那又是谁呢?

很可惜啊,BnServiceManager不存在,但确实有一个程序完成了BnServiceManager的工作,那就是service.exe(如果在windows上一定有exe后缀,叫service的名字太多了,这里加exe就表明它是一个程序)

位置在framework/base/cmdsrvicemanger.c中。

int main(int argc, char **argv)

{

    struct binder_state *bs;

    void *svcmgr = BINDER_SERVICE_MANAGER;

    bs = binder_open(128*1024);//应该是打开binder设备吧?

    binder_become_context_manager(bs) //成为manager

    svcmgr_handle = svcmgr;

    binder_loop(bs, svcmgr_handler);//处理BpServiceManager发过来的命令

}

看看binder_open是不是和我们猜得一样?

struct binder_state *binder_open(unsigned mapsize)

{

    struct binder_state *bs;

    bs = malloc(sizeof(*bs));

   ....

    bs->fd = open("/dev/binder", O_RDWR);//果然如此

  ....

    bs->mapsize = mapsize;

    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);

  }

再看看binder_become_context_manager

int binder_become_context_manager(struct binder_state *bs)

{

    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);//把自己设为MANAGER

}

binder_loop 肯定是从binder设备中读请求,写回复的这么一个循环吧?

void binder_loop(struct binder_state *bs, binder_handler func)

{

    int res;

    struct binder_write_read bwr;

    readbuf[0] = BC_ENTER_LOOPER;

    binder_write(bs, readbuf, sizeof(unsigned));

    for (;;) {//果然是循环

        bwr.read_size = sizeof(readbuf);

        bwr.read_consumed = 0;

        bwr.read_buffer = (unsigned) readbuf;



        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

      //哈哈,收到请求了,解析命令

        res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);

  }

这个...后面还要说吗??

恩,最后有一个类似handleMessage的地方处理各种各样的命令。这个就是

svcmgr_handler,就在ServiceManager.c中

int svcmgr_handler(struct binder_state *bs,

                   struct binder_txn *txn,

                   struct binder_io *msg,

                   struct binder_io *reply)

{

    struct svcinfo *si;

    uint16_t *s;

    unsigned len;

    void *ptr;



    s = bio_get_string16(msg, &len);

    switch(txn->code) {

    case SVC_MGR_ADD_SERVICE:

        s = bio_get_string16(msg, &len);

        ptr = bio_get_ref(msg);

        if (do_add_service(bs, s, len, ptr, txn->sender_euid))

            return -1;

        break;

...

其中,do_add_service真正添加BnMediaService信息

int do_add_service(struct binder_state *bs,

                   uint16_t *s, unsigned len,

                   void *ptr, unsigned uid)

{

    struct svcinfo *si;

    si = find_svc(s, len);s是一个list

     si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));

       si->ptr = ptr;

        si->len = len;

        memcpy(si->name, s, (len + 1) * sizeof(uint16_t));

        si->name[len] = \0

        si->death.func = svcinfo_death;

        si->death.ptr = si;

        si->next = svclist;

        svclist = si; //看见没,这个svclist是一个列表,保存了当前注册到ServiceManager

中的信息

    }

   binder_acquire(bs, ptr);//这个吗。当这个Service退出后,我希望系统通知我一下,好释放上面malloc出来的资源。大概就是干这个事情的。

    binder_link_to_death(bs, ptr, &si->death);

    return 0;

}

喔,对于addService来说,看来ServiceManager把信息加入到自己维护的一个服务列表中了。

2.9 ServiceManager存在的意义

为何需要一个这样的东西呢?

原来,Android系统中Service信息都是先add到ServiceManager中,由ServiceManager来集中管理,这样就可以查询当前系统有哪些服务。而且,Android系统中某个服务例如MediaPlayerService的客户端想要和MediaPlayerService通讯的话,必须先向ServiceManager查询MediaPlayerService的信息,然后通过ServiceManager返回的东西再来和MediaPlayerService交互。

毕竟,要是MediaPlayerService身体不好,老是挂掉的话,客户的代码就麻烦了,就不知道后续新生的MediaPlayerService的信息了,所以只能这样:

l         MediaPlayerService向SM注册

l         MediaPlayerClient查询当前注册在SM中的MediaPlayerService的信息

l         根据这个信息,MediaPlayerClient和MediaPlayerService交互

另外,ServiceManager的handle标示是0,所以只要往handle是0的服务发送消息了,最终都会被传递到ServiceManager中去。

三 MediaService的运行

上一节的知识,我们知道了:

l         defaultServiceManager得到了BpServiceManager,然后MediaPlayerService 实例化后,调用BpServiceManager的addService函数

l         这个过程中,是service_manager收到addService的请求,然后把对应信息放到自己保存的一个服务list中

到这儿,我们可看到,service_manager有一个binder_looper函数,专门等着从binder中接收请求。虽然service_manager没有从BnServiceManager中派生,但是它肯定完成了BnServiceManager的功能。

同样,我们创建了MediaPlayerService即BnMediaPlayerService,那它也应该:

l         打开binder设备

l         也搞一个looper循环,然后坐等请求

service,service,这个和网络编程中的监听socket的工作很像嘛!

好吧,既然MediaPlayerService的构造函数没有看到显示的打开binder设备,那么我们看看它的父类即BnXXX又到底干了些什么呢?

3.1 MediaPlayerService打开binder

class MediaPlayerService : public BnMediaPlayerService

// MediaPlayerService从BnMediaPlayerService派生

//而BnMediaPlayerService从BnInterface和IMediaPlayerService同时派生

class BnMediaPlayerService: public BnInterface<IMediaPlayerService>

{

public:

    virtual status_t    onTransact( uint32_t code,

                                    const Parcel& data,

                                    Parcel* reply,

                                    uint32_t flags = 0);

};

看起来,BnInterface似乎更加和打开设备相关啊。

template<typename INTERFACE>

class BnInterface : public INTERFACE, public BBinder

{

public:

    virtual sp<IInterface>      queryLocalInterface(const String16& _descriptor);

    virtual const String16&     getInterfaceDescriptor() const;



protected:

    virtual IBinder*            onAsBinder();

};

兑现后变成

class BnInterface : public IMediaPlayerService, public BBinder

BBinder?BpBinder?是不是和BnXXX以及BpXXX对应的呢?如果是,为什么又叫BBinder呢?

BBinder::BBinder()

    : mExtras(NULL)

{

//没有打开设备的地方啊?

}

完了?难道我们走错方向了吗?难道不是每个Service都有对应的binder设备fd吗?

.......

回想下,我们的Main_MediaService程序,有哪里打开过binder吗?

int main(int argc, char** argv)

{

//对啊,我在ProcessState中不是打开过binder了吗?



    sp<ProcessState> proc(ProcessState::self());

    sp<IServiceManager> sm = defaultServiceManager();

MediaPlayerService::instantiate();   

  ......

3.2 looper  

啊?原来打开binder设备的地方是和进程相关的啊?一个进程打开一个就可以了。那么,我在哪里进行类似的消息循环looper操作呢?

...

//难道是下面两个?

ProcessState::self()->startThreadPool();

IPCThreadState::self()->joinThreadPool();

看看startThreadPool吧

void ProcessState::startThreadPool()

{

  ...

    spawnPooledThread(true);

}

void ProcessState::spawnPooledThread(bool isMain)

{

    sp<Thread> t = new PoolThread(isMain);isMain是TRUE

//创建线程池,然后run起来,和java的Thread何其像也。

    t->run(buf);

 }

PoolThread从Thread类中派生,那么此时会产生一个线程吗?看看PoolThread和Thread的构造吧

PoolThread::PoolThread(bool isMain)

        : mIsMain(isMain)

    {

    }

Thread::Thread(bool canCallJava)//canCallJava默认值是true

    :   mCanCallJava(canCallJava),

        mThread(thread_id_t(-1)),

        mLock("Thread::mLock"),

        mStatus(NO_ERROR),

        mExitPending(false), mRunning(false)

{

}

喔,这个时候还没有创建线程呢。然后调用PoolThread::run,实际调用了基类的run。

status_t Thread::run(const char* name, int32_t priority, size_t stack)

{

  bool res;

    if (mCanCallJava) {

        res = createThreadEtc(_threadLoop,//线程函数是_threadLoop

                this, name, priority, stack, &mThread);

    }

//终于,在run函数中,创建线程了。从此

主线程执行

IPCThreadState::self()->joinThreadPool();

新开的线程执行_threadLoop

我们先看看_threadLoop

int Thread::_threadLoop(void* user)

{

    Thread* const self = static_cast<Thread*>(user);

    sp<Thread> strong(self->mHoldSelf);

    wp<Thread> weak(strong);

    self->mHoldSelf.clear();



    do {

 ...

        if (result && !self->mExitPending) {

                result = self->threadLoop();哇塞,调用自己的threadLoop

            }

        }

我们是PoolThread对象,所以调用PoolThread的threadLoop函数

virtual bool PoolThread ::threadLoop()

    {

//mIsMain为true。

//而且注意,这是一个新的线程,所以必然会创建一个

新的IPCThreadState对象(记得线程本地存储吗?TLS),然后      

IPCThreadState::self()->joinThreadPool(mIsMain);

        return false;

    }

主线程和工作线程都调用了joinThreadPool,看看这个干嘛了!

void IPCThreadState::joinThreadPool(bool isMain)

{

     mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

     status_t result;

    do {

        int32_t cmd;

         result = talkWithDriver();

         result = executeCommand(cmd);

        }

       } while (result != -ECONNREFUSED && result != -EBADF);



    mOut.writeInt32(BC_EXIT_LOOPER);

    talkWithDriver(false);

}

看到没?有loop了,但是好像是有两个线程都执行了这个啊!这里有两个消息循环?

下面看看executeCommand

status_t IPCThreadState::executeCommand(int32_t cmd)

{

BBinder* obj;

    Refbase::weakref_type* refs;

    status_t result = NO_ERROR;

case BR_TRANSACTION:

        {

            binder_transaction_data tr;

            result = mIn.read(&tr, sizeof(tr));

//来了一个命令,解析成BR_TRANSACTION,然后读取后续的信息

       Parcel reply;

             if (tr.target.ptr) {

//这里用的是BBinder。

                sp<BBinder> b((BBinder*)tr.cookie);

                const status_t error = b->transact(tr.code, buffer, &reply, 0);

}

让我们看看BBinder的transact函数干嘛了

status_t BBinder::transact(

    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

就是调用自己的onTransact函数嘛      

err = onTransact(code, data, reply, flags);

    return err;

}

BnMediaPlayerService从BBinder派生,所以会调用到它的onTransact函数 

终于水落石出了,让我们看看BnMediaPlayerServcice的onTransact函数。

status_t BnMediaPlayerService::onTransact(

    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

// BnMediaPlayerService从BBinder和IMediaPlayerService派生,所有IMediaPlayerService

//看到下面的switch没?所有IMediaPlayerService提供的函数都通过命令类型来区分

//

    switch(code) {

        case CREATE_URL: {

            CHECK_INTERFACE(IMediaPlayerService, data, reply);

            create是一个虚函数,由MediaPlayerService来实现!!

sp<IMediaPlayer> player = create(

                    pid, client, url, numHeaders > 0 ? &headers : NULL);



            reply->writeStrongBinder(player->asBinder());

            return NO_ERROR;

        } break;

其实,到这里,我们就明白了。BnXXX的onTransact函数收取命令,然后派发到派生类的函数,由他们完成实际的工作。

说明:

这里有点特殊,startThreadPool和joinThreadPool完后确实有两个线程,主线程和工作线程,而且都在做消息循环。为什么要这么做呢?他们参数isMain都是true。不知道google搞什么。难道是怕一个线程工作量太多,所以搞两个线程来工作?这种解释应该也是合理的。

网上有人测试过把最后一句屏蔽掉,也能正常工作。但是难道主线程提出了,程序还能不退出吗?这个...管它的,反正知道有两个线程在那处理就行了。

四 MediaPlayerClient

这节讲讲MediaPlayerClient怎么和MediaPlayerService交互。

使用MediaPlayerService的时候,先要创建它的BpMediaPlayerService。我们看看一个例子

IMediaDeathNotifier::getMediaPlayerService()

{

        sp<IServiceManager> sm = defaultServiceManager();

        sp<IBinder> binder;

        do {

//向SM查询对应服务的信息,返回binder           

binder = sm->getService(String16("media.player"));

            if (binder != 0) {

                break;

             }

             usleep(500000); // 0.5 s

        } while(true);



//通过interface_cast,将这个binder转化成BpMediaPlayerService

//注意,这个binder只是用来和binder设备通讯用的,实际

//上和IMediaPlayerService的功能一点关系都没有。

//还记得我说的Bridge模式吗?BpMediaPlayerService用这个binder和BnMediaPlayerService

//通讯。

    sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);

    }

    return sMediaPlayerService;

}

为什么反复强调这个Bridge?其实也不一定是Bridge模式,但是我真正想说明的是:

Binder其实就是一个和binder设备打交道的接口,而上层IMediaPlayerService只不过把它当做一个类似socket使用罢了。我以前经常把binder和上层类IMediaPlayerService的功能混到一起去。

当然,你们不一定会犯这个错误。但是有一点请注意:

4.1 Native层

刚才那个getMediaPlayerService代码是C++层的,但是整个使用的例子确实JAVA->JNI层的调用。如果我要写一个纯C++的程序该怎么办?

int main()

{

  getMediaPlayerService();直接调用这个函数能获得BpMediaPlayerService吗?

不能,为什么?因为我还没打开binder驱动呐!但是你在JAVA应用程序里边却有google已经替你

封装好了。

所以,纯native层的代码,必须也得像下面这样处理:

sp<ProcessState> proc(ProcessState::self());//这个其实不是必须的,因为

//好多地方都需要这个,所以自动也会创建.

getMediaPlayerService();

还得起消息循环呐,否则如果Bn那边有消息通知你,你怎么接受得到呢?

ProcessState::self()->startThreadPool();

//至于主线程是否也需要调用消息循环,就看个人而定了。不过一般是等着接收其他来源的消息,例如socket发来的命令,然后控制MediaPlayerService就可以了。

}



五 实现自己的Service

好了,我们学习了这么多Binder的东西,那么想要实现一个自己的Service该咋办呢?

如果是纯C++程序的话,肯定得类似main_MediaService那样干了。

int main()

{

  sp<ProcessState> proc(ProcessState::self());

sp<IServiceManager> sm = defaultServiceManager();

sm->addService(“service.name”,new XXXService());

ProcessState::self()->startThreadPool();

IPCThreadState::self()->joinThreadPool();

}

看看XXXService怎么定义呢?

我们需要一个Bn,需要一个Bp,而且Bp不用暴露出来。那么就在BnXXX.cpp中一起实现好了。

另外,XXXService提供自己的功能,例如getXXX调用

5.1 定义XXX接口

XXX接口是和XXX服务相关的,例如提供getXXX,setXXX函数,和应用逻辑相关。

需要从IInterface派生

class IXXX: public IInterface

{

public:

DECLARE_meta_INTERFACE(XXX);申明宏

virtual getXXX() = 0;

virtual setXXX() = 0;

}这是一个接口。

5.2 定义BnXXX和BpXXX

为了把IXXX加入到Binder结构,需要定义BnXXX和对客户端透明的BpXXX。

其中BnXXX是需要有头文件的。BnXXX只不过是把IXXX接口加入到Binder架构中来,而不参与实际的getXXX和setXXX应用层逻辑。

这个BnXXX定义可以和上面的IXXX定义放在一块。分开也行。

class BnXXX: public BnInterface<IXXX>

{

public:

    virtual status_t    onTransact( uint32_t code,

                                    const Parcel& data,

                                    Parcel* reply,

                                    uint32_t flags = 0);

//由于IXXX是个纯虚类,而BnXXX只实现了onTransact函数,所以BnXXX依然是

一个纯虚类

};

有了DECLARE,那我们在某个CPP中IMPLEMNT它吧。那就在IXXX.cpp中吧。

IMPLEMENT_meta_INTERFACE(XXX, "android.xxx.IXXX");//IMPLEMENT宏



status_t BnXXX::onTransact(

    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

    switch(code) {

        case GET_XXX: {

            CHECK_INTERFACE(IXXX, data, reply);

           读请求参数

           调用虚函数getXXX()

            return NO_ERROR;

        } break; //SET_XXX类似

BpXXX也在这里实现吧。

class BpXXX: public BpInterface<IXXX>

{

public:

    BpXXX (const sp<IBinder>& impl)

        : BpInterface< IXXX >(impl)

    {

}

vitural getXXX()

{

  Parcel data, reply;

  data.writeInterfaceToken(IXXX::getInterfaceDescriptor());

   data.writeInt32(pid);

   remote()->transact(GET_XXX, data, &reply);

   return;

}

/tXXX类似

至此,Binder就算分析完了,大家看完后,应该能做到以下几点:

l         如果需要写自己的Service的话,总得知道系统是怎么个调用你的函数,恩。对。有2个线程在那不停得从binder设备中收取命令,然后调用你的函数呢。恩,这是个多线程问题。

l         如果需要跟踪bug的话,得知道从Client端调用的函数,是怎么最终传到到远端的Service。这样,对于一些函数调用,Client端跟踪完了,我就知道转到Service去看对应函数调用了。反正是同步方式。也就是Client一个函数调用会一直等待到Service返回为止 
  • 打印
  • 关闭