Bluedroid中的线程介绍
版权所有,转载时请注明出处
luowh0822@outlook.com
本文基于android O的代码进行分析。通过本文档,能够了解bluedroid的线程结构和协议栈架构。
线程的基本用法
osi/src/thread.cc
对posix的线程函数进行了封装
thread_t* thread_new(const char* name);
bool thread_post(thread_t* thread, thread_fn func, void* context);
使用thread_new()创建一个线程,参数为线程名字的字符串。
使用thread_post()把子线程的处理函数传递给子线程。
Bluedroid封装的后的thread函数与POSIX的thread有点不同。
- thread_new不需要传入子线程的入口函数,内部会创建一个默认的入口函数,等待用户传入任务。
- thread_post传递用户真正需要的执行函数给子线程。
线程详解
struct thread_t {
std::atomic_bool is_joined{
false}; //是否joined
pthread_t pthread; //pthread的handle
pid_t tid; //线程id
char name[THREAD_NAME_MAX + 1]; //线程名
reactor_t* reactor; //很重要的一个参数,循环epoll所有注册的queue,当queue中有数据就会调用相应的函数处理
fixed_queue_t* work_queue; //处理函数队列,用户调用thread_post()把处理函数及参数放到这个队列中,在reactor中处理。
};
thread_t* thread_new_sized(const char* name, size_t work_queue_capacity) {
thread_t* ret = static_cast<thread_t*>(osi_calloc(sizeof(thread_t)));
ret->reactor = reactor_new();
ret->work_queue = fixed_queue_new(work_queue_capacity);
// Start is on the stack, but we use a semaphore, so it's safe
struct start_arg start;
start.start_sem = semaphore_new(0);
strncpy(ret->name, name, THREAD_NAME_MAX);
start.thread = ret;
start.error = 0;
pthread_create(&ret->pthread, NULL, run_thread, &start);
semaphore_wait(start.start_sem);
semaphore_free(start.start_sem);
}
static void* run_thread(void* start_arg) {
struct start_arg* start = static_cast<struct start_arg*>(start_arg);
thread_t* thread = start->thread;
thread->tid = gettid();
semaphore_post(start->start_sem);
int fd = fixed_queue_get_dequeue_fd(thread->work_queue);
void* context = thread->work_queue;
reactor_object_t* work_queue_object =
reactor_register(thread->reactor, fd, context, work_queue_read_cb, NULL);
reactor_start(thread->reactor);
...
}
- 当用户调用thread_new后,内部首先创建线程的reactor和work_queue,semaphore用来创建线程时跟父线程同步用的。
- 然后调用pthread_create(&ret->pthread, NULL, run_thread, &start)函数,创建并执行run_thread()接口函数。
- 在run_thread()函数里,调用reactor_register(),把work_queue加入到epoll的集合里。具体细节是work_queue有semaphore,而semaphore是使用eventfd实现的,eventfd可以使用epoll来监听。当有数据放到queue里面,semaphore就会改变,epoll就可以被唤醒。
- reactor_start(thread->reactor),函数里面有死循环,通过epoll,监听所有)通过reactor_register()传入的queue。当queue有数据传入,执行fixed_queue_register_dequeue()传入的对应处理函数。
创建线程后,线程会把自己内部的work_queue注册到reactor,当用户调用thread_post,传入函数指针和参数后,子线程就会处理。当创建线程后,至少会调用一次thread_post。我们可以看到btif_a2dp_sink.cc和btif_a2dp_source.cc中多次调用thread_post()的情况。以btif_a2dp_source.cc为例,当创建线程时,会把cmd_msg_queue注册到reactor中,用来接收a2dp的控制命令,另外当a2dp工作时,会设置一个周期的定时器,当超时,会调用thread_post,做音频数据编码再发送的工作,另外a2dp停止时,也会调用thread_post去停止。
thread还有另一种工作方式。用户调用fixed_queue_register_dequeue(),把外部的queue传给thread。这时候,向queue里添加元素(Event),就可以处理。后面bta_btu_thread会有详解的讲解。
bluedroid中的线程列表
在/system/bt/目录下”grep -rns thread_new *”就可以列出所有的thread。
下面列出的的是去掉测试代码中的线程后的线程列表:
thread | description |
---|---|
thread_new(“module_wrapper”) | module manager |
thread_new(“stack_manager”) | stack manager, enable other thread |
thread_new(“btif_sock”) | btif socket |
thread_new(“btif_a2dp_sink_worker_thread”) | a2dp sink |
thread_new_sized(“media_worker”, MAX_MEDIA_WORKQUEUE_SEM_COUNT) | a2dp source |
thread_new(BT_JNI_WORKQUEUE_NAME) | jni thread |
thread_new(“hci_thread”) | hci thread |
thread_new(“hci_inject”) | send hci raw data |
thread_new_sized(“alarm_default_callbacks”, SIZE_MAX) | alarm callback execution |
thread_new(“alarm_dispatcher”) | alarm management |
thread_new(BT_WORKQUEUE_NAME) | bta_btu thread, core stack thread. |
另外,Android N及之前的版本有bt transport layer的线程,用来读uart的数据。在Android O的时候,这部分的工作在hidl里完成了。
主要线程分析
接下来分析bluedroid流程分析时最重要的三个线程,从下往上分别是:hci_thread, bta_btu_thread, jni_thread。
hci_thread:靠近底层传输层,完成HCI的拆包和组包工作,处理HCI命令,事件及数据包。
bta_btu_thread:实现协议栈的核心功能
jni_thread:衔接协议栈及上层JNI
jni_thread
- 上层的java service调用bluredroid接口时,切换到这个线程执行调用程序,并跟下层(协议栈核心模块,bta_btu_thread)交互
- bta_btu_thread向上发送的event,切换到这个线程处理event。
btif_core.cc
bt_status_t btif_init_bluetooth() {
bt_jni_workqueue_thread = thread_new(BT_JNI_WORKQUEUE_NAME);
thread_post(bt_jni_workqueue_thread, run_message_loop, nullptr);
}
bt_status_t btif_transfer_context(tBTIF_CBACK* p_cback, uint16_t event, ...)
创建线程后,会执行run_messag