另,线程的资源占用可见: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 & 用户级内核级线程 & 线程与信号处理的更多相关文章

  1. Linux时间子系统之四:Timer在用户和内核空间流程

    用户空间应用中创建一个Timer(alarm/setitimer/POSIX Timer等等),然后程序继续执行: 内核进入创建/设置Timer系统调用,开始计时,在超时后通过何种方式通知用户空间: ...

  2. 操作系统学习笔记5 | 用户级线程 && 内核级线程

    在上一部分中,我们了解到操作系统实现多进程图像需要组织.切换.考虑进程之间的影响,组织就是用PCB的队列实现,用到了一些简单的数据结构知识.而本部分重点就是进程之间的切换. 参考资料: 课程:哈工大操 ...

  3. 多线程 用户级线程和内核级线程 from C++多核高级编程

    转 http://book.51cto.com/art/201006/206946.htm 6.1.1 用户级线程和内核级线程 2010-06-21 20:37 齐宁/董泽惠 译 清华大学出版社 字号 ...

  4. 内核级线程(KLT)和用户级线程(ULT)

    内核级线程(KLT)和用户级线程(ULT) tags: KLT ULT 内核级线程 用户级线程 引言:本文涉及到操作系统的内核模式和用户模式,如果不太懂的话,可以参看我的这篇文章内核模式和用户模式,其 ...

  5. linux0.11内核源码——用户级线程及内核级线程

    参考资料:哈工大操作系统mooc 用户级线程 1.每个进程执行时会有一套自己的内存映射表,即我们所谓的资源,当执行多进程时切换要切换这套内存映射表,即所谓的资源切换 2.但是如果在这个进程中创建线程, ...

  6. Linux用户级线程和内核级线程区别

    1.内核级线程: (1)线程的创建.撤销和切换等,都需要内核直接实现,即内核了解每一个作为可调度实体的线程.(2)这些线程可以在全系统内进行资源的竞争.(3)内核空间内为每一个内核支持线程设置了一个线 ...

  7. {Python之线程} 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Threading模块 九 锁 十 信号量 十一 事件Event 十二 条件Condition(了解) 十三 定时器

    Python之线程 线程 本节目录 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Thr ...

  8. linux内核--进程与线程

    http://blog.csdn.net/yusiguyuan/article/details/12154823 在<linux内核设计与实现>中第三章讲解了进程管理,在关于进程和线程的概 ...

  9. Linux内核:kthread_create(线程)、SLEEP_MILLI_SEC

    转自:http://blog.csdn.net/guowenyan001/article/details/39230181 一.代码 #include <linux/module.h> # ...

随机推荐

  1. android MotionEvent获得当前位置

    event.getY()与event.getX()为当前触摸位置距离当前元素顶端和左端的距离: event.getRowY()与event.getRowX()为当前触摸位置距离当前屏幕顶端和左端的距离 ...

  2. 简单粗暴下载Spring

    http://repo.springsource.org/libs-release-local/org/springframework/spring/4.3.3.RELEASE/(想要下载什么版本,替 ...

  3. 如何获取网页上的LOGO

    一般公司网页上的图片都会禁止右键另存为,用截图工具接下来的图会带背景色,PS成背景透明有点费时间. 用Google Chrome 或Firefox 打开目标网页,右键点击审查元素,将鼠标放在图片上,一 ...

  4. LeetCode Shortest Distance from All Buildings

    原题链接在这里:https://leetcode.com/problems/shortest-distance-from-all-buildings/ 题目: You want to build a ...

  5. cocos2dx 3.x(场景(层)的生命周期)

    //进入当前层初第一步始化层调用 bool GameScence::init() { if( !void init() ) { returnfalse; } log("进入当前层初第一步始化 ...

  6. spring设置webAppRootKey

    今天一个同事来问webAppRootKey 在哪设置的 <context-param> <param-name>webAppRootKey</param-name> ...

  7. 【转修正】sql server行版本控制的隔离级别

    在SQL Server标准的已提交读(READ COMMITTED)隔离级别下,一个读操作会和一个写操作相互阻塞.未提交读(READ UNCOMMITTED)虽然不会有这种阻塞,但是读操作可能会读到脏 ...

  8. Java:并行编程及同步使用方法

    知道java可以使用java.util.concurrent包下的 CountDownLatch ExecutorService Future Callable 实现并行编程,并在并行线程同步时,用起 ...

  9. 20145320 《Java程序设计》第5周学习总结

    20145320 <Java程序设计>第5周学习总结 教材学习内容总结 8.1 语法与继承架构 try.catch Java中的错误会被包装为对象,而使用try与catch,JVM会执行t ...

  10. 如何快速建立一个测试资源Web服务器及异步获取资源(Unity3D)

    背景 1.最近看了几位专栏作家的文章,几篇提到了资源通过网络的动态获取.如何建立一个快速的测试环境,不免是一个问题,也就最简单的就是假设http服务器了,微软系的当然首选的IIS了,别的也能用阿帕奇或 ...