複数のクライアントから同時にリクエストが送信される場合、Binderドライバがサーバプロセスに対して新規スレッドの生成を要求することがあります。この動作はkernel/drivers/android/binder.cの処理ロジックに基づいています。
ドライバ内部の判定処理では、以下の条件をすべて満たした場合にBR_SPAWN_LOOPERコマンドが発行されます。
- プロセス内のrequested_threadsカウンタがゼロであること。これはスレッド生成リクエストの実行状態を示す値で、BC_REGISTER_LOOPERコマンド受信時にデクリメントされます。
- 待機中のスレッドが存在しないこと
- 起動済みスレッド数が設定上限値を超えていないこと
これらの条件を満たすと、サーバプロセスはBR_SPAWN_LOOPERを受信し、自らスレッドを生成する必要があります。
実装手順は以下の通りです。
1. スレッド数上限の設定
void configure_thread_limit(struct binder_context *handle, int max_threads) {
ioctl(handle->fd, BINDER_SET_MAX_THREADS, &max_threads);
}
2. BR_SPAWN_LOOPERの処理
void *binder_worker_main(void *arg) {
struct thread_config *cfg = (struct thread_config *)arg;
binder_event_loop(cfg->handle, cfg->handler);
return NULL;
}
// コマンド処理内の実装
case BR_SPAWN_LOOPER: {
pthread_t worker_tid;
struct thread_config worker_cfg = {
.handle = ctx,
.handler = callback
};
pthread_create(&worker_tid, NULL, binder_worker_main, &worker_cfg);
break;
}
3. スレッド登録処理
生成したスレッドは、BC_REGISTER_LOOPERコマンドを送信してドライバに登録する必要があります。
void binder_event_loop(struct binder_context *handle, request_processor proc)
{
int ret;
struct binder_write_read io_req;
uint32_t cmd_buf[64];
// スレッド登録
cmd_buf[0] = BC_REGISTER_LOOPER;
binder_write(handle, cmd_buf, sizeof(uint32_t));
for (;;) {
io_req.read_size = sizeof(cmd_buf);
io_req.read_consumed = 0;
io_req.read_buffer = (uintptr_t)cmd_buf;
io_req.write_size = 0;
io_req.write_buffer = 0;
ret = ioctl(handle->fd, BINDER_WRITE_READ, &io_req);
if (ret < 0) {
ALOGE("イベントループ失敗: %s", strerror(errno));
break;
}
ret = binder_parse(handle, 0, (uintptr_t)cmd_buf,
io_req.read_consumed, proc);
if (ret <= 0) break;
}
}
上記の実装により、サーバプロセスは複数スレッドで並行処理を行えるようになります。動作確認の結果は以下の通りです。
# ps -t | grep sample_server
root 15678 13452 5812 1896 binder_wkr b4aef9f0 S sample_server
root 15712 15678 5812 1896 binder_wkr b4aef9f0 S sample_server
スレッドが2つ存在していることが確認できます。実装前はメインスレッドのみでした。
このように、Binderドライバからのスレッド生成要求を適切に処理し、BC_REGISTER_LOOPERでの登録を行うことで、マルチスレッド化が実現できます。