线程是如何创建的

news2024/9/26 5:20:00

线程不是一个完全由内核实现的机制,它是由内核态和用户态合作完成的。pthread_create 不是一个系统调用,是 Glibc 库的一个函数,所以我们还要去 Glibc 里面去找线索。

首先处理的是线程的属性参数。例如前面写程序的时候,我们设置的线程栈大小。如果没有传入线程属性,就取默认值。

const struct pthread_attr *iattr = (struct pthread_attr *) attr;
struct pthread_attr default_attr;
if (iattr == NULL)
{
  ......
  iattr = &default_attr;
}

接下来,就像在内核里一样,每一个进程或者线程都有一个 task_struct 结构,在用户态也有一个用于维护线程的结构,就是这个 pthread 结构。

struct pthread *pd = NULL;

凡是涉及函数的调用,都要使用到栈。每个线程也有自己的栈。那接下来就是创建线程栈了。

int err = ALLOCATE_STACK (iattr, &pd);

ALLOCATE_STACK 是一个宏,我们找到它的定义之后,发现它其实就是一个函数。allocate_stack 主要做了以下这些事情:

  • 如果你在线程属性里面设置过栈的大小,需要你把设置的值拿出来;
  • 为了防止栈的访问越界,在栈的末尾会有一块空间 guardsize,一旦访问到这里就错误了;
  • 其实线程栈是在进程的堆里面创建的。如果一个进程不断地创建和删除线程,我们不可能不断地去申请和清除线程栈使用的内存块,这样就需要有一个缓存。get_cached_stack 就是根据计算出来的 size 大小,看一看已经有的缓存中,有没有已经能够满足条件的;
  • 如果缓存里面没有,就需要调用 __mmap 创建一块新的,系统调用那一节我们讲过,如果要在堆里面 malloc 一块内存,比较大的话,用 __mmap;
  • 线程栈也是自顶向下生长的,还记得每个线程要有一个 pthread 结构,这个结构也是放在栈的空间里面的。在栈底的位置,其实是地址最高位;
  • 计算出 guard 内存的位置,调用 setup_stack_prot 设置这块内存的是受保护的;
  • 接下来,开始填充 pthread 这个结构里面的成员变量 stackblock、stackblock_size、guardsize、specific。这里的 specific 是用于存放 Thread Specific Data 的,也即属于线程的全局变量;
  • 将这个线程栈放到 stack_used 链表中,其实管理线程栈总共有两个链表,一个是 stack_used,也就是这个栈正被使用;另一个是 stack_cache,就是上面说的,一旦线程结束,先缓存起来,不释放,等有其他的线程创建的时候,给其他的线程用。

真正创建线程的是调用 create_thread 函数,这个函数定义如下:

static int
create_thread (struct pthread *pd, const struct pthread_attr *attr,
bool *stopped_start, STACK_VARIABLES_PARMS, bool *thread_ran)
{
  const int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SYSVSEM | CLONE_SIGHAND | CLONE_THREAD | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | 0);
  ARCH_CLONE (&start_thread, STACK_VARIABLES_ARGS, clone_flags, pd, &pd->tid, tp, &pd->tid);
  /* It's started now, so if we fail below, we'll have to cancel it
and let it clean itself up.  */
  *thread_ran = true;
}

如果在进程的主线程里面调用其他系统调用,当前用户态的栈是指向整个进程的栈,栈顶指针也是指向进程的栈,指令指针也是指向进程的主线程的代码。此时此刻执行到这里,调用 clone 的时候,用户态的栈、栈顶指针、指令指针和其他系统调用一样,都是指向主线程的。

但是对于线程来说,这些都要变。因为我们希望当 clone 这个系统调用成功的时候,除了内核里面有这个线程对应的 task_struct,当系统调用返回到用户态的时候,用户态的栈应该是线程的栈,栈顶指针应该指向线程的栈,指令指针应该指向线程将要执行的那个函数。

创建进程的话,调用的系统调用是 fork,在 copy_process 函数里面,会将五大结构 files_struct、fs_struct、sighand_struct、signal_struct、mm_struct 都复制一遍,从此父进程和子进程各用各的数据结构。而创建线程的话,调用的是系统调用 clone,在 copy_process 函数里面, 五大结构仅仅是引用计数加一,也即线程共享进程的数据结构。

此文章为10月Day30学习笔记,内容来源于极客时间《趣谈Linux操作系统》,推荐该课程。

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

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

相关文章

一些关于weex编译apk的环境配置记录,成功打包weex app

背景 使用weex编译安卓app。老是会遇到一些配置问题,导致编译apk异常。 最后重新下载Android studio,配置好环境,再使用官网的命令,就编译成功了,下面是记录。 编译成功的环境 Java版本:jdk1.8.0_241 And…

深度学习_2 数据操作

数据操作 机器学习包括的核心组件有: 可以用来学习的数据(data);如何转换数据的模型(model);一个目标函数(objective function),用来量化模型的有效性&…

AB试验(七)利用Python模拟A/B试验

AB试验(七)利用Python模拟A/B试验 到现在,我相信大家理论已经掌握了,轮子也造好了。但有的人是不是总感觉还差点什么?没错,还缺了实战经验。对于AB实验平台完善的公司 ,这个经验不难获得&#…

记录一段帮朋友写的代码,使用牛顿-拉夫逊方法解方程

要求 已知公式: t G A B F r B r 2 2 F A 2 B G A F ln ⁡ ( r − A ) C o n s t t\frac{GAB}{F}r\frac{Br^2}{2F}\frac{A^2BGA}{F}\ln (r-A)Const tFGAB​r2FBr2​FA2BGA​ln(r−A)Const 其中 t 的值为0-1000,每间隔25取一次值A2.12941E-10B0.…

