Android FrameWork开发之binder驱动的源码分析1

Android源码分析 - Framework层的Binder(客户端篇) 本篇以分支作为基础解析我们在之前的文章中,从驱动层面分析了是怎样工作的,但驱动只涉及传输部分,待传输对象是怎么产生的呢,这就是层的工作了。我们要彻底了解的工作原理,不仅要去看驱动层,还得去看层以及应用层()我们还是以第一次见到的地方开始分析,我们选取方法来分析(这个方法既有入参也有返回),抛除掉它缓存和的部分,最核心的代码就一句 BinderInternal.getContextObject 我们从开始看起,这个函数是一个函数,他被实现在中 ProcessState 我们在这里可以发现一个比较关键的类,它是 阅读详情
AI摘要

本文详细剖析了Android中ServiceManager与Binder驱动之间的通信流程,从获取远程代理、addService操作到驱动数据传输,一步步揭示了核心步骤和关键技术细节。

csdn在线学习课程,课程咨询答疑和新课信息:QQ交流群:422901085进行课程讨论

[android跨进程通信实战视频课程(加群获取优惠)](https://edu.youkuaiyun.com/course/detail/35911)

上一节已经讲过ServiceManager其实也是属于一个普通的应用程序,它也需要与binder驱动进行通信,他在跨进程通信中CS模式中扮演的

Server端,普通进程需要添加获取Service就是Client端,如下图:

具体过程如下:

 上节课把与驱动通信这一部分完全当作一个黑盒子,本节讲带大家源码层面进行分析,当然因为代码实在太多,这里

主要就把几个主要流程梳理。

1、首先第一步进程要与ServiceManager进程通信,肯定要 获取它的远程代理对象

代码如下:

int main() {
  sp <IServiceManager> sm = defaultServiceManager();
  ..省略
  return 0;
}

看看defaultServiceManager方法,路径:frameworks/native/libs/binder/IServiceManager.cpp

sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;

    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));
            if (gDefaultServiceManager == NULL)
                sleep(1);
        }
    }

    return gDefaultServiceManager;
}

这里其实调用 ProcessState::self()->getContextObject(NULL)),到ProcessState看看,路径:frameworks/native/libs/binder/ProcessState.cpp

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    return getStrongProxyForHandle(0);
}

这里又调用的 getStrongProxyForHandle(0),注意这个0非常非常关键,代表了servicemanager的handle

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;
..省略

    if (e != NULL) {
       
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
              
..省略
                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
//这里主要看看servicemanager是否可以正常通信
                if (status == DEAD_OBJECT)
                   return NULL;
            }

            b = new BpBinder(handle); //直接就可以创建对应的BpBinder对象
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } 
..省略
    }

    return result;
}

这里大家可以看出其实它最后就是new BpBinder(0),这个就成了最后的ServiceManager的本地代理对象IBinder,ps:因为系统默认servicemanager的handle固定就是0,所以本地代理创建就非常非常简单

获取了BpBinder后,在经过interface_cast<IServiceManager>的转换成对应的具体IServiceManager接口,这里其实java中IBinder对象转具体口对象的的asInterface一样,具体这里不做分析,和java上的基本类似。就是把BpBinder作为IServiceManager的一个成员变量remote()。

2、再来看看A进程已经获取了ServiceManager的远程对象,使用简单的一次addService进行通信,先来看下面图:

其实主要分为以下4个步骤:

1、A进程调用ServiceManagre代理的addService方法(方法中携带了了自己本地定义业务IBinder,及这个业务IBinder的名字),经过一系列调用会调用到IPCThreadState::transact

status_t ret = sm->addService(String16(SAMPLE_SERIVCE_DES), samServ);

这里调用IServiceManager的addService

  virtual status_t addService(const String16& name, const sp<IBinder>& service,
            bool allowIsolated)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }
这里组装Parcel等数据然后调用remote()的transact方法,这里前面已经提过其实remote()返回就是BpBinder对象故其实调用到了BpBinder的transact
status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}
所以就到了IPCThreadState的transact
status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    ..省略
    if (err == NO_ERROR) {//把handle和code等一系列数据需要打包成传输数据,但cmd这里固定BC_TRANSACTION
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }

    if ((flags & TF_ONE_WAY) == 0) {
         ..省略
    } else {
        err = waitForResponse(NULL, NULL);
    }

    return err;
}

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr;

    tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
    tr.target.handle = handle;
    tr.code = code;
    tr.flags = binderFlags;
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;

    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    } else if (statusBuffer) {
        tr.flags |= TF_STATUS_CODE;
        *statusBuffer = err;
        tr.data_size = sizeof(status_t);
        tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
        tr.offsets_size = 0;
        tr.data.ptr.offsets = 0;
    } else {
        return (mLastError = err);
    }

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

    return NO_ERROR;
}

