可以参照如下文章
从线程的一生看系统调用实现原理(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<pthread_internal_t*>(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 <pthread.h>
-
- 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);
- }