内核基础概念理解
-
为什么在内核状态下进程是互通的。 因为在内核眼里 进程都是我管理的,对于内核来说 ,所有的东西都他控制的。内核就是无敌的。进程对于内核来说就像个变量一样。所以想怎么搞就怎么搞。操作系统其实就是个程序。
-
用户空间想访问内核空间咋办?
通过系统调用或者硬件中断,一般都是系统调用。
-
binder 驱动涉及到系统的调用有哪些?
ioctl和mmap -
常用方法的在
Binder.c对应
open-->binder_open ioctl-->binder_ioctl mmap-->binder_mmap
方法源码分析
binder_open
//android/binder.c
static int binder_open(struct inode *nodp, struct file *filp)
{
//构建 binder_proc 进程
struct binder_proc *proc;
struct binder_device *binder_dev;
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (proc == NULL)
return -ENOMEM;
spin_lock_init(&proc->inner_lock);
spin_lock_init(&proc->outer_lock);
get_task_struct(current->group_leader);
proc->tsk = current->group_leader;
mutex_init(&proc->files_lock);
INIT_LIST_HEAD(&proc->todo);
if (binder_supported_policy(current->policy)) {
proc->default_priority.sched_policy = current->policy;
proc->default_priority.prio = current->normal_prio;
} else {
proc->default_priority.sched_policy = SCHED_NORMAL;
proc->default_priority.prio = NICE_TO_PRIO(0);
}
binder_dev = container_of(filp->private_data, struct binder_device,
miscdev);
proc->context = &binder_dev->context;
//核心代码
binder_alloc_init(&proc->alloc);
binder_stats_created(BINDER_STAT_PROC);
//核心代码应用层的pid 也就是当前谁调用的binder 的应用的pid
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
INIT_LIST_HEAD(&proc->waiting_threads);
//前面构建好的proc 赋值给了filpd的属性
filp->private_data = proc;
mutex_lock(&binder_procs_lock);
//加入进程的列表
hlist_add_head(&proc->proc_node, &binder_procs);
mutex_unlock(&binder_procs_lock);
if (binder_debugfs_dir_entry_proc) {
char strbuf[11];
snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
binder_debugfs_dir_entry_proc,
(void *)(unsigned long)proc->pid,
&binder_proc_fops);
}
return 0;
}
//总结 搞了个 进程对象 然后赋值过去了
mmap
/** vm_area_struct 用于表示0~3G的空间中一段连续的虚拟地址空间,是给user space的process使用.
vm_struct 是kernel space 除low memory中用于表示连续的虚拟地址空间,常用于vmalloc/vfree的操作
简单理解为vm_area_struct 就是用户空间的虚拟内存描述结构体,vm_struct 是kernel中的虚拟内存描述结构体。
所以这里大参数是用户空间传递来的虚拟内存结构体。
这里binder_mmap除了判断限制了申请内存最大为4M,其他真正业务都是在binder_alloc_mmap_handler方法中,同时把proc的binder_alloc传递类进去,及用户空间的vm_area_struct 也传递了进去
*/
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
//前面 open 方法里 给filp->private_data = proc 这里就可以拿出来了
int ret;
struct binder_proc *proc = filp->private_data;
const char *failure_string;
if (proc->tsk != current->group_leader)
return -EINVAL;
//检查 用户空间虚拟地址end -start 是否大于4m
if ((vma->vm_end - vma->vm_start) > SZ_4M)
//如果大于4m 就限制到4m 一般情况是1M-8k
vma->vm_end = vma->vm_start + SZ_4M;
if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
ret = -EPERM;
failure_string = "bad vm_flags";
goto err_bad_arg;
}
vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
//设置了一个回调方法 没啥用
vma->vm_ops = &binder_vm_ops;
// 又赋值给了 wma的属性
vma->vm_private_data = proc;
//一个关键方法
ret = binder_alloc_mmap_handler(&proc->alloc, vma);
if (ret)
return ret;
mutex_lock(&proc->files_lock);
proc->files = get_files_struct(current);
mutex_unlock(&proc->files_lock);
return 0;
err_bad_arg:
pr_err("binder_mmap: %d %lx-%lx %s failed %d\n",
proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
return ret;
}
//
总结 限制到4m 以内, 然后 赋值给wma 调用了 binder_alloc_mmap_handler
binder_alloc_mmap_handler
//android/binder_alloc.c
int binder_alloc_mmap_handler(struct binder_alloc *alloc,
struct vm_area_struct *vma)
{
int ret;
//内核中虚拟地址
struct vm_struct *area;
const char *failure_string;
struct binder_buffer *buffer;
mutex_lock(&binder_alloc_mmap_lock);
if (alloc->buffer) {
ret = -EBUSY;
failure_string = "already mapped";
goto err_already_mapped;
}
//构造内核虚拟地址 传入的大小 是用户空间内核的大小长度
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
if (area == NULL) {
ret = -ENOMEM;
failure_string = "get_vm_area";
goto err_get_vm_area_failed;
}
//将用户allco 中的buffer 赋值为申请出来的空间地址
alloc->buffer = area->addr;
//用户空间和内核空间的偏移量 计算出来
alloc->user_buffer_offset =
vma->vm_start - (uintptr_t)alloc->buffer;
mutex_unlock(&binder_alloc_mmap_lock);
#ifdef CONFIG_CPU_CACHE_VIPT
if (cache_is_vipt_aliasing()) {
while (CACHE_COLOUR(
(vma->vm_start ^ (uint32_t)alloc->buffer))) {
pr_info("binder_mmap: %d %lx-%lx maps %pK bad alignment\n",
alloc->pid, vma->vm_start, vma->vm_end,
alloc->buffer);
vma->vm_start += PAGE_SIZE;
}
}
#endif
//申请页数 就是对象的长度除以一页的大小 一般4096 一页
alloc->pages = kzalloc(sizeof(alloc->pages[0]) *
((vma->vm_end - vma->vm_start) / PAGE_SIZE),
GFP_KERNEL);
if (alloc->pages == NULL) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
}
alloc->buffer_size = vma->vm_end - vma->vm_start;
//申请了一个buffer
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
if (!buffer) {
ret = -ENOMEM;
failure_string = "alloc buffer struct";
goto err_alloc_buf_struct_failed;
}
//装载内容 buffer的内容 指向alloc的buffer 这里的buffer 已经是内核里面的地址了
buffer->data = alloc->buffer;
list_add(&buffer->entry, &alloc->buffers);
buffer->free = 1;
binder_insert_free_buffer(alloc, buffer);
//异步空间的地址大小 为申请的buffer的一半
alloc->free_async_space = alloc->buffer_size / 2;
barrier();
//给用户空间属性 赋值回去
alloc->vma = vma;
alloc->vma_vm_mm = vma->vm_mm;
/* Same as mmgrab() in later kernel versions */
atomic_inc(&alloc->vma_vm_mm->mm_count);
return 0;
err_alloc_buf_struct_failed:
kfree(alloc->pages);
alloc->pages = NULL;
err_alloc_pages_failed:
mutex_lock(&binder_alloc_mmap_lock);
vfree(alloc->buffer);
alloc->buffer = NULL;
err_get_vm_area_failed:
err_already_mapped:
mutex_unlock(&binder_alloc_mmap_lock);
pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
alloc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
return ret;
}
总结 申请了内核空间, 计算出来用户和内核空间之间的偏移量, 方便后面寻找, 只需要一个差额的计算就能找到对方地址,申请了页出来, 构建了buffer ,这里可以看出来不是每个都是1m-8k ,而是看你需要多少再去申请对应的页, 真正申请是在 binder_update_page_range 方法里
binder_update_page_range 申请真实物理内存地址
//在这里 就相当于根据需要地址大小去申请对应的物理地址, 遍历了需要的页数 保证了申请成功,并把虚拟地址和真正的物理地址进行了映射。 然后根据之前算出来的偏差 把用户里面的虚拟地址和真实的物理地址也映射一起,这样用户和内核都映射到一块物理地址
static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
void *start, void *end)
{
void *page_addr;
unsigned long user_page_addr;
struct binder_lru_page *page;
struct vm_area_struct *vma = NULL;
struct mm_struct *mm = NULL;
bool need_mm = false;
。。省略
//根据start和end区域大小,计算申请对应的内存页
for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE];
//判断这个物理页是否已经申请,如果没有申请则need_mm = true;就从这一页开始申请分配物理内存
if (!page->page_ptr) {
need_mm = true;
break;
}
}
/* Same as mmget_not_zero() in later kernel versions */
if (need_mm && atomic_inc_not_zero(&alloc->vma_vm_mm->mm_users))
mm = alloc->vma_vm_mm;
if (mm) {
down_write(&mm->mmap_sem);
vma = alloc->vma;
}
//又一次遍历。。其实好像和上面的代码有点重复遍历,可以考虑是否可以优化这里减少遍历次数
for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
index = (page_addr - alloc->buffer) / PAGE_SIZE;
page = &alloc->pages[index];
//page->page_ptr不为null说明已经被使用,只能继续遍历
if (page->page_ptr) {
//lru优化。。。暂时不理会
on_lru = list_lru_del(&binder_alloc_lru, &page->lru);
continue;
}
//遍历到了没有被申请使用的,那就开始真正page内存页申请
page->page_ptr = alloc_page(GFP_KERNEL |
__GFP_HIGHMEM |
__GFP_ZERO);
。。省略
page->alloc = alloc;
//把内核虚拟地址page_addr与真实的页内存进行映射
ret = map_kernel_range_noflush((unsigned long)page_addr,
PAGE_SIZE, PAGE_KERNEL,
&page->page_ptr);
flush_cache_vmap((unsigned long)page_addr,
(unsigned long)page_addr + PAGE_SIZE);
//根据原来的内核和用户空间地址偏移,可以直接得出用户空间的虚拟地址
user_page_addr =
(uintptr_t)page_addr + alloc->user_buffer_offset;
// 把用户空间虚拟地址user_page_addr与真实的页内存进行映射
ret = vm_insert_page(vma, user_page_addr, page[0].page_ptr);
if (index + 1 > alloc->pages_high)
alloc->pages_high = index + 1;
}
//。。省略
}
//
传输数据的基本流程 数据的写入,aidl 实际调用的是BPBinder. 然后调用的IPCThreadState 里面的transact()
//native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err;
flags |= TF_ACCEPT_FDS;
//数据写入
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);
//一般不报错
if (err != NO_ERROR) {
if (reply) reply->setError(err);
return (mLastError = err);
}
if ((flags & TF_ONE_WAY) == 0) {
//省略。。
if (reply) {
//等待响应
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
#if 0
if (code == 4) { // relayout
ALOGI("<<<<<< RETURNING transaction 4");
} else {
ALOGI("<<<<<< RETURNING transaction %d", code);
}
#endif
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
<< handle << ": ";
if (reply) alog << indent << *reply << dedent << endl;
else alog << "(none requested)" << endl;
}
} else {
err = waitForResponse(nullptr, nullptr);
}
return err;
}
// transact 里面会调用 writeTransactionData
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;
tr.target.handle = handle;//很关键
tr.code = code; //就是aidl 里面掉的方法code 从0开始那个
tr.flags = binderFlags;
tr.cookie = 0;
tr.sender_pid = 0;
tr.sender_euid = 0;
const status_t err = data.errorCheck();
if (err == NO_ERROR) {
//从data 里面取出数据 那边也用这个结构体 里面存的是指针地址
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 是个 Parcel 相当于数据写进进去
mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
// binder 里面对buffer 只拷贝一次因为这个比较大 ,其他的结构体的其他属性 还是拷贝两次的
//等待结果
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
uint32_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;
cmd = (uint32_t)mIn.readInt32();
switch (cmd) {
case BR_ONEWAY_SPAM_SUSPECT:
CallStack::logStack("oneway spamming", CallStack::getCurrent().get(),
ANDROID_LOG_ERROR);
[[fallthrough]];
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
case BR_DEAD_REPLY:
err = DEAD_OBJECT;
goto finish;
case BR_FAILED_REPLY:
err = FAILED_TRANSACTION;
goto finish;
case BR_FROZEN_REPLY:
err = FAILED_TRANSACTION;
goto finish;
case BR_ACQUIRE_RESULT:
{
const int32_t result = mIn.readInt32();
if (!acquireResult) continue;
*acquireResult = result ? NO_ERROR : INVALID_OPERATION;
}
goto finish;
case BR_REPLY:
{
binder_transaction_data tr;
err = mIn.read(&tr, sizeof(tr));
if (err != NO_ERROR) goto finish;
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);
} else {
err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
freeBuffer(nullptr,
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));
}
} else {
freeBuffer(nullptr,
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));
continue;
}
}
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;
}
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
if (mProcess->mDriverFD < 0) {
return -EBADF;
}
//读写结构体
binder_write_read bwr;
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
//获取传入的大小
bwr.write_size = outAvail;
//从out里面把数据取出来
bwr.write_buffer = (uintptr_t)mOut.data();
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
//先走write 所以read 就是0
bwr.read_size = 0;
bwr.read_buffer = 0;
}
//无读无写就错误了
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
#if defined(__ANDROID__)
// bwr 组装好 调用 ioctl 将组好的 结构体 传入进去了
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
#else
err = INVALID_OPERATION;
#endif
if (mProcess->mDriverFD < 0) {
err = -EBADF;
}
} while (err == -EINTR);
if (err >= NO_ERROR) {
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < mOut.dataSize())
else {
mOut.setDataSize(0);
processPostWriteDerefs();
}
}
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
return NO_ERROR;
}
return err;
}
//总结 弄了个结构体 给 ioctl了 会调用 binder_ioctl
binder_ioctl
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
//这里cmd 是上面传的 BINDER_WRITE_READ
int ret;
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
binder_selftest_alloc(&proc->alloc);
trace_binder_ioctl(cmd, arg);
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
goto err_unlocked;
//进程里 获取thread
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}
switch (cmd) {
case BINDER_WRITE_READ:
//走这
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
case BINDER_SET_MAX_THREADS: {
int max_threads;
if (copy_from_user(&max_threads, ubuf,
sizeof(max_threads))) {
ret = -EINVAL;
goto err;
}
binder_inner_proc_lock(proc);
proc->max_threads = max_threads;
binder_inner_proc_unlock(proc);
break;
}
case BINDER_SET_CONTEXT_MGR:
ret = binder_ioctl_set_ctx_mgr(filp);
if (ret)
goto err;
break;
case BINDER_THREAD_EXIT:
binder_thread_release(proc, thread);
thread = NULL;
break;
case BINDER_VERSION: {
struct binder_version __user *ver = ubuf;
if (size != sizeof(struct binder_version)) {
ret = -EINVAL;
goto err;
}
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
&ver->protocol_version)) {
ret = -EINVAL;
goto err;
}
break;
}
case BINDER_GET_NODE_INFO_FOR_REF: {
struct binder_node_info_for_ref info;
if (copy_from_user(&info, ubuf, sizeof(info))) {
ret = -EFAULT;
goto err;
}
ret = binder_ioctl_get_node_info_for_ref(proc, &info);
if (ret < 0)
goto err;
if (copy_to_user(ubuf, &info, sizeof(info))) {
ret = -EFAULT;
goto err;
}
break;
}
case BINDER_GET_NODE_DEBUG_INFO: {
struct binder_node_debug_info info;
if (copy_from_user(&info, ubuf, sizeof(info))) {
ret = -EFAULT;
goto err;
}
ret = binder_ioctl_get_node_debug_info(proc, &info);
if (ret < 0)
goto err;
if (copy_to_user(ubuf, &info, sizeof(info))) {
ret = -EFAULT;
goto err;
}
break;
}
default:
ret = -EINVAL;
goto err;
}
ret = 0;
err:
if (thread)
thread->looper_need_return = false;
wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
err_unlocked:
trace_binder_ioctl_done(ret);
return ret;
}
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);
//来自于用户端 所以是user
void __user *ubuf = (void __user *)arg;
//内核空间的地址
struct binder_write_read bwr;
//肯定等于 所以不进去
if (size != sizeof(struct binder_write_read)) {
ret = -EINVAL;
goto out;
}
//走 这 将用户空间的 地址里的东西 copy内核结构体里面去
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
if (bwr.write_size > 0) {//有数据肯定大于0 走 binder_thread_write
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);//这里 write_consumed 是0
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 (bwr.read_size > 0) {
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
binder_inner_proc_lock(proc);
if (!binder_worklist_empty_ilocked(&proc->todo))
binder_wakeup_proc_ilocked(proc);
binder_inner_proc_unlock(proc);
if (ret < 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;
}
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){ // consumed 0 size 是buffer 长度
uint32_t cmd;
struct binder_context *context = proc->context;
//又转成user
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;
//IPCThreadState::transact { err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);}
//这里所以是 BC_TRANSACTION
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
trace_binder_command(cmd);
if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
atomic_inc(&binder_stats.bc[_IOC_NR(cmd)]);
atomic_inc(&proc->stats.bc[_IOC_NR(cmd)]);
atomic_inc(&thread->stats.bc[_IOC_NR(cmd)]);
}
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 第三个参数 是false
binder_transaction(proc, thread, &tr,
cmd == BC_REPLY, 0);
break;
}
//省略。。。
default:
pr_err("%d:%d unknown command %d\n",
proc->pid, thread->pid, cmd);
return -EINVAL;
}
*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){
//reply 为false 是0 最后 extra_buffers_size 为0 tr是关键
//省略
if(reply){
//省略
} else{
if (tr->target.handle) {//之前构造的handle 是0 则为SM
//ProcessState::getContextObject Sm构造的时候 自己是0
//可以 看 BpBinder.cpp 中的 BpBinder::create
//这里代码可以看ProcessState::getContextObject 有一个基础的调用过程
//最后构建了 一个 binder_transaction_data 里面加入的handle 就是0 正好对应binder.c 里面的基础流程
struct binder_ref *ref;
//加锁
binder_proc_lock(proc);
//看该方法
ref = binder_get_ref_olocked(proc, tr->target.handle,
true);
if (ref) {//找到对方进程的binder node 这玩意相当于 binder 实现
//因为你可能一对多 在binder_open 里面
// 调用 加入进去的hlist_add_head(&proc->proc_node, &binder_procs);
//一般情况下是1对1 正常来说 target_node=ref->node->proc;
// tagget_node 相当于 内核里面对应的那块 proc 相当于具体哪个进程的
//
target_node = binder_get_node_refs_for_txn(
ref->node, &target_proc,
&return_error);
} else {
binder_user_error("%d:%d got transaction to invalid handle\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
}
binder_proc_unlock(proc);
}
//省略
if (target_thread)
e->to_thread = target_thread->pid;
e->to_proc = target_proc->pid;
//申请内存 struct binder_transaction *t; 结构体
t = kzalloc(sizeof(*t), GFP_KERNEL);
//省略
//给t 的from 赋值
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;
else
t->from = NULL;
//赋值。。。
t->sender_euid = task_euid(proc->tsk);
t->to_proc = target_proc;
t->to_thread = target_thread;
t->code = tr->code;
t->flags = tr->flags;
if (!(t->flags & TF_ONE_WAY) &&
binder_supported_policy(current->policy)) {
t->priority.sched_policy = current->policy;
t->priority.prio = current->normal_prio;
} else {
t->priority = target_proc->default_priority;
trace_binder_transaction(reply, t, target_node);
// buffer 参数 分别是申请内存的相关 offsets_size 感觉没啥用 extra_buffers_size默认为0
//这个buffer 其实是在目标对象的内存空间申请的
t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
tr->offsets_size, extra_buffers_size,
!reply && (t->flags & TF_ONE_WAY));
//省略
//把data buffer 拷贝 datasize的长度到 t的buffer里面 这里是ipcdata
if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
tr->data.ptr.buffer, tr->data_size)) {
binder_user_error("%d:%d got transaction with invalid data ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
return_error_param = -EFAULT;
return_error_line = __LINE__;
goto err_copy_data_failed;
}
//copy
if (copy_from_user(offp, (const void __user *)(uintptr_t)
tr->data.ptr.offsets, tr->offsets_size)) {
binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
return_error_param = -EFAULT;
return_error_line = __LINE__;
goto err_copy_data_failed;
}
//省略
for (; offp < off_end; offp++) {
struct binder_object_header *hdr;
//省略
//将t的buffer中data 转回了 binder_object_header
//可能将binder 转为handle类型 因为需要在对应的进程里面进行查询 添加
hdr = (struct binder_object_header *)(t->buffer->data + *offp);
off_min = *offp + object_size;
switch (hdr->type) {// 获取类型
case BINDER_TYPE_BINDER://可以理解为就是这个类型
case BINDER_TYPE_WEAK_BINDER: {
struct flat_binder_object *fp;
fp = to_flat_binder_object(hdr);
ret = binder_translate_binder(fp, t, thread);
if (ret < 0) {
return_error = BR_FAILED_REPLY;
return_error_param = ret;
return_error_line = __LINE__;
goto err_translate_failed;
}
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
struct flat_binder_object *fp;
struct binder_ref_data rdata;
int ret;
fp = to_flat_binder_object(hdr);
ret = binder_dec_ref_for_handle(proc, fp->handle,
hdr->type == BINDER_TYPE_HANDLE, &rdata);
if (ret) {
pr_err("transaction release %d bad handle %d, ret = %d\n",
debug_id, fp->handle, ret);
break;
}
binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d\n",
rdata.debug_id, rdata.desc);
} break;
//省略
//赋值为 数据传输完成
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
//t的状态切换为工作
t->work.type = BINDER_WORK_TRANSACTION;
if (reply) {
binder_enqueue_thread_work(thread, tcomplete);
binder_inner_proc_lock(target_proc);
if (target_thread->is_dead) {
binder_inner_proc_unlock(target_proc);
goto err_dead_proc_or_thread;
}
BUG_ON(t->buffer->async_transaction != 0);
binder_pop_transaction_ilocked(target_thread, in_reply_to);
binder_enqueue_thread_work_ilocked(target_thread, &t->work);
binder_inner_proc_unlock(target_proc);
wake_up_interruptible_sync(&target_thread->wait);
binder_restore_priority(current, in_reply_to->saved_priority);
binder_free_transaction(in_reply_to);
} else if (!(t->flags & TF_ONE_WAY)) {
//不是回复 走这
binder_inner_proc_lock(proc);
binder_enqueue_deferred_thread_work_ilocked(thread, tcomplete);
//跟T走 1 需要回复
t->need_reply = 1;
//现在还是null
t->from_parent = thread->transaction_stack;
//把transaction_stack 赋值为t
thread->transaction_stack = t;
binder_inner_proc_unlock(proc);
//任务放进todo 队列 唤醒了线程 准备干活
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 {
//省略
}
//省略
smp_wmb();
WRITE_ONCE(e->debug_id_done, t_debug_id);
return;
}
}
}
static struct binder_ref *binder_get_ref_olocked(struct binder_proc *proc,
u32 desc, bool need_strong_ref)
{
struct rb_node *n = proc->refs_by_desc.rb_node;
struct binder_ref *ref;
//相当于红黑树 遍历出来对应的ref
while (n) {
ref = rb_entry(n, struct binder_ref, rb_node_desc);
if (desc < ref->data.desc) {
n = n->rb_left;
} else if (desc > ref->data.desc) {
n = n->rb_right;
} else if (need_strong_ref && !ref->data.strong) {
return NULL;
} else {
return ref;
}
}
return NULL;
}
struct binder_ref {
struct binder_ref_data data;
struct rb_node rb_node_desc;
struct rb_node rb_node_node;
struct hlist_node node_entry;
struct binder_proc *proc;
struct binder_node *node;
struct binder_ref_death *death;
};
static size_t binder_validate_object(struct binder_buffer *buffer, u64 offset)
{
/* Check if we can read a header first */
struct binder_object_header *hdr;
size_t object_size = 0;
if (offset > buffer->data_size - sizeof(*hdr) ||
buffer->data_size < sizeof(*hdr) ||
!IS_ALIGNED(offset, sizeof(u32)))
return 0;
/* Ok, now see if we can read a complete object. */
hdr = (struct binder_object_header *)(buffer->data + offset);
switch (hdr->type) {
case BINDER_TYPE_BINDER://binder 类型
case BINDER_TYPE_WEAK_BINDER:
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE:
object_size = sizeof(struct flat_binder_object);
break;
case BINDER_TYPE_FD:
object_size = sizeof(struct binder_fd_object);
break;
case BINDER_TYPE_PTR:
object_size = sizeof(struct binder_buffer_object);
break;
case BINDER_TYPE_FDA:
object_size = sizeof(struct binder_fd_array_object);
break;
default:
return 0;
}
if (offset <= buffer->data_size - object_size &&
buffer->data_size >= object_size)
return object_size;
else
return 0;
}
大家常说的Binder只有一次拷贝,这个怎么简单理解呢。就是A进程, 其实在构建对象的时候,自己也不知自己需要跨进程。不可能这会直接往共享内存干。 他会在自己的堆栈空间里,找一块放这些数据。
然后 需要跨进程的时候,内核空间开一块 物理内存 给他俩进程共享,把数据拷贝到这块共享内存 就是一次拷贝。
但是 其实 Parcel 中的 mData 中的对象才会一次拷贝,因为比较大。 结构体里面的其他数据是不会的。因为很小。还是需要 先搞到内核, 然后内核再拷到对方那去。
还有数据的偏移问题,Parcel 数据都在mData 里面 ,但是 比如被包装为flat_binder_object
这种,只能记录在mObject 里。 然后读的时候 就先算 在mObject 之前需要偏移多少,然后挨个读。 mObject里面要知道 第一个对象 开始和结束,第二个开始结束。
mData和 mObject 这玩意数据量大,只拷贝一次,像一般的基础类型,其实是不会节省。
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;
struct binder_priority node_prio;
bool oneway = !!(t->flags & TF_ONE_WAY);
bool pending_async = false;
BUG_ON(!node);
binder_node_lock(node);
node_prio.prio = node->min_priority;
node_prio.sched_policy = node->sched_policy;
if (oneway) {
BUG_ON(thread);
if (node->has_async_transaction) {
pending_async = true;
} else {
node->has_async_transaction = 1;
}
}
binder_inner_proc_lock(proc);
//没死
if (proc->is_dead || (thread && thread->is_dead)) {
binder_inner_proc_unlock(proc);
binder_node_unlock(node);
return false;
}
//现在thread是null
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 */);
binder_inner_proc_unlock(proc);
binder_node_unlock(node);
return true;
}
#### binder的读取
//binder_wakeup_thread_ilocked()-->唤醒等待的线程
//native/libs/binder/IPCThreadState.cpp
//会一直等待
void IPCThreadState::joinThreadPool(bool isMain)
-->getAndExecuteCommand()
-->talkWithDriver()
{
if (doReceive && needRead) {
//这次走read
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
#if defined(__ANDROID__)
//走ioctl
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
}
//--------------------ioctrl 代码开始------------------------------
--->Binder.c /binder_ioctl(){
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
}
-->binder_ioctl_write_read() {
//省略
if (bwr.read_size > 0) {
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
binder_inner_proc_lock(proc);
if (!binder_worklist_empty_ilocked(&proc->todo))
binder_wakeup_proc_ilocked(proc);
binder_inner_proc_unlock(proc);
if (ret < 0) {
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
}
--> binder_thread_read(){
//省略 等待别人唤醒
ret = binder_wait_for_work(thread, wait_for_proc_work);
//looper 被重置掉 可以走下面代码
thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
//省略
switch (w->type) {
whilie(1){
struct binder_transaction *t = NULL;
/省略
case BINDER_WORK_TRANSACTION: {
//加锁
binder_inner_proc_unlock(proc);
//算出t 的启点 其实t就是传过来的数据的相关内容
t = container_of(w, struct binder_transaction, work);
//省略
//把t的相关都拿出来 拷贝到这个进程空间里面
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;
}
//继续赋值用户端传递的数据
tr.code = t->code;
tr.flags = t->flags;
tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);
t_from = binder_get_txn_from(t);
//数据长度
tr.data_size = t->buffer->data_size;
//偏移
tr.offsets_size = t->buffer->offsets_size;
tr.data.ptr.offsets = tr.data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof(void *));
//
if (put_user(cmd, (uint32_t __user *)ptr)) {
if (t_from)
binder_thread_dec_tmpref(t_from);
binder_cleanup_transaction(t, "put_user failed",
BR_FAILED_REPLY);
return -EFAULT;
}
//做偏移
ptr += sizeof(uint32_t);
//内核里面的东西拷贝到ptr里
if (copy_to_user(ptr, &tr, sizeof(tr))) {
if (t_from)
binder_thread_dec_tmpref(t_from);
binder_cleanup_transaction(t, "copy_to_user failed",
BR_FAILED_REPLY);
return -EFAULT;
}
//省略
}
}
// --------------------ioctrl 代码结束------------------------------
//继续走 getAndExecuteCommand 后面的代码
result = executeCommand(cmd);
{
case BR_TRANSACTION_SEC_CTX:
case BR_TRANSACTION:
{
//省略
result = mIn.read(&tr, sizeof(tr));
tr_secctx.secctx = 0;
buffer.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);
//省略 执行到bbinder
error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
&reply, tr.flags);
}
}
// 就是一读一写 通知线程等待和唤醒 ioctrl 才是开始进入内核状态