SwallowJoe的博客

Be a real go-getter,
NEVER SETTLE!

0%

Binder(5)--binder驱动响应ioctl

简介

接上文,我们现在了解了binder驱动的加载过程,回过头继续分析App使用Binder通信的过程。

先回顾下App使用binder通信的大致过程:

  1. 创建AIDL文件,定义接口函数并在服务端app中实现,并注册进SystemServer
  2. 客户端app通过SystemServer获取服务端注册的Service所代表的IBinder(BpBinder)
    1. Client app <–> SystemServer <–> Server app
    2. Server App将Service的IBinder保存在SystemServer中,在Client App通过bindService的时候,传入。
  3. 客户端app通过该IBinder与服务端app直接通信。

之前我们分析到最后一步是:IPCThreadState#talkWithDriver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
// ......

do {
// .....
// 使用ioctl与binder驱动通信, 将bwr存储的信息传输给binder驱动
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
// .....
if (mProcess->mDriverFD < 0) {
err = -EBADF;
}
IF_LOG_COMMANDS() {
alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
}
} while (err == -EINTR);

// ......

return err;
}

在前面我们也了解了ioctl的用法,简单回顾下:

int ioctl(int fd, unsigned long request, …);

  1. fd 必须是打开文件描述符。
  2. cmd 是依赖于设备的请求代码, 即交互协议,设备驱动将根据 cmd 执行对应操作
  3. argp(…) 是指向内存的非类型指针, 它传统上是char*argp, ioctl()请求在其中编码了参数是in参数还是out参数,参数argp的大小以字节为单位。

这里我们还不知道此处的ioctl是怎么和binder驱动勾搭上的,是因为我们之前分析客户端app使用ioctl的时候忽略了IPCThreadState的初始化。

传入的参数mProcess->mDriverFD这个文件描述符是怎么获取的呢,接下来先看IPCThreadState的初始化。

一. IPCThreadState的初始化

我们在调用transact的时候,会先调用self()函数,这个函数就是初始化用的。

1
2
IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);

1.1 IPCThreadState::self

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
static std::atomic<bool> gHaveTLS(false);
static pthread_key_t gTLS = 0;
static std::atomic<bool> gShutdown = false;
static std::atomic<bool> gDisableBackgroundScheduling = false;

IPCThreadState* IPCThreadState::self()
{
// gHaveTLS为true说明之前已经初始化过
// 或者说pthread_key_create有被调用过,及本线程私有数据内存空间已开辟
if (gHaveTLS.load(std::memory_order_acquire)) {
restart:
const pthread_key_t k = gTLS;
// 取得本线程对应关键字gTLS所关联的私有数据空间首址
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
// 如果不为null,直接返回, 当然如果是第一次进入肯定为null的。
if (st) return st;
// 1.2 实例化IPCThreadState
return new IPCThreadState;
}

// 处于shutdown时直接返回null
if (gShutdown.load(std::memory_order_relaxed)) {
ALOGW("Calling IPCThreadState::self() during shutdown is dangerous, expect a crash.\n");
return nullptr;
}

pthread_mutex_lock(&gTLSMutex);
if (!gHaveTLS.load(std::memory_order_relaxed)) {
// 创建关键字gTLS及其对应的内存释放函数threadDestructor
// 关键字关联线程私有数据空间首址,初始化时是NULL
int key_create_value = pthread_key_create(&gTLS, threadDestructor);
if (key_create_value != 0) {
pthread_mutex_unlock(&gTLSMutex);
ALOGW("IPCThreadState::self() unable to create TLS key, expect a crash: %s\n",
strerror(key_create_value));
return nullptr;
}
// 表明线程私有数据内存空间已开辟
gHaveTLS.store(true, std::memory_order_release);
}
pthread_mutex_unlock(&gTLSMutex);
// 回到restart中准备实例化IPCThreadState
goto restart;
}

