Linux pthread_create源码分析

前言

本文介绍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, &param);
  }
  return 0;
}

linux的clone、fork、vfork

这里需要理解linux创建进程的几个方法,clone、fork和vfork。
image.png

man手册
Linux Clone函数

猜你喜欢

转载自blog.csdn.net/u014099894/article/details/131019943