另,线程的资源占用可见: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. UIView画虚线边框

    //fatherView加虚线边框 -(void)boundingRectangleForView:(UIView *)fatherView{ CAShapeLayer *borderLayer = ...

  2. An AVPlayerItem cannot be associated with more than one instance of AVPlayer错误

    An AVPlayerItem cannot be associated with more than one instance of AVPlayer An AVPlayerItem cannot ...

  3. What is the difference between a Clustered and Non Clustered Index?

    A clustered index determines the order in which the rows of a table are stored on disk. If a table h ...

  4. SimpleInjector的使用

    SimpleInjector的使用       国庆大假,但是,小弟依然学习,前天去看了房交会,尼玛,吓屎宝宝了,还是安静的坐在公司里巧代码比较合适: the usage of injector co ...

  5. LeetCode Alien Dictionary

    原题链接在这里:https://leetcode.com/problems/alien-dictionary/ 题目: There is a new alien language which uses ...

  6. Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf 动态库的后缀为*.so 静态库的后缀为 libxxx.a ldconfig 目录名

    Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf  动态库的后缀为*.so  静态库的后缀为 libxxx.a   ldconfig   目录名 转载自:http://b ...

  7. Hausdorff distance

    微分动力系统原理 这本书里有介绍 Hausdorff距离是描述两组点集之间相似程度的一种量度,它是两个点集之间距离的一种定义形式:假设有两组集合A={a1,…,ap},B={b1,…,bq},则这两个 ...

  8. cocos2dx 3.x(常见的46种动作)

    Sprite * sp= Sprite::create("Icon.png"); sp->setPosition(Vec2(, )); addChild(sp,,); // ...

  9. react input 获取/失去焦点

    <div className={ this.state.focus ? "dis_bottom_left_onfocus" : "dis_bottom_left&q ...

  10. 献给初学者:谈谈如何学习Linux操作系统

    本文出自 “技术成就梦想” 博客,请务必保留此出处http://ixdba.blog.51cto.com/2895551/569329. 为了能把这篇不错的文章分享给大家.所以请允许我暂时用原创的形式 ...