在Linux 中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone() 。
该系统copy 了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。不过这个copy过程和fork不一样。
copy 后的进程和原先的进程共享了所有的变量,运行环境(clone的实现是可以指定新进程与老进程之间的共享关系,100%共享就表示创建了一个线程)。
这样,原先进程中的变量变动在copy 后的进程中便能体现出来。

1.2 IPCThreadState实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
IPCThreadState::IPCThreadState()
// 1.2.1 创建ProcessState
: mProcess(ProcessState::self()),
mServingStackPointer(nullptr),
mWorkSource(kUnsetWorkSource),
mPropagateWorkSource(false),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0),
mCallRestriction(mProcess->mCallRestriction)
{
// 将自身存入关键字gTLS对应的线程私有数据空间中,缓存以备后续使用
pthread_setspecific(gTLS, this);
clearCaller();
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
}

1.3 ProcessState::self

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifdef __ANDROID_VNDK__
const char* kDefaultDriver = "/dev/vndbinder";
#else
const char* kDefaultDriver = "/dev/binder";
#endif

sp<ProcessState> ProcessState::self()
{
Mutex::Autolock _l(gProcessMutex);
if (gProcess != nullptr) {
return gProcess;
}
// 这里假定kDefaultDriver为"/dev/binder"
gProcess = new ProcessState(kDefaultDriver);
return gProcess;
}

1.4 ProcessState实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
ProcessState::ProcessState(const char *driver)
: mDriverName(String8(driver))
// 1.5 打开binder驱动,获取其文件描述符
, mDriverFD(open_driver(driver))
, mVMStart(MAP_FAILED)
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
, mExecutingThreadsCount(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
, mStarvationStartTimeMs(0)
, mBinderContextCheckFunc(nullptr)
, mBinderContextUserData(nullptr)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
, mCallRestriction(CallRestriction::NONE)
{
// ......
if (mDriverFD >= 0) {
// 将binder驱动与进程内存映射,提供一块虚拟地址空间来接收binder驱动数据。
mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
// *sigh*
ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
close(mDriverFD);
mDriverFD = -1;
mDriverName.clear();
}
}
// ......
}

1.5 open_driver - 打开binder驱动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static int open_driver(const char *driver)
{
// 1.5.1 打开/dev/binder驱动
int fd = open(driver, O_RDWR | O_CLOEXEC);
if (fd >= 0) {
int vers = 0;
// 1.5.2 ioctl通信!!!
status_t result = ioctl(fd, BINDER_VERSION, &vers);
if (result == -1) {
ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
close(fd);
fd = -1;
}
if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d",
vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
close(fd);
fd = -1;
}
// #define DEFAULT_MAX_BINDER_THREADS 15
// 默认最大binder线程数目是15个
size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
// 1.5.2 ioctl通信,设置当前进程的最大binder线程数为15
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
if (result == -1) {
ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
}
} else {
ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
}
return fd;
}

这里首先使用open函数打开对应目录的驱动文件,成功打开后调用ioctl函数获取Binder版本,最后还是通过ioctl设置最大binder线程。

我们常听闻在Linux中,一切皆文件,这里就体现出来了。回顾下binder驱动注册进文件系统时的操作函数集:

1
2
3
4
5
6
7
8
9
10
11
// android/kernel/msm-4.19/drivers/android/binder.c
const struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.compat_ioctl = binder_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_release,
};

1.5.1 open函数-binder_open

在app进程中通过open函数打开/dev/binder文件,就会调用到binder.c中的binder_open函数了。

