前言
本文介绍pthread_create函数的使用和源码分析。
/include/pthread.h
bionic/libc/bionic/pthread_create.cpp
bionic/libc/bionic/pthread_attr.cpp
pthread_create使用
Android中的绝大部分线程,最后都是通过pthread_create创建的。
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg);
-
pthread_t *thread:
传递一个 pthread_t 类型的指针变量,也可以直接传递某个 pthread_t 类型变量的地址。pthread_t 是一种用于表示线程的数据类型,每一个 pthread_t 类型的变量都可以表示一个线程。 -
const pthread_attr_t *attr:
用于手动设置新建线程的属性,例如线程的调用策略、线程所能使用的栈内存的大小等。大部分场景中,我们都不需要手动修改线程的属性,将 attr 参数赋值为 NULL,pthread_create() 函数会采用系统默认的属性值创建线程。 -
void *(*start_routine) (void ):
以函数指针的方式指明新建线程需要执行的函数,该函数的参数最多有 1 个(可以省略不写),形参和返回值的类型都必须为 void 类型。 -
void *arg:指定传递给 start_routine 函数的实参,当不需要传递任何数据时,将 arg 赋值为 NULL 即可。
-
返回值:
如果成功创建线程,pthread_create() 函数返回数字 0,反之返回非零值。各个非零值都对应着不同的宏,指明创建失败的原因,常见的宏有以下几种:
EAGAIN:系统资源不足,无法提供创建线程所需的资源。
EINVAL:传递给 pthread_create() 函数的 attr 参数无效。
EPERM:传递给 pthread_create() 函数的 attr 参数中,某些属性的设置为非法操作,程序没有相关的设置权限。
// 示例:
pthread_t new_pthread;
pthread_attr_t attr;
child_thread->tlsPtr_.tmp_jni_env = child_jni_env_ext.get();
CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), "new thread");
// 设置PTHREAD_CREATE_DETACHED属性,线程销毁后自动清理资源
CHECK_PTHREAD_CALL(pthread_attr_setdetachstate, (&attr, PTHREAD_CREATE_DETACHED), "PTHREAD_CREATE_DETACHED");
// 设置堆栈size
CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), stack_size);
pthread_create_result = pthread_create(&new_pthread,
&attr,
Thread::CreateCallback,
child_thread);
pthread_create源码分析
int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr, void* (*start_routine)(void*), void* arg) {
pthread_attr_t thread_attr;
if (attr == NULL) {
pthread_attr_init(&thread_attr);
} else {
thread_attr = *attr;
attr = NULL; // Prevent misuse below.
}
pthread_internal_t* thread = NULL;
void* child_stack = NULL;
int result = __allocate_thread(&thread_attr, &thread, &child_stack);
thread->start_routine = start_routine;
thread->start_routine_arg = arg;
thread->set_cached_pid(getpid());
// clone
int flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM |
CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;
void* tls = reinterpret_cast<void*>(thread->tls);
clone(__pthread_start, child_stack, flags, thread, &(thread->tid), tls, &(thread->tid));
__init_thread(thread);
return 0;
}
pthread_attr_init
每个线程都可以处理信号,也就是执行信号处理函数。
执行函数需要栈空间,这部分栈空间可以和线程栈公用,也可以单独申请一块内存。
如果和线程栈公用,那当出现线程栈溢出导致的SIGSEGV异常时,信号处理函数也无法执行下去了。
为了避免这种情况安卓就会在线程栈中,预留了这部分内存,这部分就是SIGSTKSZ。
#define PTHREAD_STACK_SIZE_DEFAULT ((1 * 1024 * 1024) - SIGSTKSZ)
#define SIGSTKSZ 8192
int pthread_attr_init(pthread_attr_t* attr) {
attr->flags = 0;
attr->stack_base = NULL;
attr->stack_size = PTHREAD_STACK_SIZE_DEFAULT; // 1M -8k
attr->guard_size = PAGE_SIZE;
attr->sched_policy = SCHED_NORMAL;
attr->sched_priority = 0;
return 0;
}
__allocate_thread
分配内存
mmap_size = stack_size + 4KB;
static int __allocate_thread(pthread_attr_t* attr, pthread_internal_t** threadp, void** child_stack) {
size_t mmap_size;
uint8_t* stack_top;
if (attr->stack_base == NULL) {
// mmap_size = stack_size + 4KB
mmap_size = BIONIC_ALIGN(attr->stack_size + sizeof(pthread_internal_t), PAGE_SIZE);
attr->guard_size = BIONIC_ALIGN(attr->guard_size, PAGE_SIZE);
attr->stack_base = __create_thread_mapped_space(mmap_size, attr->guard_size);
if (attr->stack_base == NULL) {
return EAGAIN;
}
stack_top = reinterpret_cast<uint8_t*>(attr->stack_base) + mmap_size;
} else {
mmap_size = 0;
stack_top = reinterpret_cast<uint8_t*>(attr->stack_base) + attr->stack_size;
}
stack_top = reinterpret_cast<uint8_t*>((reinterpret_cast<uintptr_t>(stack_top) - sizeof(pthread_internal_t)) & ~0xf);
pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(stack_top);
if (mmap_size == 0) {
memset(thread, 0, sizeof(pthread_internal_t));
}
attr->stack_size = stack_top - reinterpret_cast<uint8_t*>(attr->stack_base);
thread->mmap_size = mmap_size;
thread->attr = *attr;
__init_tls(thread);
*threadp = thread;
*child_stack = stack_top;
return 0;
}
clone
所有用户态创建的线程都是libc中的clone()函数实现的,通过系统调用再去创建线程。
创建线程需要传入线程栈(child_stack)和线程的入口函数(fd)。
所有clone创建的线程在/proc/pid/task/目录下都有对应的节点,节点名称为该线程的tid。
bionic/libc/bionic/clone.cpp
int clone(int (*fn)(void*), void* child_stack, int flags, void* arg, ...) {
int* parent_tid = NULL;
int* child_tid = NULL;
int clone_result = __bionic_clone(flags, child_stack, parent_tid, new_tls, child_tid, fn, arg);
return clone_result;
}
__init_thread
int __init_thread(pthread_internal_t* thread) {
atomic_init(&thread->join_state, THREAD_DETACHED);
// Set the scheduling policy/priority of the thread.
if (thread->attr.sched_policy != SCHED_NORMAL) {
sched_param param;
param.sched_priority = thread->attr.sched_priority;
sched_setscheduler(thread->tid, thread->attr.sched_policy, ¶m);
}
return 0;
}
linux的clone、fork、vfork
这里需要理解linux创建进程的几个方法,clone、fork和vfork。
man手册
Linux Clone函数