可以参照如下文章
从线程的一生看系统调用实现原理(bionic c库为例)_nginux的博客-CSDN博客。
要点:
1. 如果不特定指定PTHREAD_ATTR_FLAG_DETACHED,线程创建完状态时:THREAD_NOT_JOINED;如果指定上面FLAG,创建完状态是:THREAD_DETACHED。
2.线程执行完成的时候,默认都会调用pthread_exit(即使程序中不显示调用pthread_exit)。
源码分析:
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr,
void* (*start_routine)(void*), void* arg) {
ErrnoRestorer errno_restorer;
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);
if (result != 0) {
return result;
}
// Create a lock for the thread to wait on once it starts so we can keep
// it from doing anything until after we notify the debugger about it
//
// This also provides the memory barrier we need to ensure that all
// memory accesses previously performed by this thread are visible to
// the new thread.
thread->startup_handshake_lock.init(false);
thread->startup_handshake_lock.lock();
//用户传入的线程入口函数
thread->start_routine = start_routine;
thread->start_routine_arg = arg;
thread->set_cached_pid(getpid());
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(thread->tls);
#if defined(__i386__)
// On x86 (but not x86-64), CLONE_SETTLS takes a pointer to a struct user_desc rather than
// a pointer to the TLS itself.
user_desc tls_descriptor;
__init_user_desc(&tls_descriptor, false, tls);
tls = &tls_descriptor;
#endif
//内部通过linux clone系统调用实现
int rc = clone(__pthread_start, child_stack, flags, thread, &(thread->tid), tls, &(thread->tid));
if (rc == -1) {
int clone_errno = errno;
// We don't have to unlock the mutex at all because clone(2) failed so there's no child waiting to
// be unblocked, but we're about to unmap the memory the mutex is stored in, so this serves as a
// reminder that you can't rewrite this function to use a ScopedPthreadMutexLocker.
thread->startup_handshake_lock.unlock();
if (thread->mmap_size != 0) {
munmap(thread->attr.stack_base, thread->mmap_size);
}
async_safe_format_log(ANDROID_LOG_WARN, "libc", "pthread_create failed: clone failed: %s",
strerror(clone_errno));
return clone_errno;
}
int init_errno = __init_thread(thread);
if (init_errno != 0) {
// Mark the thread detached and replace its start_routine with a no-op.
// Letting the thread run is the easiest way to clean up its resources.
atomic_store(&thread->join_state, THREAD_DETACHED);
__pthread_internal_add(thread);
thread->start_routine = __do_nothing;
thread->startup_handshake_lock.unlock();
return init_errno;
}
// Publish the pthread_t and unlock the mutex to let the new thread start running.
*thread_out = __pthread_internal_add(thread);
thread->startup_handshake_lock.unlock();
return 0;
}
// Called from the __bionic_clone assembler to call the thread function then exit.
extern "C" __LIBC_HIDDEN__ void __start_thread(int (*fn)(void*), void* arg) {
BIONIC_STOP_UNWIND;
pthread_internal_t* self = __get_thread();
if (self && self->tid == -1) {
self->tid = syscall(__NR_gettid);
}
//相当于执行pthread_start函数
int status = (*fn)(arg);
__exit(status);
}
static int __pthread_start(void* arg) {
pthread_internal_t* thread = reinterpret_cast(arg);
// Wait for our creating thread to release us. This lets it have time to
// notify gdb about this thread before we start doing anything.
// This also provides the memory barrier needed to ensure that all memory
// accesses previously made by the creating thread are visible to us.
thread->startup_handshake_lock.lock();
__init_alternate_signal_stack(thread);
//调用到用户调用pthread_create传入的线程入口函数
void* result = thread->start_routine(thread->start_routine_arg);
//线程执行完都会调用pthread_exit
pthread_exit(result);
return 0;
}
内核执行完clone系统调用,创建完线程之后会跳转到__start_thread执行,最终会调用到用户pthread_create传入的线程执行函数体中。
man pthread_join如下:
PTHREAD_JOIN(3) Linux Programmer's Manual PTHREAD_JOIN(3)
NAME
pthread_join - join with a terminated thread
SYNOPSIS
#include
int pthread_join(pthread_t thread, void **retval);
Compile and link with -pthread.
DESCRIPTION
The pthread_join() function waits for the thread specified by thread to terminate. If that thread has already terminated, then pthread_join()
returns immediately. The thread specified by thread must be joinable.
If retval is not NULL, then pthread_join() copies the exit status of the target thread (i.e., the value that the target thread supplied to
pthread_exit(3)) into the location pointed to by *retval. If the target thread was canceled, then PTHREAD_CANCELED is placed in *retval.
If multiple threads simultaneously try to join with the same thread, the results are undefined. If the thread calling pthread_join() is can‐
celed, then the target thread will remain joinable (i.e., it will not be detached).
1. 等待指定的线程结束,且等待的线程必须是joinable(不然报错)。
2. 多个线程pthread_join一个线程结果未知。
3. pthread_join会释放等待线程的资源。
4.pthread_join执行成功,线程状态:THREAD_JOINED。
使用场景:
线程A需要请线程B帮忙干活,且要等待线程B的结果才继续执行,属于串行执行,A负责释放线程B的资源。
pthread_join的实现源码分析:
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int pthread_join(pthread_t t, void** return_value) {
if (t == pthread_self()) {
return EDEADLK;
}
pthread_internal_t* thread = __pthread_internal_find(t);
if (thread == NULL) {
return ESRCH;
}
//将线程状态修改为:THREAD_JOINED
ThreadJoinState old_state = THREAD_NOT_JOINED;
while ((old_state == THREAD_NOT_JOINED || old_state == THREAD_EXITED_NOT_JOINED) &&
!atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_JOINED)) {
}
//如果等待的线程是detached(调用过pthread_detach)或者已经被pthread_join过,返回错误。
if (old_state == THREAD_DETACHED || old_state == THREAD_JOINED) {
return EINVAL;
}
pid_t tid = thread->tid;
volatile int* tid_ptr = &thread->tid;
// We set thread->join_state to THREAD_JOINED with atomic operation,
// so no one is going to remove this thread except us.
// Wait for the thread to actually exit, if it hasn't already.
//等待线程结束
while (*tid_ptr != 0) {
__futex_wait(tid_ptr, tid, NULL);
}
//拿到等待线程的返回值
if (return_value) {
*return_value = thread->return_value;
}
//释放等待线程的资源,删除线程的数据结构和unmap栈内存
__pthread_internal_remove_and_free(thread);
return 0;
}
man pthread_detach:
NAME
pthread_detach - detach a thread
SYNOPSIS
#include
int pthread_detach(pthread_t thread);
Compile and link with -pthread.
DESCRIPTION
The pthread_detach() function marks the thread identified by thread as detached. When a detached thread terminates, its resources are automati‐
cally released back to the system without the need for another thread to join with the terminated thread.
Attempting to detach an already detached thread results in unspecified behavior.
RETURN VALUE
On success, pthread_detach() returns 0; on error, it returns an error number.
使用场景:
与pthread_join使用的场景相反,如果线程A请线程B帮忙做事,线程A和线程B可以并行执行,不需要等待线程B的结果反馈,此时由于可能B线程完毕了,那么B要负责自己的资源释放(比如栈内存释放)
要点:
1.detach的线程自己负责释放资源(数据结构和栈内存)
2.重复detach同一个线程是个未定义行为。
3.pthread_detach执行成功,线程状态为:THREAD_DETACHED
源码分析:
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int pthread_detach(pthread_t t) {
pthread_internal_t* thread = __pthread_internal_find(t);
if (thread == NULL) {
return ESRCH;
}
//线程状态修改为:THREAD_DETACHED
ThreadJoinState old_state = THREAD_NOT_JOINED;
while (old_state == THREAD_NOT_JOINED &&
!atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_DETACHED)) {
}
if (old_state == THREAD_NOT_JOINED) {
return 0;
} else if (old_state == THREAD_EXITED_NOT_JOINED) {
// Use pthread_join to clean it up.
return pthread_join(t, NULL);
}
return EINVAL;
}
上面代码看完是否产生一个疑问,man手册中说pthread_detach的线程要自己释放资源,而上面源码中并未看到释放资源的代码,线程执行完比之后是怎么实现自己释放资源的?答案是在pthread_exit接口中。
通过pthread_create小节的分析,每个线程创建完成后,会先执行__start_thread,该函数进而执行pthread_start函数,这里执行线程函数主题,线程退出的时候调用了pthread_exit,也就说每个线程退出的时候都会调用下pthread_exit,而这里正式detached线程释放自己释放资源的地方:
extern "C" __noreturn void __exit(int);
1,1 Top
memset(&ss, 0, sizeof(ss));
ss.ss_flags = SS_DISABLE;
sigaltstack(&ss, NULL);
// Free it.
munmap(thread->alternate_signal_stack, SIGNAL_STACK_SIZE);
thread->alternate_signal_stack = NULL;
}
ThreadJoinState old_state = THREAD_NOT_JOINED;
while (old_state == THREAD_NOT_JOINED &&
!atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_EXITED_NOT_JOINED)) {
}
//如果线程是DETACHED状态,释放自己的资源(栈内存和数据结构)
if (old_state == THREAD_DETACHED) {
// The thread is detached, no one will use pthread_internal_t after pthread_exit.
// So we can free mapped space, which includes pthread_internal_t and thread stack.
// First make sure that the kernel does not try to clear the tid field
// because we'll have freed the memory before the thread actually exits.
__set_tid_address(NULL);
// pthread_internal_t is freed below with stack, not here.
__pthread_internal_remove(thread);
if (thread->mmap_size != 0) {
// We need to free mapped space for detached threads when they exit.
// That's not something we can do in C.
// We don't want to take a signal after we've unmapped the stack.
// That's one last thing we can do before dropping to assembler.
ScopedSignalBlocker ssb;
__pthread_unmap_tls(thread);
_exit_with_stack_teardown(thread->attr.stack_base, thread->mmap_size);
}
}
// No need to free mapped space. Either there was no space mapped, or it is left for
// the pthread_join caller to clean up.
__pthread_unmap_tls(thread);
__exit(0);
}