我们来看看这个open的时候驱动里面具体做了什么吧,至于用户进程中调用open进而使用系统调用进入内核态调用到binder_open的方式我们后续分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
static int binder_open(struct inode *nodp, struct file *filp)
{
// binder_proc代表一个进程
struct binder_proc *proc;
struct binder_device *binder_dev;
struct binderfs_info *info;
struct dentry *binder_binderfs_dir_entry_proc = NULL;

binder_debug(BINDER_DEBUG_OPEN_CLOSE, "%s: %d:%d\n", __func__,
current->group_leader->pid, current->pid);
// 给binder_proc分配内核内存
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (proc == NULL)
return -ENOMEM;
// 初始化自旋锁lock,其实是将自旋锁指针lock 指向SPIN_LOCK_UNLOCKED宏
// 该宏的定义在内核文件spinlock_types.h中,它表示自旋锁的状态为未加锁
spin_lock_init(&proc->inner_lock);
spin_lock_init(&proc->outer_lock);

// current是一个内核宏,它是当前进程的指针
// get_task_struct的最终实现是将传入的task_struct的usage加1:
// #define get_task_struct(tsk) do { atomic_inc(&(tsk)->usage); } while(0)
// 这个宏的作用是标记传入的task_struct,当期有人在用。
// usage的数量代表正在使用该task_struct的程序数量。
get_task_struct(current->group_leader);
// 将binder_proc的tsk标记为当前进程的主进程
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);
}

// 判断nodp是否是binder_device:
// inode->i_sb->s_magic == BINDERFS_SUPER_MAGIC
// s_magic是在binderfs_fill_super也就是binder驱动挂载时赋值的
if (is_binderfs_device(nodp)) {
// 如果是binder_device:
// 回忆下上一篇文章3.4 binderfs.c#binderfs_binder_device_create
// 我们将创建的binder_device存入了i_private数据中
binder_dev = nodp->i_private;
info = nodp->i_sb->s_fs_info;
binder_binderfs_dir_entry_proc = info->proc_log_dir;
} else {
binder_dev = container_of(filp->private_data,
struct binder_device, miscdev);
}
// 标记binder_device的引用计数+1
refcount_inc(&binder_dev->ref);
// binder_proc的上下文
proc->context = &binder_dev->context;
binder_alloc_init(&proc->alloc);
// binder_stats中BINDER_STAT_PROC类型的创建数加+1
binder_stats_created(BINDER_STAT_PROC);
// binder_proc的pid为当前进程的主进程的pid
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
INIT_LIST_HEAD(&proc->waiting_threads);
filp->private_data = proc;

mutex_lock(&binder_procs_lock);
// 将当前binder_proc的proc_node加入binder_procs列表中
// binder_procs是binder驱动中存储一系列binder_proc的链表
// 关于binder_proc是如何插入链表中的,我们后续分析,涉及‘传说中’的红黑树操作
// 注意当前虽然是在应用进程中,但此时已经进入内核态了。
hlist_add_head(&proc->proc_node, &binder_procs);
mutex_unlock(&binder_procs_lock);

// sys/kernel/debug/binder/proc目录存在时
if (binder_debugfs_dir_entry_proc) {
char strbuf[11];

snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
// proc调试条目是在上下文之间共享的
// 创建当前binder_proc的目录,可以看到在sys/kernel/debug/binder/proc目录下有很多pid的目录
proc->debugfs_entry = debugfs_create_file(strbuf, 0444,
binder_debugfs_dir_entry_proc,
(void *)(unsigned long)proc->pid,
&proc_fops);
}

// 也是binderfs_fill_super中创建的一个目录:/dev/binderfs/binder_logs/proc
if (binder_binderfs_dir_entry_proc) {
char strbuf[11];
struct dentry *binderfs_entry;

snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
// 与debugfs类似,进程特定的日志文件在上下文之间共享。
// 如果已经为进程创建了文件,如果同一进程的另一个上下文调用binder_open()
// 则以下binderfs_create_file()调用将失败,错误代码为EEXIST。
// 因为与debugfs相同,日志文件将包含给定PID的所有上下文的信息。
binderfs_entry = binderfs_create_file(binder_binderfs_dir_entry_proc,
strbuf, &proc_fops, (void *)(unsigned long)proc->pid);
if (!IS_ERR(binderfs_entry)) {
proc->binderfs_entry = binderfs_entry;
} else {
int error;

error = PTR_ERR(binderfs_entry);
if (error != -EEXIST) {
pr_warn("Unable to create file %s in binderfs (error %d)\n",
strbuf, error);
}
}
}

return 0;
}