首先writeTransactionData把handle和code等一系列数据需要打包成传输数据,但cmd这里固定BC_TRANSACTION,其次调用waitForResponse
 

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;

    while (1) {//注意这里是一个循环,而不是单独的一次哦
        if ((err=talkWithDriver()) < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;//写完之后就要一直循环的读取数据,直到读取到BR_REPLY
        cmd = (uint32_t)mIn.readInt32();
..省略
        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;

        
        case BR_REPLY:
            {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
             

                if (reply) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        reply->ipcSetDataReference(
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t),
                            freeBuffer, this);
                    } else {
                        err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
                        freeBuffer(NULL,
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t), this);
                    }
                } else {
       
                }
            }
            goto finish;

        default:
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }
    }

finish:
    if (err != NO_ERROR) {
        if (acquireResult) *acquireResult = err;
        if (reply) reply->setError(err);
        mLastError = err;
    }

    return err;
}

这里其实又是调用的talkwithDriver,就是和binder驱动进行通信,但它不是单独一次talkwithDriver哦,写完之后就会一直talkwithDriver读取返回结果
 

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
  
    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();

 
    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
     
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;


    } while (err == -EINTR);

 
        return NO_ERROR;
    }

    return err;
}

这里其实又到了一个与驱动交互的核心方法ioctl,一旦调用到了ioctl,那接下来就需要分析binder的驱动部分代码的ioctl


2、binder驱动接受到了A进程的ioctl,并做出把数据拷贝到ServiceManager进程并传递给ServiceManager进行处理

binder驱动的ioctl对应方法为binder_ioctl

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	..省略
	switch (cmd) {
	case BINDER_WRITE_READ:
		ret = binder_ioctl_write_read(filp, cmd, arg, thread);
		if (ret)
			goto err;
		break;
	..省略
	
	default:
		ret = -EINVAL;
		goto err;
	}
	ret = 0;
    ..省略
	return ret;
}

这里刚才应用传递cmd为BINDER_WRITE_READ,接下来调用的是ret = binder_ioctl_write_read(filp, cmd, arg, thread);

static int binder_ioctl_write_read(struct file *filp,
				unsigned int cmd, unsigned long arg,
				struct binder_thread *thread)
{
	int ret = 0;
	struct binder_proc *proc = filp->private_data;
	unsigned int size = _IOC_SIZE(cmd);
	void __user *ubuf = (void __user *)arg;
	struct binder_write_read bwr;

	if (size != sizeof(struct binder_write_read)) {
		ret = -EINVAL;
		goto out;
	}
	if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {//拷贝用户空间数据到内核
		ret = -EFAULT;
		goto out;
	}
	。。省略

	if (bwr.write_size > 0) {//调用binder_thread_write
		ret = binder_thread_write(proc, thread,
					  bwr.write_buffer,
					  bwr.write_size,
					  &bwr.write_consumed);
		trace_binder_write_done(ret);
		if (ret < 0) {
			bwr.read_consumed = 0;
			if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
				ret = -EFAULT;
			goto out;
		}
	}
	if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
		ret = -EFAULT;
		goto out;
	}
out:
	return ret;
}

这里简单把应用传递的一个结构体拷贝到内核,然后调用binder_thread_write

static int binder_thread_write(struct binder_proc *proc,
			struct binder_thread *thread,
			binder_uintptr_t binder_buffer, size_t size,
			binder_size_t *consumed)
{
	uint32_t cmd;
	struct binder_context *context = proc->context;
	void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
	void __user *ptr = buffer + *consumed;
	void __user *end = buffer + size;

	while (ptr < end && thread->return_error.cmd == BR_OK) {
		int ret;

		if (get_user(cmd, (uint32_t __user *)ptr))
			return -EFAULT;
		。。省略
		switch (cmd) {
		。。省略
		case BC_TRANSACTION:
		case BC_REPLY: {
			struct binder_transaction_data tr;

			if (copy_from_user(&tr, ptr, sizeof(tr)))
				return -EFAULT;
			ptr += sizeof(tr);
			binder_transaction(proc, thread, &tr,
					   cmd == BC_REPLY, 0);
			break;
		}
                。。省略
		}
		*consumed = ptr - buffer;
	}
	return 0;
}

