Linux pthread_create源码分析

news2024/11/17 12:40:01

前言

本文介绍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函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/604729.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【LeetCode热题100】打卡第11天:有效括号合并两个有序链表

文章目录 【LeetCode热题100】打开第11天⛅前言&#x1f512;有效括号&#x1f511;题解&#x1f512;合并两个有序链表&#x1f511;题解&#x1f4a1;思路一&#xff1a;合并到第三方链表上&#x1f510;【非递归实现】&#x1f510;【递归实现】 &#x1f4a1;思路二 &#…

2023年上半年数据库系统工程师下午真题及答案解析

试题一(15分) 某新能源汽车公司为了提升效率&#xff0c;需开发一个汽车零件采购系统。请完成系统的数据库设计。 概念结构设计 需求描述 (1)记录供应商信息&#xff0c;包括供应商的名称、地址和一个电话。 (2)记录零件信息&#xff0c;包括零件的编码、名称和价格。 (3)…

线程的四个属性

如上图所示&#xff0c;线程有四个属性&#xff1a; 线程ID线程名称守护线程线程优先级 1. 线程ID 每个线程都有id&#xff0c;这个id不能修改 线程id会不停的自增&#xff0c;从1开始 main函数就是第一个线程&#xff0c;id1 id 是操作系统用来识别各个线程的编号&#…

DHVT:在小数据集上降低VIT与卷积神经网络之间差距,解决从零开始训练的问题

VIT在归纳偏置方面存在空间相关性和信道表示的多样性两大缺陷。所以论文提出了动态混合视觉变压器(DHVT)来增强这两种感应偏差。 在空间方面&#xff0c;采用混合结构&#xff0c;将卷积集成到补丁嵌入和多层感知器模块中&#xff0c;迫使模型捕获令牌特征及其相邻特征。 在信…

tcp shrinking window 之进退

一个有趣的问题&#xff1a;Unbounded memory usage by TCP for receive buffers, and how we fixed it 引出一个 kernel patch&#xff1a;[PATCH] Add a sysctl to allow TCP window shrinking in order to honor memory limits 但这 patch 把一个问题变成了两个问题&#…

apple pencil一代的平替有哪些品牌?平价电容笔推荐

要知道&#xff0c;真正的苹果原装电容笔&#xff0c;价格可不低&#xff0c;仅仅一支就是近千块。实际上&#xff0c;平替电容笔对没有太多预算的用户是个不错的选择。一款苹果的电容笔的售价&#xff0c;相当于平替电容笔的四倍&#xff0c;不过平替电容笔的书写体验&#xf…

pnpm对npm及yarn降维打击详解

目录 正文npm2yarnpnpm总结 正文 大家最近是不是经常听到 pnpm&#xff0c;我也一样。今天研究了一下它的机制&#xff0c;确实厉害&#xff0c;对 yarn 和 npm 可以说是降维打击。 那具体好在哪里呢&#xff1f; 我们一起来看一下。 我们按照包管理工具的发展历史&#xf…

3.5 凸多边形最优三角部分

博主简介&#xff1a;一个爱打游戏的计算机专业学生博主主页&#xff1a; 夏驰和徐策所属专栏&#xff1a;算法设计与分析 1.什么是多边形的三角剖分&#xff1f; 多边形三角剖分是指将多边形分割成互不相交的三角形的弦的集合T。 我的理解&#xff1a; 多边形三角剖分是将给…

uniapp本地存储详解

uniapp本地存储详解 前言 在开发uniapp应用时&#xff0c;我们常常需要使用本地存储来保存一些数据&#xff0c;比如用户登录信息、设置项等&#xff0c;使得应用能够在设备上保存和读取数据&#xff0c;以便提供更好的用户体验和离线功能支持&#xff0c;本文将简单介绍unia…

python编程——编译器与解释器

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 本文专栏&#xff1a;python专栏 专栏介绍&#xff1a;本专栏为免费专栏&#xff0c;并且会持续更新python基础知识&#xff0c;欢迎各位订阅关注。 目录 前言 一、编译器与解释器的介绍 二、编译器与解释器…

JDK1.8下载安装(优雅)

