OK6410A 开发板 (七) 6 buildroot-2021.02 OK6410A glibc 中的线程库 NPTL与LinuxThreads

linux-2.4.18及以前, 不支持内核线程("轻量级进程"作为线程的实现),用户空间用LinuxThreads 库 支持线程,此时的线程模型为 1:1
linux-2.4.20及以前, 不支持内核线程("轻量级进程"作为线程的实现),用户空间用NPTL线程库       支持线程,此时的线程模型为 1:1

注意 : N:1模型 是 Portable Thread (用户空间实现)

我们讨论的模型,以linux-2.4.20 为时间点,时间点后
linux 和 用户线程库 都发生了改变
我们关注这些改变

linux-2.4.20以前的代码 +  LinuxThreads库 	为 线程1.0(LinuxThreads ) , 线程模型为1:1
linux-2.4.20以后的代码 +  NPTL库 		 	为 线程2.0(NPTL) 		 , 线程模型为1:1

线程1.0 LinuxThreads
  • linux
当初 Linux 被开发出来的时候,其内核并不真正地支持多线程。



进程 A 是 是一个进程
由于 clone 系统调用创建了调用进程的副本,而且可以和父进程共享地址空间;
通过 clone,LinuxThreads 完全在用户空间模拟了线程。
进程A使用(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)参数来调用clone()创建"线程",表示共享内存、共享文件系统访问计数、共享文件描述符表,以及共享信号处理方式。
创建除了轻量级进程B


然而,这种方法有很多的缺陷,并没有符合 POSIX 的要求,特别是在信号处理,调度和进程间同步原语等方面
	1. 对于SMP系统,所有的进程fork出来后,都被分配到与父进程相同的cpu上,一直到该进程被调度时才会进行cpu选择。
	2. linux 线程的 实际 实现为 轻量级进程 , 轻量级进程与进程处于同一个调度层次.
	3. 进程id不同.
		// 最根本的问题
	4. 线程的调度由内核管理
	5. 线程其他的管理(第N次的线程创建,线程取消、线程间的同步)在核外函数库中实现,(因为共享同一个进程标识符,所以无法在内核中管理) 
		// 专门为每一个进程(所有的线程)构造了一个管理线程,负责处理线程相关的管理工作
		// 当进程第一次调用pthread_create()创建一个线程的时候就会创建(__clone())并启动管理线程。
		// 进程第二(N)次调用pthread_create// pthread_create()向管理线程发送REQ_CREATE请求之后,管理线程即调用pthread_handle_create()创建新线程
		// 每个LinuxThreads线程都同时具有线程id和进程id,其中进程id就是内核所维护的进程号,而线程id则由LinuxThreads分配和维护
		// 一旦管理线程死亡,用户线程就不得不手工清理了
	6. 信号的管理
		// 由于异步信号是内核以进程为单位分发的,而LinuxThreads的每个线程对内核来说都是一个进程,且没有实现"线程组",因此,某些语义不符合POSIX标准,比如没有实现向进程中所有线程发送信号,README对此作了说明。
	7.线程总数
		// LinuxThreads将每个进程的线程最大数目定义为1024,但实际上这个数值还受到整个系统的总进程数限制,这又是由于线程其实是核心进程。
	
	8.线程管理
		管理线程容易成为瓶颈,这是这种结构的通病;同时,管理线程又负责用户线程的清理工作,因此,尽管管理线程已经屏蔽了大部分的信号,但一旦管理线程死亡,用户线程就不得不手工清理了,而且用户线程并不知道管理线程的状态,之后的线程创建等请求将无人处理。
	

  • LinuxThreads 用户线程库
每个LinuxThreads线程都同时具有线程id和进程id,其中进程id就是内核所维护的进程号,而线程id则由LinuxThreads分配和维护
管理线程
线程2.0 NPTL
  • linux

LinuxThreads  最根本的问题 的 解决
	在linux 2.6, 内核有了线程组的概念,task_struct结构中增加了一个tgid(thread group id)字段.
	
	如果这个task是一个”主线程”, 则它的tgid等于pid, 否则tgid等于进程的pid(即主线程的pid).
	
	在clone系统调用中, 传递CLONE_THREAD参数就可以把新进程的tgid设置为父进程的tgid(否则新进程的tgid会设为其自身的pid).
	
	类似的XXid在task_struct中还有两个:task->signal->pgid保存进程组的打头进程的pid、task->signal->session保存会话打头进程的pid。通过这两个id来关联进程组和会话。
	
	有了tgid, 内核或相关的shell程序就知道某个tast_struct是代表一个进程还是代表一个线程, 也就知道在什么时候该展现它们, 什么时候不该展现(比如在ps的时候, 线程就不要展现了).getpid(获取进程ID)系统调用返回的也是tast_struct中的tgid,而tast_struct中的pid则由gettid系统调用来返回.

在执行ps命令的时候不展现子线程,也是有一些问题的。比如程序a.out运行时,创建了一个线程。假设主线程的pid是10001、子线程是10002(它们的tgid都是10001)。这时如果你kill 10002,是可以把1000110002这两个线程一起杀死的,尽管执行ps命令的时候根本看不到10002这个进程。

在200289月份,一直不肯松劲的Linus Torvalds先生终于被说服了,Ingo Molnar把一些重要特性加入到2.5开发版官方内核中。
这些特性大体包括:新的clone系统调用,TLS系统调用,posix线程间信号,exit_group(exit的一个变体),等等。

同步问题的解决
	使用 NPTL 线程库与新内核实现,就可以避免使用信号来对线程进行同步了。
	为了这个目的,NPTL 引入了一种名为 futex 的新机制。
	futex 在共享内存区域上进行工作,因此可以在进程之间进行共享,这样就可以提供进程间 POSIX 同步机制。我们也可以在进程之间共享一个 futex。
	这种行为使得进程间同步成为可能。
	实际上,NPTL 包含了一个 PTHREAD_PROCESS_SHARED 宏,使得开发人员可以让用户级进程在不同进程的线程之间共享互斥锁。

新引入问题的解决
	兼容性
		NPTL 线程库所引入的一个实现特性是对 ABI(应用程序二进制接口)的支持。
		这帮助实现了与 LinuxThreads 的向后兼容性。
		这个特性是通过使用 LD_ASSUME_KERNEL 实现的,下面就来介绍这个特性。
		老代码(linuxthreads)也可以用在 NPTL 系统上

  • NPTL用户线程库
有了OS的支持,Ingo Molnar先生同Ulrich Drepper(glibc的LinuxThreads库的维护者,NPTL的设计者与维护者,现工作于RedHat公司)
和其他一些Hackers开始NPTL的完善工作。

NPTL没有使用管理线程,核心线程的管理直接放在核内进行,这也带了性能的优化。


NPTL的设计目标归纳可归纳为以下几点:

	POSIX兼容性
	SMP结构的利用
	低启动开销
	低链接开销(即不使用线程的程序不应当受线程库的影响)
	与LinuxThreads应用的二进制兼容性
	软硬件的可扩展能力
	多体系结构支持
	NUMA支持
	与C++集成


其他
  • 线程1.0和线程2.0的ABI兼容

  • 如何判断当前环境是 线程1.0还是线程2.0
getconf GNU_LIBPTHREAD_VERSION
  • 线程1.0 与 posix 标准
LinuxThreads 并不符合 POSIX 在线程方面的标准

  • 线程2.0 与 posix 标准
原生 POSIX 线程库(Native POSIX Thread Library,NPTL)比 LinuxThreads 更符合标准
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值