这里最后调用到了binder_transaction,代码过长省略大部分。。

static void binder_transaction(struct binder_proc *proc,
			       struct binder_thread *thread,
			       struct binder_transaction_data *tr, int reply,
			       binder_size_t extra_buffers_size)
{
	int ret;
	struct binder_transaction *t;
	struct binder_work *tcomplete;
	binder_size_t *offp, *off_end, *off_start;
	binder_size_t off_min;
	u8 *sg_bufp, *sg_buf_end;
	struct binder_proc *target_proc = NULL;
	struct binder_thread *target_thread = NULL;
	struct binder_node *target_node = NULL;
	struct binder_transaction *in_reply_to = NULL;
	struct binder_transaction_log_entry *e;
	。。省略
	e = binder_transaction_log_add(&binder_transaction_log);
	e->debug_id = t_debug_id;
	e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY);
	e->from_proc = proc->pid;
	e->from_thread = thread->pid;
	e->target_handle = tr->target.handle;
	e->data_size = tr->data_size;
	e->offsets_size = tr->offsets_size;
	e->context_name = proc->context->name;

	if (reply) {
		。。省略
	} else {
		if (tr->target.handle) {
			。。省略
		} else {
    
			target_node = context->binder_context_mgr_node;
			if (target_node)
				target_node = binder_get_node_refs_for_txn(
						target_node, &target_proc,
						&return_error);
			。。省略
		}
		。。省略
		if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
		。。省略
		}

	}
	
	。。省略


	t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
		tr->offsets_size, extra_buffers_size,
		!reply && (t->flags & TF_ONE_WAY));
	。。省略
        //用户空间拷贝出对应数据,这个后面再进行详细分析
	if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
			   tr->data.ptr.buffer, tr->data_size)) {
		。。省略
	}
	if (copy_from_user(offp, (const void __user *)(uintptr_t)
			   tr->data.ptr.offsets, tr->offsets_size)) {
		。。省略
	}
。。省略
	for (; offp < off_end; offp++) {
		struct binder_object_header *hdr;
		size_t object_size = binder_validate_object(t->buffer, *offp);
。。省略

		hdr = (struct binder_object_header *)(t->buffer->data + *offp);
		off_min = *offp + object_size;
		switch (hdr->type) {
		。。省略
		case BINDER_TYPE_HANDLE:
		case BINDER_TYPE_WEAK_HANDLE: {
			struct flat_binder_object *fp;

			fp = to_flat_binder_object(hdr);
			ret = binder_translate_handle(fp, t, thread);
			。。省略
		} break;
		。。省略
	}
	tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
	t->work.type = BINDER_WORK_TRANSACTION;

	if (reply) {
。。省略
	} else if (!(t->flags & TF_ONE_WAY)) {
		BUG_ON(t->buffer->async_transaction != 0);
		binder_inner_proc_lock(proc);
		/*
		 * Defer the TRANSACTION_COMPLETE, so we don't return to
		 * userspace immediately; this allows the target process to
		 * immediately start processing this transaction, reducing
		 * latency. We will then return the TRANSACTION_COMPLETE when
		 * the target replies (or there is an error).
		 */
		binder_enqueue_deferred_thread_work_ilocked(thread, tcomplete);
		t->need_reply = 1;
		t->from_parent = thread->transaction_stack;
		thread->transaction_stack = t;
		binder_inner_proc_unlock(proc);
               //这里发起唤醒服务进程等待队列
		if (!binder_proc_transaction(t, target_proc, target_thread)) {
			binder_inner_proc_lock(proc);
			binder_pop_transaction_ilocked(thread, t);
			binder_inner_proc_unlock(proc);
			goto err_dead_proc_or_thread;
		}
	} else {
		。。省略
	}
	。。省略
	return;

。。省略
}

代码实在太多,这里只说说最核心的,最后一切数据准备好了,也找到了目标进程了,会binder_proc_transaction

