文 / 王越 经过6年时间,4个发行版,苹果终于完成了向64位的迁移,并随着Snow Leopard的发布推出了解决并行编程问题的Grand Central Dispatch(简称GCD)技术,释放了多核系统的潜力。 和10.5一样,在10.6 Snow Leopard中,苹果继续利用64位的迁移砍掉了诸多老技术,很多新技术仅以64位的模式被支持。例如重写的QuickTime X框架,虽然QuickTime X应用程序以32位和64位的模式发布,但其API仅暴露给64位。另一个例子是Objective-C 2.1的运行库,快速Vtable调度,新的和C++统一的异常处理模型,以及彻底解决对象的FBI问题等,都仅限64位程序使用。

内核的64位化

读者应该发现,经过这4个发行版,Mac OS X自下而上地对整个系统向64位迁移。10.3内核空间提供了64位整数运算的支持。10.4允许程序以64位模式运行在用户空间,并且提供了64位的libSystem使得开发者可以开发64位的Unix程序,而10.5中系统所有未废弃的函数库、框架都提供64位版本,到了10.6,所有用户空间的程序,包括Unix层和图型界面层,基本都更新到64位。细心的读者不禁会问—那内核是64位的吗?是的,自下而上支持64位后,10.6又从上往下,迁移了整个系统中最后一个也是最重要的部分—内核。 内核64位化的意义 对于Windows、Linux,以及FreeBSD等操作系统,64位实现的第一步是实现64位的内核。然而Mac OS X却反其道而行。主要原因是,反正32位的内核也能以非模拟、非兼容的方式原生地运行64位用户空间程序,而内核和与内核动态链接的驱动,很少需要用到64位的寻址空间(你什么时候见过内核本身使用4GB内存?),所以该问题可以暂缓。 但要记住,用户空间的内存是由内核管理的,虚拟内存、内存分页等机制,都是由内核一一实现的。一旦在不久的将来,随着用户空间的内存占用越来越多,虚拟内存的分页比也会不断膨胀。比方说,一个用户程序使用4GB的空间,每个分页包含4KB的页面,那么总共有1M个页面。因此,假设一个页面需要64B的PTE来记录该页的位置,那总共也就需要64MB的内核空间来记录这个用户空间程序的虚拟内存,不算太多。而在不久的将来,如果一个64位用户程序使用128GB的空间,则需要32M个页面,每个页面64B的PTE会导致2GB的内核地址空间来寻址(暂不考虑大分页)。32位的内核就显得非常紧张。 另外,上一期我们也提到64位的Intel架构提供了比32位多一倍的寄存器,因此,用户空间程序对64位内核的系统调用也会更快。根据苹果的数据,系统调用的响应速度比原先快了250%,而用户空间和内核空间的数据交换也快了70%,因此,64位内核要比32位内核更快。 内核完成64位迁移 虽然在Mac OS X 10.6中,苹果提供了64位模式运行的内核,但在大部分苹果计算机上,这个特性并不默认启用。其原因是,虽然64位程序和32位程序可以在计算机上同时运行,但64位的程序只可以加载64位的库或插件,32位程序只能加载32位的库或插件。因此,如果默认使用64位模式启动,则诸多第三方的32位驱动或内核模块将无法使用。当然,用户可以通过修改com.apple.Boot.plist、nvram,或开机按住6和4强制加载64位内核,不过苹果并不推荐这样的方式。直到Mac OS X 10.7时,第三方内核扩展已趋完善,大部分的Mac才默认使用64位内核模式启动。 苹果用了整整6年的时间完成64位的迁移,在2009年WWDC的一个讲座上,Bertrand Serlet告诉开发者,我们这个64位技术的讲座,只针对Mac OS X,而iPhone、iPad等iOS设备,由于使用ARM平台,在可预见的未来可能并不会支持64位技术。 不过两年之后的2011年10月27日,ARMv8发布,ARM正式宣布支持64位。未来会不会出现基于ARM的Mac,或是64位的iPad,除了苹果,谁知道呢? [caption id="attachment_11561" align="aligncenter" width="300" caption="Bertrand Serlet在WWDC 2009上介绍Snow Leopard的64位和Grand Central Dispatch技术"]

