多核时代:并行程序设计探讨(3)——Windows和Linux对决(多进程多线程)

简介:

并行程序设计探讨(3)——WindowsLinux对决(多进程多线程)

前面的博文经过分析总结,最后得出两种并行技术:多进程多线程、多机协作。对于多进程和多线程来说,最有代表性且最常见的的莫过于WindowsLinux(作为UNIX类操作系统的代表,下同)这两个操作系统了。

真是冤家路窄,WindowsLinux这对冤家在这里又碰面了!!

当然,我这里不是要挑起WindowsLinux谁优谁劣的争论,对于一个真正的技术人来说,WindowsLinux本身并没有优劣之分,只有在不同的使用场景下用谁会更好的问题。之所以将WindowsLinux拿来对比,是因为对比更加容易让人理解,记忆也更加深刻!

下面我们首先从多进程和多线程的实现机制方面来对比WindowsLinux

                                         多进程多线程实现机制

说起进程和线程,估计大家都会立刻想起那句耳熟能详的解释“进程是资源分配的最小单位,线程是运行的最小单位”!。

理论上来说这是对的,但实际上来说就不一定了,例如Windows有进程和线程的概念,而传统UNIX却只有进程的概念(例如经典的《UNIX环境高级编程》中就没有多线程的概念,但SolarisAIX等又有另外的实现,此处暂且不表),Linux也有进程和线程的概念,但实现机制和Windows又不一样,真是林子大了什么鸟都有:)

有几个进程、线程相关的概念首先要简单介绍一下:

1.1   概念介绍

1.1.1  进程

资源分配最小单位,有的操作系统还是运行最小单位;

1.1.2   线程

运行最小单位,也是CPU调度的最小单位;

1.1.3   ULTKLT

用户态线程和内核态线程;主要的区分就是“谁来管理”线程,用户态是用户管理,内核态是内核管理(但肯定要提供一些API,例如创建)。

简单对比两者优劣势:

1)可移植性:因为ULT完全在用户态实现线程,因此也就和具体的内核没有什么关系,可移植性方面ULT略胜一筹;

2)可扩展性:ULT是由用户控制的,因此扩展也就容易;相反,KLT扩展就很不容易,基本上只能受制于具体的操作系统内核;

3)性能:由于ULT的线程是在用户态,对应的内核部分还是一个进程,因此ULT就没有办法利用多处理器的优势,而KLT就可以通过调度将线程分布在多个处理上运行,这样KLT的性能高得多;另外,一个ULT的线程阻塞,所有的线程都阻塞,而KLT一个线程阻塞不会影响其它线程。

4)编程复杂度:ULT的所有管理工作都要由用户来完成,而KLT仅仅需要调用API接口,因此ULT要比KLT复杂的多;

1.1.4   POSIX

为了解决不同操作系统之间移植时接口不兼容而制定的接口标准,详见维基百科解释:http://zh.wikipedia.org/wiki/POSIX

1.1.5  NPTL

为了解决Linux原有线程实现机制的缺陷而创立的一个开源项目,从2.4开始就有发布版本采用NPTL来实现多线程支持了。详见维基百科解释http://zh.wikipedia.org/wiki/Native_POSIX_Thread_Library

1.1.6   LWP

Lightweight Process,轻量级进程,看名字有点奇怪,为什么叫轻量级进程呢?为什么又要用轻量级线程呢?

看了前面ULTKLT的比较,估计大家也发现了一个问题:所谓的ULT,因为不能利用多处理器的优势和线程互相阻塞,其实完全不能堪重任,但对于传统UNIXLinux这类操作系统,内核设计和实现的时候就没有线程这种对象,那怎么实现多线程呢?

天才们于是想出了LWP这个招数,说白了这就是一个“山寨版的进程”,完全具有了山寨的一切特征:

文件系统是原来的进程的;

文件描述符是原来的进程的;

信号处理是原来的进程的;

地址空间是原来的进程的;

但就是进程ID不是原来的进程的,你说像不像BlackBerry的山寨版BlockBerry

 

详情请参考维基百科解释:http://en.wikipedia.org/wiki/Light-weight_process

1.2  详细对比