static bool binder_proc_transaction(struct binder_transaction *t,
				    struct binder_proc *proc,
				    struct binder_thread *thread)
{
	struct binder_node *node = t->buffer->target_node;
	..省略
	if (!thread && !pending_async)//对方的进程中寻找到一个线程进行传输
		thread = binder_select_thread_ilocked(proc);

	if (thread) {
		binder_transaction_priority(thread->task, t, node_prio,
					    node->inherit_rt);
		binder_enqueue_thread_work_ilocked(thread, &t->work);//把对应任务放入线程执行队列
	} else if (!pending_async) {
		binder_enqueue_work_ilocked(&t->work, &proc->todo);
	} else {
		binder_enqueue_work_ilocked(&t->work, &node->async_todo);
	}

	if (!pending_async)//如果同步调用,则唤醒目标线程
		binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */);

	
	return true;
}

这里的目标线程当然就是servicemanager的loop的那个主线程,上节课已经知道他是一直ioctl方式在读取驱动数据,所以它的进程应该执行的是

static int binder_thread_read(struct binder_proc *proc,
			      struct binder_thread *thread,
			      binder_uintptr_t binder_buffer, size_t size,
			      binder_size_t *consumed, int non_block)
{
	。。省略

	if (non_block) {
		。。省略
	} else {
        //等待有任务
		ret = binder_wait_for_work(thread, wait_for_proc_work);
	}

	。。省略
	while (1) {
		uint32_t cmd;
		。。省略
        //取出任务
		w = binder_dequeue_work_head_ilocked(list);
		if (binder_worklist_empty_ilocked(&thread->todo))
			thread->process_todo = false;

		switch (w->type) {
		case BINDER_WORK_TRANSACTION: {
			binder_inner_proc_unlock(proc);
			t = container_of(w, struct binder_transaction, work);
		} break;
		
	。。省略
		BUG_ON(t->buffer == NULL);
		if (t->buffer->target_node) {
			struct binder_node *target_node = t->buffer->target_node;
			struct binder_priority node_prio;

			tr.target.ptr = target_node->ptr;
			tr.cookie =  target_node->cookie;
			node_prio.sched_policy = target_node->sched_policy;
			node_prio.prio = target_node->min_priority;
			binder_transaction_priority(current, t, node_prio,
						    target_node->inherit_rt);
			cmd = BR_TRANSACTION;
		} else {
			。。省略
		}
		tr.code = t->code;
		tr.flags = t->flags;
		tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);
        。。省略
		tr.data_size = t->buffer->data_size;
		tr.offsets_size = t->buffer->offsets_size;
		tr.data.ptr.buffer = (binder_uintptr_t)
			((uintptr_t)t->buffer->data +
			binder_alloc_get_user_buffer_offset(&proc->alloc));
		tr.data.ptr.offsets = tr.data.ptr.buffer +
					ALIGN(t->buffer->data_size,
					    sizeof(void *));
        //拷贝cmd到用户空间
		if (put_user(cmd, (uint32_t __user *)ptr)) {
		..省略
		}
		ptr += sizeof(uint32_t);
        //拷贝真实实体数据
		if (copy_to_user(ptr, &tr, sizeof(tr))) {
	        ..省略
		}
		ptr += sizeof(tr);
        
		binder_stat_br(proc, thread, cmd);
		。。省略
		
		if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
			binder_inner_proc_lock(thread->proc);
			t->to_parent = thread->transaction_stack;
			t->to_thread = thread;
			thread->transaction_stack = t;
			binder_inner_proc_unlock(thread->proc);
		} 

。。省略
	return 0;
}

大家可以注释大概可以看出binder驱动read数据是怎么一个过程,

1.等待挂起直到有任务唤醒

2.唤醒后取出任务及拼好数据

3.对携带数据拷贝写回到用户空间,这样一个read的ioctl就执行完毕

3、ServiceManager已经读取了数据,进行addService完成,返回数据给驱动,让驱动告知A进程结果

3.1、读取数据进行addService部分 代码路径:frameworks/native/cmds/servicemanager/binder.c(注意和驱动的binder.c路径不一样哦)

void binder_loop(struct binder_state *bs, binder_handler func)
{
   。。省略

    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

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

     。。省略
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
         。。省略
    }
}

主要循环读取数据其实主要是binder_parse方法:

