当前位置: 首页 > news >正文

linux系统线程实现原理浅析

背景

对进程和线程的理解,之前一直都是凭一些零碎不完整的信息在理解;

linux的进程和线程基本上一样,线程是轻量级进程,彼此有关联又独立。

得亏内核支持的好,写用户态程序可以不依赖于实现的理解,只需要知道从哪个函数开始就是线程函数了,哪里的变量是全局的,哪里的变量是线程私有的。

这样写出来的程序也不至于有问题。

一直觉得没有把这个东西搞清楚,终于受不了这种煎熬决定看下底层是怎么实现的。

线程/进程历史

linux系统是先有进程的概念,后有线程的概念;

linux内核里面现有的线程实现,其实是redhat这个发行版厂商的实现,合入到linux主线了,redhat的实现就是现在的glibc里面的nptl(Native POSIX Thread Library)加上内核fork共同协作实现的

除了NPTL,还有IBM的开源实现NGPT,后面没有被纳入内核,停止维护;

Linux内核也实现了Linux Threads,对比NPTL优势不明显吧

Linux线程实现概述

用户态(glibc)+内核态组合共同实现

用户态做的事情

线程attribute赋值(如果需要)

线程运行时用户态运行的栈空间分配(包括栈空间的缓存和分配,大小、栈地址)

glibc的线程对象创建以及初始化

内核态做的事情

clone系统调用在调用do_fork时,主要flag有:CLONE_VM | CLONE_FS | CLONE_FILES

CLONE_VM表示和创建线程的进程共享地址空间

CLONE_FS表示和创建线程的进程共享文件系统(根目录、工作目录、文件创建掩码等)

CLONE_FILES表示和创建线程的进程共享打开的文件

创建线程用的clone和创建真进程fork的区别是什么

fork在内核也是调用do_fork函数,fork系统调用在调用do_fork创建新的进程时,只携带了SIGCHLD这一个flag,其实就是不共享地址空间、打开的文件、使用的文件系统等,其它的和clone基本没有区别。

说线程是轻量级进程就是这个原因,和创建它的进程共享了一部分主要资源。

后文把线程、轻量级进程、进程统一称为进程,linux系统里面进程是内核支持的,线程是轻量级进程的别名(我认为这样好理解,没有官方说法支持)

用户态和内核态如何配合达到效果

进程是对CPU的虚拟化,CPU是用来执行二进制程序的,二进制可以是用户态也可以是内核态;也就是进程既可以执行用户态代码,也可以执行内核态代码,进程是一个逻辑概念,内核态/用户态代码是表示运行指令时CPU处在不同的保护模式(ring0还是ring3)

进程是由内核态创建,进程主要作用是做为调度一员,获取CPU时间片、地址空间、文件系统、打开文件、进程权限、用户权限、cpu亲和性、调度算法、进程组、运行时间、命名空间等信息(其它信息可以看task_struct的定义)

进程如何创建和运行起来

long do_fork(...)
{struct task_struct *p;p = copy_process(clone_flags, stack_start, stack_size,child_tidptr, NULL, trace, NUMA_NO_NODE);wake_up_new_task(p);}

依据clone_flags(用户态或者系统调用时添加的flags)的不同使用copy_process创建一个新的进程p;

新创建的进程p执行了wake_up_new_task后就会被调度进程调度运行,进程被调度就会分配CPU运行。

如何执行用户态线程函数

用户态线程函数如何触发执行

glibc里面pthread_create在X86_64的实现里面会调用__clone函数(clone.S,clone的实现之一)

1、用户线程函数地址调用pthread_create后如何保存

用户调用glibc函数创建线程时传递下来的fn地址是放在rdi这个寄存器(clone.S注释有说明,应该也是遵循ABI函数调用堆栈参数保存约定的)。

注意这里的fn其实是glibc的start_thread函数,这个函数再调用户的线程函数,指针保存在start_routine成员里面,具体代码如下