binder_open的函数做的事情也不复杂:

  1. 创建binder_proc, 其pid为当前进程的主进程pid
  2. 标记binder_device的引用计数+1
  3. 将当前binder_proc的proc_node加入binder_procs列表中
  4. 创建当前binder_proc的目录,目录名为: sys/kernel/debug/binder/proc/pid
  5. /dev/binderfs/binder_logs/proc存在时,也在这个目录下创建对应pid目录

顺便说一下很多Linux内核宏都用 do { code; } while(0) 的形式,这个是确保code不会被打乱,比如:

1
2
3
4
5
#define demo do { codeA; codeB; } while(0)

if (A)
demo; //这里展开就不会因为宏内有多行代码而出现逻辑异常
nextCodeLine;

1.5.2 binder_ioctl - BINDER_VERSION & BINDER_SET_CONTEXT_MGR_EXT

status_t result = ioctl(fd, BINDER_VERSION, &vers);

从参数名称可以看到,这里是获取binder版本号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
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;

/*pr_info("binder_ioctl: %d:%d %x %lx\n",
proc->pid, current->pid, cmd, arg);*/

// 分配BUFFER_NUM buffers以覆盖所有页面对齐情况,然后以所有可能的顺序释放它们。
// 检查页面是否正确分配,在释放缓冲区时放在lru上,在调用binder_alloc_free_page时释放。
binder_selftest_alloc(&proc->alloc);

trace_binder_ioctl(cmd, arg);

// binder_stop_on_user_error是一个int变量,当出现binder_stop_on_user_error错误
// 时为2, 这里的意思是只要这个不为2,使得cpu不休眠。
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
goto err_unlocked;

// 根据binder_proc获取binder_thread, 后续分析
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}

switch (cmd) {
// ......
case BINDER_SET_CONTEXT_MGR_EXT: {
struct flat_binder_object fbo;
// 从用户空间拷贝ubuf内容至fbo中,其实就是15,单个进程的最大binder线程数
if (copy_from_user(&fbo, ubuf, sizeof(fbo))) {
ret = -EINVAL;
goto err;
}
// 设置单个进程的最大binder数目,默认是15个(app进程)
ret = binder_ioctl_set_ctx_mgr(filp, &fbo);
if (ret)
goto err;
break;
}
// ......
case BINDER_VERSION: {
struct binder_version __user *ver = ubuf;

if (size != sizeof(struct binder_version)) {
ret = -EINVAL;
goto err;
}
// 从内核空间获取BINDER_CURRENT_PROTOCOL_VERSION拷贝到用户空间
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
&ver->protocol_version)) {
ret = -EINVAL;
goto err;
}
break;
}
// ......
return ret;
}

copy_from_user函数的目的是从用户空间拷贝数据到内核空间,失败返回没有被拷贝的字节数,成功返回0.

1
2
3
4
5
6
copy_from_user(void *to, const void __user *from, unsigned long n)
1. *to 将数据拷贝到内核的地址
2. *from 需要拷贝数据的地址
3. n 拷贝数据的长度(字节)

也就是将from地址中的数据拷贝到to地址中去,拷贝长度是n

二. ioctl - BINDER_WRITE_READ

现在万事具备了,我们继续分析进程间的ioctl通信传输数据, 回到最开始:IPCThreadState#talkWithDriver

其实我们在open_driver - 打开binder驱动的过程中就有分析看到ioctl了。

从代码的角度理解为什么说binder跨进程通信只需要一次内存拷贝。

1
2
3
// 使用ioctl与binder驱动通信, 将bwr存储的信息传输给binder驱动
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;

2.1 binder_ioctl - BINDER_WRITE_READ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
// ......
switch (cmd) {
case BINDER_WRITE_READ:
// thread是根据binder_proc获取的binder_thread
// 这里的arg就是上面的ioctl的最后一个参数bwr的地址
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
// ......
ret = 0;
err:
if (thread)
thread->looper_need_return = false;
wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret && ret != -ERESTARTSYS)
pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
err_unlocked:
trace_binder_ioctl_done(ret);
return ret;
}