int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
    。。省略
        switch(cmd) {
          。。省略
        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) {
                  。。省略
                res = func(bs, txn, &msg, &reply);//其实就是执行svcmgr_handler,里面会进行对应的do_add_service,这里上节课已经分析就不予分析了
                if (txn->flags & TF_ONE_WAY) {
                      。。省略
                } else {
                    binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
//add完成后就会发一个回复给驱动
                }
            }
            ptr += sizeof(*txn);
            break;
        }
            。。省略
        }
    }

    return r;
}

这里大家可以看会执行svcmgr_handler,里面会进行do_add_service,执行完成后调用一个 binder_send_reply(bs, &reply, txn->data.ptr.buffer, res)方法

void binder_send_reply(struct binder_state *bs,
                       struct binder_io *reply,
                       binder_uintptr_t buffer_to_free,
                       int status)
{
 。。省略
    data.cmd_free = BC_FREE_BUFFER;
    data.buffer = buffer_to_free;
    data.cmd_reply = BC_REPLY;//关键地方设置了BC_REPLY
 。。省略
    binder_write(bs, &data, sizeof(data));//进行binder数据写入binder驱动
}

 这里设置了一个cmd为BC_REPLY,然后把它写入binder驱动,写入方法是binder_write。实际他就是调用ioctl方法,那么其实接下来流程就又回到了binder驱动

int binder_write(struct binder_state *bs, void *data, size_t len)
{
    ..省略
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//最后还是调用ioctl方法与binder驱动进行通信
    ..省略
    return res;
}

4、A进程接受到返回结果,这个addService过程成功结束

这里因为已经看到了servicemanager调用到了ioctl来写入BC_REPLY数据,这个写入过程就又是一个前面分析的binder_thread_write,实际又会调用到binder_transaction(proc, thread, &tr,cmd == BC_REPLY, 0);不过这次的cmd为BC_REPLY。这里目标进程就变成A进程,而且A进程前面分析的在waitForResponse我们也说过,在ioctl写入数据完成后,A进程的调用并没有结束,而是一直不断循环获取返回结果,知道返回BR_REPLY等它才会退出循环

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

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
。。省略
        if (mIn.dataAvail() == 0) continue;
//在talkWithDriver写入数据完成后,A进程的调用并没有结束,而是一直不断循环获取返回结果,知道返回BR_REPLY等它才会退出循环
。。省略
        switch (cmd) {
      

        case BR_REPLY:
          
            goto finish;
    }
。。省略
}

,

Framk PHP轻量级WEB框架 Framk是一个完全可以个性化、运行轻畅的PHP框架,他摒弃目前框架过于臃肿、规则多、学习时间长的缺点,用户完全无需手册,只要根据示例就可以进行网站开发,同时也可以根据自我需要对框架进行个性化扩展或二次开发,因为Framk框架核心代码非常易读且嵌套少,赶快下载示例试用吧! 立即下载

相关推荐

FramkPHP运行轻畅开源框架v1.7.131110

FramkPHP是一款运行轻畅、架构清晰的PHP框架,框架以“快速易用”为理念给php开发者提供网站建设的最佳实践,框架核心代码非常易读且嵌套少易于进行个性化扩展。 FramkPHP 快速易用PHP框架 v1.7.131110 更新日志: 1.将page、与提示信息模板整合到tpl_default文件夹下,便于用户自定修改模板。 2.将内置函数缩减至5个,本站将陆续发表一些实用函数给用户,用户可根

Android系统framework、驱动、内核

2022年岗位招聘第1期

哆啦安全 5649

FramkPHP 1.7.131110.rar

FramkPHP是一款运行轻畅、架构清晰的PHP框架,核心代码仅30多KB,框架以“快速易用”为理念摒弃臃繁代码,给php开发者提供网站建设的最佳实践,框架核心代码非常易读且嵌套少,代码重用性强,易于进行个性化扩展。 FramkPHP 1.7.131110 更新日志: 1.将page、与提示信息模板整合到tpl_default文件夹下,便于用户自定修改模板。 2.将内置函数缩减至5个,本站将陆续发表一些实用函数给用户,用户可根据开发需要自己写或选择。 3.将以前版本的数据操作方法优化,操作方法见DEMO。 主要特点: 真正遵循框架原思想,去除繁缛留其核心,核心代码嵌套少,简单易懂。 配置少、规则少、学习时间短。 类方法通用性强,用户只需修改少许代码就可应用,类之间方法调用方便。 快速达到数据的更新查询目的,数据缓存遵循“数据更新,缓存更新”原则。