[/caption] GCD来临 很长一段时间以来,处理器靠更快的运行时钟来获得更高的效率。软件开发者无需改动或重新编译他们的代码,就能得到摩尔定律许诺他们的好处,因为处理器顺序地执行计算机指令,新一代的处理器就自动会跑得比原先更快。后来每每达到一个技术极限时,总有一些聪明的方法绕过这些极限,比如超纯量、指令管线化、快取等,不是悄无声息地把多条互相独立的指令同时运行,就是隐藏掉数据读写的延时。 GCD出现的缘由 到了21世纪,能想的办法基本都想尽了—现代处理器已经足够并行了,也采取了各项优化来不断提升各种预测器的准确率,而时钟频率却是不能无限提高的—提高时钟频率会极大地增加处理器的产热,使得服务器机房或笔记本的散热成为一个头痛的问题。同时对于便携设备而言,高频也意味着短得多的电池时间,因此摩尔定律正在经受重大的考验。 因此大约在21世纪头十年过掉一半时,“多核”处理器,终于开始跃入普通消费者的视线。“多核”顾名思义,就是把原先单核的半导体线路复制多份排于同一裸片上,每个核相互独立,又能彼此通信。多核处理器的出现,有效缓解了计算机处理器生产商的设计和制造压力,从而达到忽悠消费者买更新款产品这一不可告人的目的。 但这一次技术革新,并不如之前那么顺利,因为程序并不会自动在多核系统上跑得更快,甚至有很多程序每一步都有前后依赖,不能高效地并行运行。即使能够高效并行的程序,也需要大规模改写才能充分利用多核所带来的优势。 传统的并发编程模式,就是学习使用线程和锁。这听起来很简单,几句话能说明白: 把每个任务独立成一个线程; 不允许两个线程同时改动某个变量,因此得把变量“锁”起来; 手动管理线程的先后并发顺序和并发数量,让它们均匀地占满系统资源; 最好系统中只有这个程序在运行,否则你精心设计好的线程管理算法往往不能达到原来该有的效果; 最后祈祷程序在用户那儿不出问题。 但是实际操作起来,多线程程序的编写要比单线程难上不止一个数量级。一方面,调用大量内存和数据反复的加解锁本身效率就非常低下;另一个重要原因在于,由于多线程程序可能以任意的次序交错执行,程序再也无法像顺序执行时那样产生确定的结果。多线程程序看似容易编写,但难分析、难调试,更容易出错。即使是最熟练的开发者,在茫茫线程和锁之间,也会迷失方向。且程序的错误在很多时候甚至是不可重现的。所以,程序员使用线程和锁机制编写并行程序的代价是很高的。 GCD就是在这种背景下被苹果提出来的。2008年最初提出但未公布细节时,很多人怀疑它是FreeBSD的ULE调度器在Mac OS X上的实现。ULE是FreeBSD当时最新的内核调度器,用来替换掉老一代的4BSD调度器,当时使FreeBSD上跑多线程程序的效率获得了重大的性能提高,远高于同期Linux和Solaris的算法效率。但当时我就认为GCD依赖FreeBSD这项技术的可能性不大,因为Mac OS X中管理进程和线程主要用的是Mach而不是BSD。不过后来证实我只猜对了一半,GCD的实现,实际上是依赖于FreeBSD的另一项技术kqueue。kqueue是一个由FreeBSD 4时代引入的新功能,内核级别地支持消息通信管理。GCD的队列,其实就是用kqueue实现的。 GCD出现的意义 在GCD中,开发者不再管理和创建线程,而是将要实现的运算抽象成一个个任务,一起扔给操作系统,转而让操作系统管理,这在计算机科学中,被称为线程池管理模式。 在GCD中,开发者使用很简单的方式就能描述清应用程序所需执行的任务,以及任务之间的相互关联。每一个任务在代码中被描述成块(block),然后开发者把一个一个块显式地按顺序扔到队列(queue)中。使用块和队列两个抽象的表述,开发者无须创建线程,也无须管理线程,更无须考虑数据的加解锁。换之而来的,是更简短可读的代码。剩下的事,全都扔给操作系统去完成。 在操作系统那边,GCD在程序运行时,管理着一定数量的线程,线程的数量是自动分配的,取决于用户计算机的配置和用户程序运行时的负载。多核工作站每个程序配到的线程,自然就会比单核手机或双核笔记本来得多。而且这个线程的数量是会动态变化的。当程序非常忙时,线程数会相应增多,而当程序闲置时,系统会自动减少其线程数量。然后,GCD会一一从队列中读入需要执行的块,然后扔到线程上并发执行。 相信读者已经看出GCD和传统线程—锁机制的区别来了。传统的方式按劳分配,强调程序自由独立地管理,妄想通过“无形的手”把系统资源平均分配,走的是资本主义市场经济的道路。而GCD按需分配,真正实现了社会主义计划经济管理模式。因此在政治上GCD就是一个代表先进生产力的计算机技术(我被自己雷了,但事实就是这样)。 GCD是一个自底向上的技术,它实际上由以下6个部分组成。 编译器层面,LLVM为C、Objective-C和C++提供了块语法,这个内容等下会介绍。 运行库方面,有一个高效分配管理线程的运行库libdispatch。 内核方面,主要基于XNU内核Mach部分提供的Mach semaphores和BSD部分提供的kqueue()机制。关于XNU内核的更多细节,请参考即将发行的四月刊《半导体的丰收(下)》。 dispatch/dispatch.h提供了丰富的底层编程接口。 在Cocoa层面,NSOperation被重写,因为使用libdispatch,所以先前使用NSOperation的程序不需改动,就自动享受Grand Central Dispatch的最新特性。 Instruments和GDB提供了非常完整的分析和调试工具。 GCD还有一些工程上的优势。首先,程序的响应速度会更快。GCD让程序员更方便地写多线程程序,因此写一个多线程程序来实现前后台简单多了,极大改善了Mac OS X上应用程序的生态环境。而且GCD的代码块队列开销很小,比传统线程轻量得多。统计表明,传统的Mac OS X上使用的POSIX线程需要数百个计算机汇编指令,占用512KB的内存,而一个代码块队列才用256字节的长度,把块加入队列,只需要15个计算机汇编指令,因此开成百上千个也不费什么事。 其次,线程模式是一种静态的模式,一旦程序被执行,其运行模式就被固定下来了。但用户的计算机配置各不相同,运行时别的程序有可能耗用大量的计算资源。这些都会影响该程序的运行效率。而动态分配系统资源则能很好地解决这个问题。苹果自然也是不遗余力地忽悠开发者使用GCD,因为各个软件共享多核运算的资源,如果GCD被更多的开发者采用,整个苹果平台的生态也就更健康。 而最重要的,还是GCD采用的线程池模式极大简化了多线程编程,也降低了出错的可能性。著名FreeBSD开发者Robert Watson还发布了一个他修改过的Apache,并释出了补丁,声称只需原先1/3至1/2的代码量,就实现了原先的多线程模块,并比原先的效率更好。 如何应用GCD 当然,老王卖瓜,自卖自夸,没有实际的例子,是不能让读者信服的。下面我们就来简单讲解GCD的技术。 首先是块状语法,是一个对C、C++和Objective-C语言的扩展。用来描述一个任务,用^引导的大括号括起来。比如最简单的: x = ^{ printf("hello world\n");} 则 x 就变成了一个块。如果执行: x(); 那么程序会打印hello world出来。当然,blcok像函数一样,可以跟参数,比如: int spec = 4; int (^MyBlock)(int) = ^(int aNum){ return aNum * spec; };  spec = 0; printf("Block value is%d", MyBlock(4)); 这里MyBlock是一个带参数的代码块。