bug虐我千百遍&#xff0c;我待bug如初恋。 这里使用的环境是win11 64位系统&#xff0c;应该适配win8-win11 一、下载 这里提供两种下载方式&#xff0c;官网下载和第三方下载&#xff0c;区别就是下载速度不同 1. 官网下载 &#xff08;1&#xff09;官网下载&#xff1a;…

智慧物流货运系统源码 货运平台的功能介绍

网络货运平台源码 网络货运平台的功能 网络货运是指利用互联网平台&#xff0c;通过物流配送的方式进行商品销售和物流运输的一种新型商业模式。这种模式将传统的货运模式与互联网技术相结合&#xff0c;通过网络平台进行交易、物流配送和结算等一系列商业流程&#xff0c;从而…

用户画像如何创新破局数据驱动增长 | 数据增长

用户画像即用户信息标签化&#xff0c;就是企业通过收集与分析消费者社会属性、生活习惯、消费行为等主要信息的数据之后&#xff0c;完美地抽象出一个用户的商业全貌&#xff0c;是企业应用大数据技术的基本方式。例如&#xff1a;通过收集用户的人口属性、行为属性、消费习惯…

面向初学者的数据科学|要学习的内容概述

面向初学者的数据科学|要学习的内容概述 数据科学家是21世纪最性感的工作。每个人都想变得性感。该领域开始变得竞争激烈&#xff0c;提高了就业标准。 因此&#xff0c;仅仅知道如何使用不同的工具是不够的&#xff0c;求职者需要能够抓住基本的概念和技术&#xff0c;然后应用…

VMware Cloud Foundation 5.0 发布 - 领先的多云平台

VMware Cloud Foundation 5.0 发布 - 领先的多云平台 高效管理虚拟机 (VM) 和容器工作负载。为本地部署的全栈超融合基础架构 (HCI) 提供云的优势。 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-cloud-foundation-5/&#xff0c;查看最新版。原创作品&#xff…

DEMO:F4帮助 收藏夹功能

货铺QQ群号&#xff1a;834508274微信群不能扫码进了&#xff0c;可以加我微信SAPliumeng拉进群&#xff0c;申请时请提供您哪个模块顾问&#xff0c;否则是一律不通过的。进群统一修改群名片&#xff0c;例如BJ_ABAP_森林木。群内禁止发广告及其他一切无关链接&#xff0c;小程…

没有硬件资源?免费使用Colab搭建你自己的Stable Diffiusion在线模型!保姆级教程...

部署 Stable Diffusion 需要一定的硬件资源&#xff0c;具体取决于要处理的图像大小和处理速度等因素。一般来说&#xff0c;至少需要一台具有较高计算能力的服务器&#xff0c;而对 GPU 的高要求就限制了我们学习和使用SD来生成我们想要的图像。 GPU是深度学习开发的重要硬件条…

C++ 学习 ::【基础篇:16】:C++ 类的基本成员函数:拷贝构造函数(认识、特征、注意点及典型使用场景)及其基本写法与调用

本系列 C 相关文章 仅为笔者学习笔记记录&#xff0c;用自己的理解记录学习&#xff01;C 学习系列将分为三个阶段&#xff1a;基础篇、STL 篇、高阶数据结构与算法篇&#xff0c;相关重点内容如下&#xff1a; 基础篇&#xff1a;类与对象&#xff08;涉及C的三大特性等&#…

FastJSON autoType is not support问题解决

概述 产品在使用内部的后台管理系统时反馈的问题。 于是登录平台&#xff0c;发现如下报错详情&#xff1a; 排查 经过分析&#xff0c;不难得知&#xff0c;请求是从gateway网关转发到对应的统计服务 statistics&#xff0c;此服务有个接口/api/statistics/data/overview…

华为OD机试真题 Java 实现【支持优先级的队列】【2023 B卷 100分】

一、题目描述 实现一个支持优先级的队列&#xff0c;高优先级先出队列&#xff0c;同优先级时先进先出。 如果两个输入数据和优先级都相同&#xff0c;则后一个数据不入队列被丢弃。 队列存储的数据内容是一个整数。 二、输入描述 一组待存入队列的数据&#xff08;包含内…