1.2.1  Windows

在此要向Windows致敬:至少相比Linux来说,Windows在线程上的支持是Linux不能比的(不要跟我提DOS哈)!

Windows的实现机制简单来说就是前面提到的KLT,即Windows在内核级别支持线程。每个Windows进程至少有一个线程,系统调度的时候也是调度线程。

当创建一个进程时,系统会自动创建它的第一个线程,称为主线程。然后,该线程可以创建其他的线程,而这些线程又能创建更多的线程。

Windows已经提供了线程编程系列的API,这里就不详述了。

1.2.2  Linux

Linux不同的版本有不同的实现,2.0~2.4实现的是俗称LinuxThreads的多线程方式,到了2.6,基本上都是NPTL的方式了。下面我们分别介绍。

1.2.2.1 LinuxThreads      

注:以下内容主要参考“杨沙洲 (mailto:pubb@163.net?subject=Linux 线程实现机制分析&cc=pubb@163.net)国防科技大学计算机学院”的“Linux 线程实现机制分析”。

这种实现本质上是一种LWP的实现方式,即通过轻量级进程来模拟线程,内核并不知道有线程这个概念,在内核看来,都是进程。

Linux采用的“一对一”的线程模型,即一个LWP对应一个线程。这个模型最大的好处是线程调度由内核完成了,而其他线程操作(同步、取消)等都是核外的线程库函数完成的。

LinuxThreads中,专门为每一个进程构造了一个管理线程,负责处理线程相关的管理工作。当进程第一次调用pthread_create()创建一个线程的时候就会创建并启动管理线程。然后管理线程再来创建用户请求的线程。也就是说,用户在调用pthread_create后,先是创建了管理线程,再由管理线程创建了用户的线程。

这种通过LWP的方式来模拟线程的实现看起来还是比较巧妙的,但也存在一些比较严重的问题:

1)线程ID和进程ID的问题

按照POSIX的定义,同一进程的所有的线程应该共享同一个进程和父进程ID,而Linux的这种LWP方式显然不能满足这一点。

2)信号处理问题

异步信号是以进程为单位分发的,而Linux的线程本质上每个都是一个进程,且没有进程组的概念,所以某些缺省信号难以做到对所有线程有效,例如SIGSTOPSIGCONT,就无法将整个进程挂起,而只能将某个线程挂起。

3)线程总数问题

LinuxThreads将每个进程的线程最大数目定义为1024,但实际上这个数值还受到整个系统的总进程数限制,这又是由于线程其实是核心进程。

4)管理线程问题

管理线程容易成为瓶颈,这是这种结构的通病;同时,管理线程又负责用户线程的清理工作,因此,尽管管理线程已经屏蔽了大部分的信号,但一旦管理线程死亡,用户线程就不得不手工清理了,而且用户线程并不知道管理线程的状态,之后的线程创建等请求将无人处理。

5)同步问题

LinuxThreads中的线程同步很大程度上是建立在信号基础上的,这种通过内核复杂的信号处理机制的同步方式,效率一直是个问题。

6)其他POSIX兼容性问题

Linux中很多系统调用,按照语义都是与进程相关的,比如nicesetuidsetrlimit等,在目前的LinuxThreads中,这些调用都仅仅影响调用者线程。

7)实时性问题

线程的引入有一定的实时性考虑,但LinuxThreads暂时不支持,比如调度选项,目前还没有实现。不仅LinuxThreads如此,标准的Linux在实时性上考虑都很少。

1.2.2.2   NPTL的实现

NPTLNative POSIX Thread Library,天生的POSIX线程库。从命名上也可以看出所谓的NPTL就是针对原来的LinuxThreads的,不然为啥叫“Native”呢:)

本质上来说,NPTL还是一个LWP的实现机制,但相对原有LinuxThreads来说,做了很多的改进。下面我们看一下NPTL如何解决原有LinuxThreads实现机制的缺陷。

1)线程ID和进程ID问题

新的exec函数能够创建和原有进程ID一样ID的新进程,这样所有的线程ID都是一样的;且/Proc目录下只会显示进程的初始线程(初始线程就代表整个进程,类似于Windows的进程中第一个线程s),不会再像以前LinuxThreads机制时每个线程在proc目录下都有记录。