读者看到这里不禁要问,块到底有什么好处?它和C的函数指针有什么不同?我们依然用上面的例子来说明问题,虽然后面我们把spec变量改为0,但事实上在MyBlock创立时,已经生成了一个闭包,因此它最后输出的结果,仍是16,不受spec值改动的影响。这对于搞函数式编程的人来说再熟悉不过了,因此很多开发者亲切地称呼块语法的C扩展为“带lambda的C”。 有了闭包功能的C顿时牛起来—你可以把函数和数据包装在一起—这就是块的真正功能。因为只要一个闭包包含了代码和数据,它的数据就不会被别的闭包轻易改动,所以在它执行时,你根本不用为数据上锁解锁。 有了一系列的代码块后,接下来的事是把代码块扔到队列里。比如最简单的: dispatch_queue_t queue = dispatch_get_global_queue(0,0); 来创建一个轻量级的队列,然后 dispatch_async(queue, ^{printf("hello world\n");}); 那这个代码块就被扔进queue这个队列中了。你可以手动依次添加任意多个项目,比如“带着老婆”、“出了城”、“吃着火锅”、“唱着歌”、“突然就被麻匪劫了”等。当然在更多的场合,你会更倾向于使用自动事件源,每当一个事件触发时(比如定时器到点、网络传来包裹,或者用户点击了按钮),相应的代码块被自动添加到队列中。