【C#】简单认识TransactionScope,以及常见的事务类型

这是小5写的博客之星城市领跑,第一篇文章。简单介绍C#常见的事务。

小5聊的博客 5242

科研党必备:Python脚本批量下载DOI文献的保姆级教程(附避坑指南)

本文为科研人员提供了一份详细的Python脚本批量下载DOI文献的教程,涵盖环境配置、核心功能实现、性能优化及常见问题解决。通过自动化脚本,研究者可高效完成文献批量下载,显著提升科研工作效率,特别适合需要处理大量文献的研究团队使用。

weixin_26965807的博客 477

Multisim14.3中FPGA接口电路设计:原理图项目应用

深入讲解在multisim14.3环境下构建FPGA接口电路的方法,结合原理图设计与项目实践,展现multisim14.3强大的电路仿真能力,提升数字系统开发效率。

weixin_42578963的博客 1157

Binder 线程池溢出问题

第一行是 proc 2890 表示进程的 pid,第二行表示 binder 类型,接下来就是 buffer,用于表示内核中的 binder_buffer,如果这个 buffer 在程序运行过程中越来越多,那么就有内存泄漏的可能存在,不过这种情况很少,应用程序都是通过 libbinder 这个库来使用 binder 驱动的,在一次 ipc 调用结束后,库本生会执行 binder_buffer 的清理操作,这种系统中的公用库一般都非常稳定,出 bug 的几率很小。一个关于binder的debug技巧。

chuyouyinghe的专栏 1241

LabVIEW多界面应用开发:从启动器到主界面的切换架构与实现

在图形化编程领域,模块化与界面管理是构建复杂应用程序的核心。其原理在于通过清晰的程序架构,实现不同功能模块间的独立运行与协同工作,从而提升代码的可维护性和可扩展性。这种架构的技术价值在于能够有效管理应用程序的生命周期与资源,避免内存泄漏,并确保流畅的用户体验。在测控系统、自动化测试等工程实践中,常需实现如启动界面选择语言或模式后,无缝切换至主功能界面的场景。本文聚焦于LabVIEW环境,深入探讨如何利用动态调用、状态机等机制,并结合功能全局变量(FGV)与队列消息处理器(QMH)等热词技术,构建健壮的多界面

weixin_30802819的博客 263

关于AIDL接口定义中oneway的修饰符源码解析.