源码!游戏源码!大量游戏源码!!!知识星球

分享 不在游戏公司有段时间了,但是还一直在游戏圈,多年的游戏经验也做了一些总结,还有一些私货源码没有做分享,所以也跟着玩一玩知识星球。 源码来自哪 1、来自网上搜集 各种开源网站,比如github ,gitee平常会注意搜…

Windows内存取证-中等难度 -上篇

涉及的工具: SysInfoTools-ost-viewer-pro volatility_2.6_lin64_standalone VT在线工具 使用到的镜像文件: target1-1dd8701f.vmss target2-6186fe9f.vmss POS-01-c4e8f786.vmss 题干: 一名员工报告说,他的机器在收到一封可…

【深度学习docker】roop-unleashed的docker镜像,deepfake AI换脸

快速启动roop-unleashed 要求有支持CUDA11.8的显卡Linux Docker。 使用docker启动roop-unleashed: docker run -d --gpus all -p 7860:7860 kevinchina/deeplearning:roop-unleashed1访问: 制作工程使用的一些脚本 基础镜像: FROM nvidi…

基于SpringBoot的在线笔记系统

技术介绍 🔥采用技术:SpringSpringMVCMyBatisJSPMaven 🔥开发语言:Java 🔥JDK版本:JDK1.8 🔥服务器:tomcat 🔥数据库:mysql 🔥数据库开发工具&…

时间序列预测大模型-TimeGPT

时间序列预测领域正在经历一个非常激动人心的时期。仅在过去的三年里,我们就看到了许多重要的贡献,例如N-BEATS、N-HiTS、PatchTST和TimesNet。 与此同时,大型语言模型 (LLM)最近在 ChatGPT 等应用程序中广受欢迎,因为它们无需进…

LoadRunner实现接口测试

接口测试的原理是通过测试程序模拟浏览器向服务器发送请求报文,服务器接收请求报文后对相应的报文做出处理然后再把应答报文发送给浏览器,浏览器接收应答报文这一个过程。 LoadRunner是一种性能测试工具,但是它也可以用来做接口测试。开发人…

kubernetes实验挑战二(troubleshoot pv pvc )

This 2-Node Kubernetes cluster is broken! Troubleshoot, fix the cluster issues and then deploy the objects according to the given architecture diagram to unlock our Image Gallery!! 1、 kubeconfig /root/.kube/config, User ‘kubernetes-admin’ Cluster: S…

基于乌鸦算法的无人机航迹规划-附代码

基于乌鸦算法的无人机航迹规划 文章目录 基于乌鸦算法的无人机航迹规划1.乌鸦搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要:本文主要介绍利用乌鸦算法来优化无人机航迹规划。 1.乌鸦搜索算法 …

昂首资本严肃且专业地探讨波浪理论第一波

很多投资者已经了解了波浪理论第一波,今天昂首资本和各位投资者再加深一下理解,让我们严肃且专业地探讨一下第一波。 以小时价格图表举例,第一波的起始点存在一个看涨反转棒。请注意,这个棒形结构对应了比尔威廉姆斯交易策略三智…

识别flink的反压源头

背景 flink中最常见的问题就是反压,这种情况下我们要正确的识别导致反压的真正的源头,本文就简单看下如何正确识别反压的源头 反压的源头 首先我们必须意识到现实中轻微的反压是没有必要去优化的,因为这种情况下是由于偶尔的流量峰值,Task…

Android---如何同view进行渲染

ViewRootImpl 在 Activity、window 和 View 三者关系之间起着承上启下的作用。一方面,ViewRootImpl 中通过 Binder 通信机制,远程调用 WindowSession 将 View 添加到 Window 中;另一方面,ViewRootImpl 在添加 View 之前&#xff0…

centos7安装配置及Linux常用命令

目录 前言 一、centos7操作系统 1.centos7简介 2.centos7的安装及配置 3.配置centos的镜像 4.虚拟机开机初始设置 5.查看、设置IP地址 二、MobaXterm工具的使用 1.MobaXterm简介 2.MobaXterm安装 3.MobaXterm的使用 4.切换国内源 三、Linux常用命令 1.查看网络…

合肥中科深谷嵌入式项目实战——人工智能与机械臂(四)

订阅:新手可以订阅我的其他专栏。免费阶段订阅量1000 python项目实战 Python编程基础教程系列(零基础小白搬砖逆袭) 作者:爱吃饼干的小白鼠。Python领域优质创作者,2022年度博客新星top100入围,荣获多家平台专家称号。…

后门程序分析1

临时补充一个内容,这是一个后门程序,通过IDA分析,之后把里面收集的信息点全部整理出来(包括:反虚拟机,系统信息等等)pass:guet 用IDA打开先看看主函数的样子 查阅一些这些API InternetOpenA&…

C++入门06—结构体

1. 结构体基本概念 结构体属于用户自定义的数据类型,允许用户存储不同的数据类型 2. 结构体定义和使用 语法:struct 结构体名 { 结构体成员列表 }; 通过结构体创建变量的方式有三种(相当于python中的实例化对象): struct 结构体…

图像无损放大画质修复工具 Topaz Photo AI「Mac」

Topaz Photo AI是一款适用于Mac的图像处理软件,它使用人工智能技术对照片进行编辑和优化。该软件提供了多种强大的功能,帮助用户轻松地改善图像质量,并实现自定义的效果。 Topaz Photo AI支持多种文件格式,包括JPEG、TIFF、PNG、R…