一旦队列不是空的,GCD就开始分配任务到线程中。拿上面的例子来说,“老婆”、“城”等变量可是封在闭包里的,所以在运行时,不用考虑它们被某个别的闭包改掉(当然也有方法来实现这个功能)。总体而言,这个模式比线程—锁模型简单太多—它的执行是并行的,但思维却是传统的异步思维,对没有学习过系统多线程编程的开发者来说,依然能很容易地掌握。 读者可能要问,如果闭包之间有复杂的依赖关系,需要申明某两个操作必须同步或异步怎么办?比如“出了城”必须在“吃着火锅”之前。在GCD中,可以使用dispatch_async和dispatch_sync来描述这样的依赖关系,而在Cocoa层面,NSOperation中的队列依赖关系甚至可以被描述成有向图。 GCD得到广泛应用 GCD一经推出就得到了广泛的应用。苹果自家的软件Final Cut Pro X、Mail等软件,都采用GCD来实现任务并发和调度,因此Mac OS X 10.6成为了有史以来最快的发行版。从iOS 4开始,iPhone和iPad也加入了GCD的支持。更别提原来使用Cocoa的NSOperation相关接口的程序,无需改动即享受GCD的优惠。 GCD在Mac OS X 10.6发布后,又以libdispatch为名,作为一个独立的开源项目发布。 所需的外围代码,如编译器的块支持、运行库的块支持、内核的支持,也都能在LLVM和XNU等开源项目代码中找到,所以很快被别的操作系统采用。作为Mac OS X的近亲, FreeBSD在一个月后即完整移植了整套GCD技术,并最终在FreeBSD 9.0和8.1中出现。诸多Linux发行版也提供libdispatch的包,使用Linux内核的epoll来模拟FreeBSD的kqueue。2011年5月5日,Windows的移植工作也宣告完成。 另外,GCD也成为拯救动态语言的重要法宝。由于受GIL(全局解释锁)的限制,动态语言虽然有操作系统原生线程,但不能在多核处理器上并行执行。而GCD成功绕开了这个限制,如加入GCD支持的Ruby 实现MacRuby就能在多核处理器上高效执行。 因此,在苹果生态圈以外,GCD也会得到越来越多的应用。读者马上还会看到,苹果同时推出的另一项主推技术中也使用了GCD,详细内容请关注四月刊《半导体的丰收(下)》。   作者王越,美国宾夕法尼亚大学计算机系研究生,中国著名TeX开发者,非著名OpenFOAM开发者。 本文选自《程序员》杂志2012年03期,未经允许不得转载。如需转载请联系 market@csdn.net 《程序员》2012年杂志订阅送好礼活动火热进行中

