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> # ...
随机推荐
- CentOS下搭建nginx+php环境
一.下载安装nginx 参见 http://www.cnblogs.com/kreo/p/4378086.html 不再赘述 二.下载php #下载 wget http://bg2.php.net/d ...
- RabbitMQ使用相关笔记
#运行各示例脚本 [1] 1. 下载各语言的示例代码 https://github.com/rabbitmq/rabbitmq-tutorials 2. 安装pip,命令"yum -y in ...
- [daily] 宇宙终极shell之zsh
序 很久以前,我就知道zsh.后来在并不遥远的好几年前,第一次装arch的时候,默认shell就是zsh.虽然用起来不太习惯,但是仍然蛮好用的. 决定好好的学一下zsh,如果可以就切换过去,毕竟bas ...
- 用Appium进行android自动化测试
appium是开源的移动端自动化测试框架,可以测试ios,android应用.appium让移动端自动化测试不必限定在某种语言和某个具体的框架:也就是说任何人都可以使用自己最熟悉最顺手的语言以及框架来 ...
- vi全局替换
http://blog.csdn.net/vindak/article/details/7830547 vi全局替换命令
- Linux配置SSH免密码登陆
配置环境: 两台centos 6.4虚拟机,/etc/hosts配置如下 192.168.63.128 hadoop001 --master192.168.63.131 hadoop002 --sla ...
- 一些常见maven仓库
<repositories> <repository> <id>spring-releases</id> <url>https://repo ...
- 分享大家一个背景为下雪的JQuery
<html><head> <meta charset="utf-8"> <meta content="IE=edge,chrom ...
- property_自己编写一个读取Property文件的Util类
读取property文件的Util类: 所需jar包: 编写PropertiesUtil类: package com.west.util.property; import java.io.InputS ...
- 如何保存CONSOLE LOG
很早以前就想过问题,但因为我们一直有自动化程序去做,所以没仔细研究. 今天学习了一下,其实很简单,在sdsf.da 里面搜索master会看到下面这样一个TASK *MASTER* STC0000 ...