2)信号处理问题

内核实现了POSIX要求的线程信号处理机制,发送给进程的信号将由内核分发给一个合适的线程处理,对于致命和全局的信号(例如StopContinue Pending,所有的线程都同步处理。

3)线程总数问题

内核经过扩展,能够处理任意数量的线程。PID空间经过扩展后,在IA-32系统上能够最大支持20亿线程。

4)管理线程问题

去掉管理进程,管理进程的任务由扩展后的clone函数完成;增加了exit_group的系统调用,用于退出整个进程;

5)信号同步问题

实现了一个叫做FutexFase Userspace Mutex,注意不是Mutex)机制用来完成线程间同步,Futex的主要操作是在用户态完成的,这样解决了依靠内核信号机制进行同步的效率问题。详细请参考http://zh.wikipedia.org/wiki/Futex

 

当然,NPTL虽然做了很多改进,但依然不是100% POSIX兼容的,LinuxThreads的第6个和第7个问题在NPTL机制下依然没有解决,但这并不掩盖NPTL带来的巨大改进,下面是性能对比图:

NPTL官方的文档:http://people.redhat.com/drepper/nptl-design.pdf

1.3    对决?

看了前面的分析,大家可能纳闷了,这哪里是对决哦?全部是讲Linux的实现了。

其实我也郁闷,本来应该更加详细的介绍Windows实现机制的,但由于Linux的不争气,全部用来变成对它的分析了。

 

 

==========================未完待续===============================

相关文章
|
18天前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第16天】进程、线程和协程是计算机程序执行的三种基本形式。进程是操作系统资源分配和调度的基本单位,具有独立的内存空间,稳定性高但资源消耗大。线程是进程内的执行单元,共享内存,轻量级且并发性好,但同步复杂。协程是用户态的轻量级调度单位,适用于高并发和IO密集型任务,资源消耗最小,但不支持多核并行。
35 1
|
4天前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
26 4
linux进程管理万字详解!!!
|
1天前
|
Linux 调度 C语言
深入理解操作系统:进程和线程的管理
【10月更文挑战第32天】本文旨在通过浅显易懂的语言和实际代码示例,带领读者探索操作系统中进程与线程的奥秘。我们将从基础知识出发,逐步深入到它们在操作系统中的实现和管理机制,最终通过实践加深对这一核心概念的理解。无论你是编程新手还是希望复习相关知识的资深开发者,这篇文章都将为你提供有价值的见解。
|
4天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
24 4
|
4天前
深入理解操作系统:进程与线程的管理
【10月更文挑战第30天】操作系统是计算机系统的核心,它负责管理计算机硬件资源,为应用程序提供基础服务。本文将深入探讨操作系统中进程和线程的概念、区别以及它们在资源管理中的作用。通过本文的学习,读者将能够更好地理解操作系统的工作原理,并掌握进程和线程的管理技巧。
13 2
|
5天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
5天前
|
调度 Python
深入浅出操作系统:进程与线程的奥秘
【10月更文挑战第28天】在数字世界的幕后,操作系统悄无声息地扮演着关键角色。本文将拨开迷雾,深入探讨操作系统中的两个基本概念——进程和线程。我们将通过生动的比喻和直观的解释,揭示它们之间的差异与联系,并展示如何在实际应用中灵活运用这些知识。准备好了吗?让我们开始这段揭秘之旅!
|
6天前
|
消息中间件 存储 Linux
|
12天前
|
运维 Linux
Linux查找占用的端口,并杀死进程的简单方法
通过上述步骤和命令,您能够迅速识别并根据实际情况管理Linux系统中占用特定端口的进程。为了获得更全面的服务器管理技巧和解决方案,提供了丰富的资源和专业服务,是您提升运维技能的理想选择。
12 1
|
16天前
|
Python
Python中的多线程与多进程
本文将探讨Python中多线程和多进程的基本概念、使用场景以及实现方式。通过对比分析,我们将了解何时使用多线程或多进程更为合适,并提供一些实用的代码示例来帮助读者更好地理解这两种并发编程技术。