Mac OS X 背后的故事(九)半导体的丰收(中)相关推荐

  1. Mac OS X 背后的故事(二)——Linus Torvalds的短视

    文/王越 <Mac OS X背后的故事>系列文章将为大家介绍Mac OS X的发行版本.技术历史.相关人物等内容.本文是系列连载的第二篇,主要的故事来源是Linus Torvalds的自传 ...

  2. Mac OS X 背后的故事(三)Mach之父Avie Tevanian 1

    1975年,美国罗彻斯特大学纽约分校,一组研究员正在做一个名为RIG(Rochester"s Intelligent Gateway)的项目,它由Jerry Feldman主持设计.RIG的 ...

  3. Mac OS X 背后的故事(四)——政客的跨界

    文/王越 <Mac OS X背后的故事>系列文章将为大家介绍Mac OS X的发行版本.技术历史.相关人物等内容.本文是系列连载的第四篇. 2000年,美国总统大选,由于选票设计问题,时任 ...

  4. Mac OS X 背后的故事(六)Cordell Ratzlaff 引发的 Aqua 革命(上)

    Mac OS X 背后的故事(六)CordellRatzlaff 引发的 Aqua 革命(上) 文 / 王越 Aqua是Mac OS X Public Beta全新用户界面的名字,英文中为水的词根,寓 ...

  5. Mac OS X 背后的故事(九)半导体的丰收(下)

    文 / 王越 随着CPU与GPU合并成技术发展的趋势,苹果开发出了OpenCL框架,能够进行高速并行处理的能力使OpenCL成为了业界标准,被广泛应用. 最近几年,GPU的发展吸引了很多来自科学计算界 ...

  6. Mac OS X 背后的故事(八)半导体的丰收

    原文地址: http://www.programmer.com.cn/10071/ 文/王越 在美国宾夕法尼亚州的东部,有一个风景秀美的城市叫费城.在这个城市诞生了一系列改变世界的奇迹:第一个三权分立 ...

  7. Mac OS X 背后的故事(一)力挽狂澜的Ellen Hancock

    原文地址: http://www.programmer.com.cn/6727/#more-6727 文 / 王越 从本期开始,我们将在杂志上连载一系列关于Mac OS X发展历史的文章.本系列将为大 ...

  8. Mac OS X 背后的故事(六)Cordell Ratzlaff 引发的 Aqua 革命

    原文地址:http://www.programmer.com.cn/9016/ Aqua是Mac OS X Public Beta全新用户界面的名字,英文中为水的词根,寓意以水为灵感,精心设计.Ste ...

  9. 【转】操作系统Unix、Windows、Mac OS、Linux的故事

    电脑,计算机已经成为我们生活中必不可少的一部分.无论是大型的超级计算机,还是手机般小巧的终端设备,都跑着一个操作系统.正是这些操作系统,让那些硬件和芯片得意组合起来,让那些软件得以运行,让我们的世界在 ...

  10. 操作系统Unix、Windows、Mac OS、Linux的故事

    2019独角兽企业重金招聘Python工程师标准>>> 我们熟知的操作系统大概都是windows系列,近年来Apple的成功,让MacOS也逐渐走进普通用户.在服务器领域,恐怕Lin ...

最新文章

  1. tmux的使用方法和个性化配置
  2. Linux C编程--进程介绍3--进程终止和等待
  3. BZOJ2115:[WC2011] Xor(线性基)
  4. java 虚拟机 字节码,JAVA虚拟机:虚拟机字节码执行引擎
  5. windows 程序设计及API
  6. Python pandas使用
  7. 拓端tecdat|R如何与Tableau集成分步指南 - 适用于数据科学和商业智能专业人员
  8. python 实现C atoi函数
  9. micropython 人脸识别检测_基于ESP8266的人脸识别球锁开锁方案
  10. Win10驱动签名总结
  11. 华为手机隐藏app图标_华为手机怎么隐藏应用图标
  12. 【c++学习】int main(int argc, char** argv)命令行参数理解
  13. 170628 逆向-安卓查壳软件ApkDetecter安装
  14. shell脚本快速执行命令
  15. 产品经理必备技能之业务流程图
  16. 搜索 阿虚同学_凉宫春日阿虚台词“在虚构的故事当中寻求真实感的人脑袋一定有问题”动画是出自那一集?...
  17. 为明天计划,而不要为明天忧虑
  18. PHP将word文件转为图片预览
  19. tomcat集群session共享
  20. 中文分词jieba学习笔记

热门文章

  1. 2008年最吸引眼球的10只股票
  2. Windows XP SP3截至2011年4月更新补丁汇总(WinXP补丁包) 修正版
  3. 使去中心化媒体网络相关联的NFT元数据标准
  4. Aras innovator: innovator大家族
  5. 键盘密码(Qwerty)——python解密
  6. 关于Win11家庭版安装Ansys2021R1遇到的问题
  7. python脚本之对文件进行哈希校验
  8. Echarts地图深入+散点
  9. SAP报表导出格式设置
  10. 万维网联盟W3C发布HTML5新logo