2.2 binder.c:binder_ioctl_write_read

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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;
}
// 从用户空间拷贝ubuf内容至bwr中, 一次拷贝:从用户空间到内核空间
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
binder_debug(BINDER_DEBUG_READ_WRITE,
"%d:%d write %lld at %016llx, read %lld at %016llx\n",
proc->pid, thread->pid,
(u64)bwr.write_size, (u64)bwr.write_buffer,
(u64)bwr.read_size, (u64)bwr.read_buffer);

// 还记得么,这个bwr是在IPCThreadState#talkWithDriver中填充的数据
// write_size 代表 binder in parcel数据大小
// read_size 代表 binder out parcel数据大小
if (bwr.write_size > 0) {
// 处理用户进程传来的数据
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 (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_debug(BINDER_DEBUG_READ_WRITE,
"%d:%d wrote %lld of %lld, read return %lld of %lld\n",
proc->pid, thread->pid,
(u64)bwr.write_consumed, (u64)bwr.write_size,
(u64)bwr.read_consumed, (u64)bwr.read_size);
// 将从内核空间拷贝bwr内容至ubuf中, 一次拷贝:从内核空间到用户空间
// 但是这里是新的数据了,不算是内容的重复拷贝,如果说不需要回传数据,oneway的方式,这个拷贝也不是必要的
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
out:
return ret;
}

copy_from_user函数的目的是从内核空间拷贝数据到用户空间,失败返回没有被拷贝的字节数,成功返回0.

unsigned long copy_to_user(void *to, const void *from, unsigned long n)

  1. *to 目标地址(用户空间)
  2. *from 源地址(内核空间)
  3. n 将要拷贝数据的字节数

返回:成功返回0,失败返回没有拷贝成功的数据字节数

总结

可以看到用户进程IPCThreadState的初始化之后会将binder驱动与进程内存映射,提供一块虚拟地址空间来接收binder驱动数据。
这里就拿到了binder的fd, 保存在用户进程的mDriverFD中, 现在就可以愉快的通信了。

鉴于 binder_thread_write 和 binder_thread_read 函数都特别大,接下来单开两篇分开论述。

之前看 红茶一杯话Binder(传输机制篇_中) 有一张图特别清晰的展现了这里的流程,在此贴出:

图片

参考资料

  1. module_init解析及内核initcall的初始化顺序 https://www.cnblogs.com/chaozhu/p/6410271.html
  2. 各种initcall的执行先后顺序 https://blog.csdn.net/fenzhikeji/article/details/6860143
  3. binder 驱动的操作 https://blog.csdn.net/qq_15893929/article/details/103965668
  4. Android的IPC机制Binder的各个部分 http://tech.it168.com/a2009/0331/2703/000000270388_all.shtml
  5. 字符设备驱动-使用alloc_chrdev_region+cdev注册设备驱动 https://blog.csdn.net/weixin_42314225/article/details/81112217
  6. linux文件系统 - 初始化(一) https://www.cnblogs.com/alantu2018/p/8447303.html
  7. mount过程分析之五(mount_bdev->fill_super) https://blog.csdn.net/ZR_Lang/article/details/40115013
  8. VFS四大对象之一 struct super_block https://www.cnblogs.com/linhaostudy/p/7427027.html
  9. Linux字符设备驱动file_operations https://www.cnblogs.com/chen-farsight/p/6181341.html
  10. 一种linux线程私有数据技术 http://blog.163.com/william_djj@126/blog/static/3516650120085111193035/
  11. pthread_key_t和pthread_key_create()详解 https://www.cnblogs.com/klcf0220/p/5807148.html
  12. 腾讯面试题——谈一谈Binder的原理和实现一次拷贝的流程 https://blog.csdn.net/AndroidStudyDay/article/details/93749470
  13. 红茶一杯话Binder(传输机制篇_中) https://my.oschina.net/youranhongcha/blog/152963

欢迎关注我的其它发布渠道