以下源码出自于Android 8.1 该部分源码基于我另外一篇文章,Binder通信流程浅析有提到,在binder进行传输数据时有提到这个方法binder_proc_transaction,该方法是把当前client要发送的数据t加入到队列中,并唤起binder线程执行该事务,当时忽略了一个细节oneway修饰符: static bool binder_proc_transaction(struct binder_transaction *t, struct binder_proc *pro

Only_Studio的博客 1757

(Touch™)搭建 Windows 开发环境

> OpenHaptics 开发包参考文档。

置心一处,便无事不办 1500

从Bit到MCS:深度解析Xilinx FPGA程序固化流程与文件选型

本文深度解析Xilinx FPGA程序从调试到量产的固化流程,重点对比BIT、BIN、MCS和ELF四种文件格式的特性与适用场景。针对工业级应用,详细讲解MCS文件的生成优化、Flash烧录避坑指南及三级验证流程,并分享多镜像管理、加密安全等高级技巧,帮助开发者提升FPGA量产可靠性。

weixin_30924239的博客 324

Android Binder框架实现之Framework层Binder服务注册过程源码分析

  Android Binder框架实现之framework层服务注册过程源码分析

IT先森 4135

拼团系统设计-人群标签的设计与思考

最近在学习一个拼团项目中涉及到的人群模块的设计,写下一些实现与思考。

wywcool的博客 971

Android FrameWork ---- Binder驱动源码分析

Binder机制以及Binder驱动源码分析

qq_33235287的博客 2513

手把手教你用Docker快速部署ZLMediaKit流媒体服务器(含WebRTC配置)

本文详细介绍了如何使用Docker快速部署ZLMediaKit流媒体服务器,并配置WebRTC功能。通过容器化部署,实现环境一致性、资源隔离和快速扩展,同时提供WebRTC专项配置、多协议推拉流实战测试及生产环境优化建议,帮助开发者高效搭建企业级流媒体平台。

agile9scrum的博客 202

R语言层次聚类(hierarchical clustering):数据缩放、PCA聚类结果可视化、fpc包的clusterboot函数通过bootstrap重采样的方法评估hclust层次聚类的稳定性

R语言层次聚类(hierarchical clustering):数据缩放、PCA聚类结果可视化、fpc包的clusterboot函数通过bootstrap重采样的方法评估hclust层次聚类的稳定性

data+scenario+science+insight 835

Android Framework开发应该如何入门?写给程序员Android Framework 开发,这一篇就够了

前言 在 Android 开发者技能中,如果想进大厂,一般拥有较好的学历可能有优势一些。但是如果你靠硬实力也是有机会的,例如死磕Framework。Framework 知识广泛应用在Android各个领域中,重要性显而易见。 成为一名Android Framework高手,也是目前招聘过程中非常稀缺的人才,可以成为你的敲门砖。 Framework就像一棵常青的技能树,不管是普通开发还是首席架构、CTO 都能够从中汲取足够的技术养料。普通开发往往对Framework只停留在“使用过”甚至是“听说过”的阶段,这

xiaokangss的博客 1万+

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Android Binder 驱动 - Media 服务的添加过程

相关文章链接: 1. Android FrameWork - 学习启动篇 2. Android FrameWork - 开机启动 Init 进程 3. Android 开发者需要知道的 Linux 知识 4. 从 Linux 内核的角度来看 Binder 驱动 5. JNI 基础 - Android 共享内存的序列化过程 6. Android进程间通信(IPC)机制Binder简要介绍和学习计划 ...

z240336124的专栏 2309

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Android Framework开发如何入门?Framework到底该如何学习?看完这你就知道了

作为一名合格的 Android 开发,需要学习 Framework 知识,来解决 App 稳定性相关的问题Framework 的源码学习一般由 init.rc 开始看起,因为它是一个 Android 系统启动必备的重要脚本,之后的几大系统进程都是由它启动的,比如 zygote,systemserver 等,这里主要记录一些基本概念,以及 Zygote 启动的源码分析。

老皮的博客 2445

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Android Framework 开发解密,移动开发者必须掌握的底层原理_《android framework 开发揭秘

总之,一旦遇到问题,很少人能够由点及面逆向分析,最终找到瓶颈点和最优解决方案,。但Framework 系统框架底层原理是Android开发的基础,体量太大,不是一时半会能精通的。在这里给大家推荐一份由,全文共包含109个知识点,通过经典Binder、Handler、AMS等加深你对Android Framework框架层的理解,助你轻松上手Framework!(需要完整版的可扫描下方二维码领取)**腾讯技术团队出品《Android Framework开发揭秘》**完整文档的可点击直接领取。

2401_89190823的博客 2064

Android10.0 Binder通信原理(八)-Framework层分析

摘要:本节主要来讲解Android10.0 Binder 在Framework的使用分析 阅读本文大约需要花费15分钟。 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢! [Android取经之路] 的源码都基于Android-Q(10.0) 进行分析 [Android取经之路] 系列文章: 《系统启动篇》 A...

IngresGe 的专栏 8231

对于Android开发了解Android的Framework 层有多重要,Android Framework精编内核解析。

前言 了解Android的Framework 层对工作能起到什么帮助?Framework 的知识对于Android开发有多重要? 如果你去 Github 上看过腾讯 Matrix 的源码,就知道 Framework 的知识有多重要了。 像掉帧监控,函数插桩,慢函数检测,ANR 监控,启动监控,都需要对 Framework 有比较深入的了解,才能知道怎么去做监控,利用什么机制去监控,函数插桩插到哪里,反射调用该反射哪个类哪个方法哪个属性…… 另外 Framework 作为 Android 框架层,为 App

datian1234的博客 2834

Android Framework实战开发-binder通信java及jni部分源码分析

1、首先来看一下简单的一个跨进程通信的流程图 2、第一部分进程与ServiceManager通信的又上到下 平时大部分获取ServiceManager中的某一个服务,一般都是最后都是ServiceManager.getService这种方式: //ServiceManager.java中 public static IBinder getService(String name) { try { IBinder service = sCache.get(nam

learnframework的博客 1943
上一篇: Android Framework实战课程-binder专题之ServiceManager启动及运行篇
下一篇: Android Framework实战开发-Binder专题讲解之aidl文件的详细分析
千里马学框架
博客等级 码龄6年 1万+粉丝 650原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值