if (pd->c11){/* The function pointer of the c11 thread start is cast to an incorrecttype on __pthread_create_2_1 call, however it is casted back to correctone so the call behavior is well-defined (it is assumed that pointersto void are able to represent all values of int.  */int (*start)(void*) = (int (*) (void*)) pd->start_routine;ret = (void*) (uintptr_t) start (pd->arg);}
elseret = pd->start_routine (pd->arg);

2、fn如何被调用

fn是用户态地址,确实没有必要传递到内核函数里面在内核态调用,这样就牵扯到地址转换了

那如何调用?在clone系统调用之前,把fn地址压到子进程的栈里面,系统调用返回后,再弹出地址,通过call执行函数,相关代码为:

ENTRY (__clone)......movq	%rdi,0(%rsi)............movl	$SYS_ify(clone),%eax....../* End FDE now, because in the child the unwind info will bewrong.  */cfi_endproc;syscalltestq	%rax,%raxjl	SYSCALL_ERROR_LABELjz	L(thread_start)retL(thread_start):popq	%rax		/* Function to call.  */popq	%rdi		/* Argument.  */call	*%rax

内核态如何找到用户态的栈

glibc创建栈空间后,栈起始地址以及栈大小会通过系统调用的参数传递到内核

(如上面copy_process函数的第二个和第三个参数)

接下来内核调用copy_thread(clone_flags, stack_start, stack_size, p)(不同的架构对应不同的实现)

*childregs = *current_pt_regs();childregs->ax = 0;
if (sp)childregs->sp = sp;

这里对应的是X86_64的实现,可以看到把用户态传下来的栈指针设置到栈寄存器里面

这里有个点,这个sp和上面保存fn的栈是一个吗?是用户态分配的栈吗?

我认为是的,这个是已经push过参数和fn之后的栈。

其它

为什么线程的栈要用户分配在用户态?

A:栈要么分在用户态,要么分在内核态

如果分在用户态,线程主循环函数运行在用户态,都是在用户态,就不需要CPU模式切换,运行速度更快,都是用户态代码也不存在对内核的安全问题

如果分在内核态,那用户态函数执行,函数执行要不停操作栈,那至少就会有内核地址到用户态地址的转换,要不要牵扯模式切换就另说了。

http://www.xdnf.cn/news/280189.html

相关文章:

  • 企业架构革新指南:中台的定义、实践与未来
  • 嵌入式复习第二章
  • 修复笔记:SkyReels-V2项目中的 from_config 警告
  • 历史观以及文化和文明的相关知识
  • 序列到序列学习
  • 软件测试报告机构如何保障软件质量并维护其安全性?
  • Vultr之Ubuntu重设密码
  • 湖北理元理律师事务所:债务优化的合规化探索
  • 2025年- H26-Lc134- 226. 翻转二叉树(树)---java版
  • Java学习手册:SQL 优化技巧
  • 正态分布习题集 · 答案与解析篇
  • LabVIEW比例阀性能自动测试
  • 【Redis】哈希(hash)与列表(list)
  • 【SimSession 】2:PacedReceiver:支持与 PacedVideoSender 本地联调
  • PostgreSQL 的 REINDEX 命令
  • 装饰模式(Decorator Pattern)
  • 【C++】运算符重载
  • 图片压缩与尺寸调整的便捷工具推荐
  • 主成分分析(PCA)与逻辑回归在鸢尾花数据集上的实践与效果对比
  • 【翻译、转载】MCP 工具 (Tools)
  • 【python实用小脚本-47】用Python打造高效的信息推送系统:从问题到解决方案的实战之旅
  • 【默子AI】Anthropic Claude LLM对齐伪装 解读
  • Temp Mail 1.7.0 | 创建和管理临时邮箱,防止垃圾邮件骚扰,保护隐私安全
  • 高效便捷的定时关机与任务管理工具
  • Java学习手册:MyBatis 框架作用详解
  • 【循环依赖(Circular Dependency)】
  • 2025信息安全网络安全意识培训资料汇编(24份)
  • Day 4:牛客周赛Round 91
  • 力扣刷题(第十六天)
  • Mamba+Attention+CNN 预测模型:破局长程依赖的计算机视觉新范式