Linux线程的实现 & LinuxThread vs. NPTL & 用户级内核级线程 & 线程与信号处理
另,线程的资源占用可见:http://www.cnblogs.com/charlesblc/p/6242111.html
进程 & 线程的很多知识可以看这里:http://www.cnblogs.com/charlesblc/p/6135666.html
线程一直是分系统级线程和用户级线程,也就是所谓的 1:1线程模型和 1:n线程模型。注意Linux2.4版本之前pthread用的LinuxThread实现,和Linux2.5以后pthread用的NPTL(据说比较好支持了POSIX线程标准),都是系统级别的1:1线程模型,都是系统级线程。
具体可以参考这篇文章:http://blog.csdn.net/ixidof/article/details/24579879
LinuxThread的内核对应的管理实体就是进程,又称LWP(轻量级进程),每个线程的pid是不一样的。
重要的是系统调用clone(),大家熟知的fork()函数就是调用clone()来实现父进程拷贝的从而创建一个新进程的。系统调用clone()里有一个flag参数,这个参数有很多的标志位指定了克隆时需要拷贝的东西,其中标志位CLONE_VM就是定义拷贝时是否使用相同的内存空间。fork()调用clone()时没有设置CLONE_VM,所以在内核看来就是产生了两个拥有不同内存空间的进程。而pthread_create()里调用clone()时设置了CLONE_VM,所以在内核看来就产生了两个拥有相同内存空间的进程。所以用户态创建一个新线程,内核态就对应生成一个新进程。
同步互斥
内核没有提供任何对线程的支持,当然也就没有可供线程同步互斥使用的系统原语,但POSIX的线程标准里要求了诸多的互斥同步接口,怎么办呢?
LinuxThread使用信号来模拟同步互斥,比如互斥锁,大致过程我猜如下:新建互斥锁的时候,在内核里把所有的进程mask掉一个特定信号,然后再kill()发出一个信号,等某个线程执行锁定时,就用sigwait()查看是否有发出的信号,如果没有就等待,有则返回,相当于锁定。解锁时就再kill()发出这个信号。那么LinuxThread使用的是哪几个信号来模拟这个同步互斥的呢?有的文档说是SIGUSR1和SIGUSR2,也有的说是某几个实时信号,具体可以看对应线程库的开发手册。必须知道你所使用的线程库内部使用哪几个信号,因为如果你的多线程程序里也使用了这几个信号的话,就会导致线程API工作混乱。
从行分析就可以得出,LinuxThrea的同步互斥是用信号模拟完成的,所以效率不高且可能影响原有进程的信号处理,确实是个很大的缺陷。
信号处理
LinuxThread的信号处理的行为可以说跟POSIX的标准是完全不一致的。因为信号的投递过程是发生在内核的,而每个线程在内核都是对应一个个单独的进程(不理解请看LinuxThread的创建线程一节),所以没有内核支持,所以当你对一个进程发送一个信号后,只有拥有这个进程号的进程才有反应,而属于这个进程的线程因为拥有不同的进程号而无法做出响应,从而LinuxThread无法做到跟POSIX定义的行为一致。
线程管理
这里不得不说到LinuxThread的一个特性,当你创建第一个线程时,也就会自动创建一个管理线程,这个过程对用户是透明的。所以如果你还在使用LinuxThread线程库,当你创建一个线程后ps的结果会是有三个相同的进程而不是两个。这个管理线程的主要作用是管理线程的创建与终止,所以如果你把这个管理线程kill掉后,当你的某个线程退出后就会出现Zombie进程。另外,因为线程的创建与终止都要通过这个管理线程,在一个频繁创建与终止线程的程序这个线程很可能成为性能的瓶颈。
Native POSIX Thread Library(NPTL)
因为没有内核支持的LinuxThread的线程实现的诸多缺陷,所以要想实现完全跟POSIX线程标准兼容的线程库,重写线程库是必然的,内核的修改也势在必行。有关NPTL实现也从线程创建,同步互斥及信号处理及线程管理几个方面来说明。
创建线程
NPTL同样使用的是1 * 1模型,但此时对应内核的管理结构不再是LWP了。为了管理进程有进程组的概念,那内核要管理线程提出线程组的概念就是很自然的了。Linux内核只是在原来的进程管理结构新增了一个TGIP的字段,如下图。当一个线程的PID等于TGID时,这个线程就是线程组长,其PID也就是这个线程组的进程号。线程组内的所有线程的TGID字段都指向线程组长的PID,当你使用getpid返回的都是TGID字段,而线程号返回的就是PID字段。那么NPTL下线程又是如何创建线程的呢?同样是使用clone()系统调度,不过新的clone()调用的flag参数新增了一个标志位CLONE_THREAD,当这个标志位设置的时候新创建的行为就是创建一个线程,内核内部初始管理结构时把TGID指向调用者的PID,原来的PID位置填新线程号(也就是以前的进程号)。
从上,LinuxThread因为在内核是一个LWP而产生的跟POSIX标准不兼容的错误都消除了。
注意图中,用户态和内核态的划分。
同步与互斥
从LinuxThread中的线程同步与互斥中可看到使用信号来模拟的缺点,所以内核增加一个新的互斥同步原语futex(fast usesapace locking system call),意为快速用户空间系统锁。因为进程内的所有线程都使用了相同的内存空间,所以这个锁可以保存在用户空间。这样对这个锁的操作不需要每次都切换到内核态,从而大大加快了存取的速度。NPTL提供的线程同步互斥机制都建立在futex上,所以无论在效率上还是咋对程序的外部影响上都比LinuxThread的方式有了很大的改进。具体futex的描述可以man futex。
信号处理
此时因为同一个进程内的线程都属于同一个进程,所以信号处理跟POSIX标准完全统一。当你发送一个SIGSTP信号给进程,这个进程的所有线程都会停止。因为所有线程内用同样的内存空间,所以对一个signal的handler都是一样的,但不同的线程有不同的管理结构所以不同的线程可以有不同的mask。后面这一段对LinuxThread也成立。
管理线程
线程创建与结束的管理都由内核负责了,由LinuxThread的管理线程机制引出的问题已不复存在了。当然系统调度上仍是一个单独的线程而不是多个线程组成一个进程为整体进行调度的。这跟POSIX的标准还是稍有不同,不过这一缺点看起来无伤大雅。
注:从这一段可以看出:Linux是以线程为单位来调度。而POSIX貌似是要求以进程为单位来调度。
注:POSIX:Portable Operating System Interface
另外,看到网上很多文章说,POSIX线程是混合模型,有用户级和系统级线程,通过一个参数来选择。等等。我是这样理解的:
POSIX只是一个协议,各个系统的实现不一样。我只知道LInux的线程是系统级线程,是内核参与调度的,是操作系统可见的。不管LinuxThread还是NPTL都是这样的。
线程与信号处理
关于线程与信号处理的关系,还要再重点说一下。
参考这篇文章:http://www.cnblogs.com/cobbliu/p/5592659.html
Linux线程的实现 & LinuxThread vs. NPTL & 用户级内核级线程 & 线程与信号处理的更多相关文章
- Linux时间子系统之四:Timer在用户和内核空间流程
用户空间应用中创建一个Timer(alarm/setitimer/POSIX Timer等等),然后程序继续执行: 内核进入创建/设置Timer系统调用,开始计时,在超时后通过何种方式通知用户空间: ...
- 操作系统学习笔记5 | 用户级线程 && 内核级线程
在上一部分中,我们了解到操作系统实现多进程图像需要组织.切换.考虑进程之间的影响,组织就是用PCB的队列实现,用到了一些简单的数据结构知识.而本部分重点就是进程之间的切换. 参考资料: 课程:哈工大操 ...
- 多线程 用户级线程和内核级线程 from C++多核高级编程
转 http://book.51cto.com/art/201006/206946.htm 6.1.1 用户级线程和内核级线程 2010-06-21 20:37 齐宁/董泽惠 译 清华大学出版社 字号 ...
- 内核级线程(KLT)和用户级线程(ULT)
内核级线程(KLT)和用户级线程(ULT) tags: KLT ULT 内核级线程 用户级线程 引言:本文涉及到操作系统的内核模式和用户模式,如果不太懂的话,可以参看我的这篇文章内核模式和用户模式,其 ...
- linux0.11内核源码——用户级线程及内核级线程
参考资料:哈工大操作系统mooc 用户级线程 1.每个进程执行时会有一套自己的内存映射表,即我们所谓的资源,当执行多进程时切换要切换这套内存映射表,即所谓的资源切换 2.但是如果在这个进程中创建线程, ...
- Linux用户级线程和内核级线程区别
1.内核级线程: (1)线程的创建.撤销和切换等,都需要内核直接实现,即内核了解每一个作为可调度实体的线程.(2)这些线程可以在全系统内进行资源的竞争.(3)内核空间内为每一个内核支持线程设置了一个线 ...
- {Python之线程} 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Threading模块 九 锁 十 信号量 十一 事件Event 十二 条件Condition(了解) 十三 定时器
Python之线程 线程 本节目录 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Thr ...
- linux内核--进程与线程
http://blog.csdn.net/yusiguyuan/article/details/12154823 在<linux内核设计与实现>中第三章讲解了进程管理,在关于进程和线程的概 ...
- Linux内核:kthread_create(线程)、SLEEP_MILLI_SEC
转自:http://blog.csdn.net/guowenyan001/article/details/39230181 一.代码 #include <linux/module.h> # ...
随机推荐
- jQuery获取一般处理程序(ashx)的JSON数据
昨天有在开发的软件生产线生产流程,RFID扫描IC卡的数据,当中有用到jQuery获取一般处理程序(ashx)的JSON数据.今有把它写成一个小例子,望需要的网友能参考. 在网站中,创建一个一般应用程 ...
- 请求量限制方法-使用本地Cache记录当前请求量[坑]
有个需求:需要限制每个账户请求服务器的次数(该次数可以配置在DB,xml文件或其他).单位:X次/分钟.若1分钟内次数<=X 则允许访问,1分钟内次数>X则不再允许访问. 这类需求很常 ...
- gradlew解决jar或class冲突
以LeanCloud的推送sdk为例. 我的项目中使用了android-async-http库和fastjson的库,然后LeanCloud的的sdk中也使用了这两个库,但是版本有点低. 处理方式: ...
- ++i 与 i++ 区别
i++返回原来的值 ++i 返回i+1的值 但是i++ i的值也会增加1 但是返回还是原来的值 int i = 1; i = i++; System.out.println(i); 输出 1 i ...
- git如何使用 svn如何使用
git和svn是2款常用的版本控制系统. git 的功能: 1.从服务器上克隆完整的Git仓库(包括代码和版本信息)到单机上. 也就是说自己机器上有一个git仓库. 这和svn是不同的,svn是没有本 ...
- on-my-zsh agnoster 主题设置问题
安装Menlo-Powerline字体补丁 https://gist.github.com/qrush/1595572/raw/417a3fa36e35ca91d6d23ac96107... 然后 c ...
- copy file to docker from realhost
http://blog.e3rp4y.me/blog/2014/05/23/copy-file-from-host-to-docker.html --------------------------- ...
- this 的工作原理
JavaScript 有一套完全不同于其它语言的对 this 的处理机制. 在五种不同的情况下 ,this 指向的各不相同. 全局范围内 this; 当在全部范围内使用 this,它将会指向全局对象. ...
- ZOJ 3781 Paint the Grid Reloaded(BFS)
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3781 Leo has a grid with N rows an ...
- C语言 在VS环境下一个很有意思的报错:stack around the variable was corrupted
今天做一个很简单的oj来温习下c 语言 题目如下 输入 3位正整数 输出 逆置后的正整数 代码如下: #include"stdio.h"int main(){ float h,su ...