早期版本留下的时间线
Cyclone是一种面向系统编程的C语言方言。项目发行说明把0.2版标注为2001年11月20日,把0.3版标注为2002年4月18日,本文日期均为公历。2001年的发行记录在先,次年继续修订。
0.2到0.3的修改涉及实际使用条件。为改善程序库覆盖,团队把直接支持的平台收缩到Linux(x86)、Cygwin和Mac OS X;另增buildlib工具,帮助为C程序库生成Cyclone所需的头文件和封装接口。发行工作不仅交付编译器,也要使它能够接触已有的系统软件。
保留C语言的控制能力
项目手册将初始设计与实现归于丹·格罗斯曼(Dan Grossman)、特雷弗·吉姆(Trevor Jim)和格雷格·莫里塞特(Greg Morrisett)。这项工作部分建立在康奈尔大学的Popcorn语言之上;Popcorn原是带类型汇编语言(Typed Assembly Language)研究项目中的类C语言。2002年的区域内存论文列出康奈尔大学团队与AT&T实验室研究部门(AT&T Labs Research)的合作。
他们面对的矛盾来自系统软件本身。设备驱动、文件服务器等程序需要控制数据在内存中的排列和对象的存活时间,C语言可以提供这种控制,却也容许越界访问和悬空指针。Cyclone尝试保留低层控制,同时用类型规则和必要的运行时检查约束危险操作,使已有C代码有机会逐步迁入安全的环境。
让指针带上生命周期约束
2002年的论文详细说明了区域式内存管理:对象各自属于某一区域,局部变量所在的栈区域随代码块结束而释放;另有堆区域和可在其中不断分配对象的动态区域。这里的动态区域具有由代码作用域决定的寿命,离开区域语句时,其中的对象一起释放。堆对象则可选用垃圾回收器管理。
论文用一个短程序解释悬空指针:代码块中建立整数x,把它的地址赋给外层指针p,退出代码块后再通过p写入42。此时x的存储已经失效。Cyclone的区域类型规则会拒绝这个例子;换写指针声明也不能掩盖对象所属区域的不一致。检查的重点是访问时对象是否仍然存活,而非仅看地址是否非空。
为减轻移植负担,编译器可以推断局部变量的区域标注,并为部分接口采用默认标注。分析以单个函数为单位;跨函数所需的区域关系通过接口表达,因而仍然能够分别编译。
参考:[2]
把一项请求放进一个区域
这种办法被用在mini_httpd网络服务器的移植实验中。团队让主处理函数为一次请求建立动态区域,把区域句柄放入请求结构,再交给各个子程序使用。请求处理完毕,区域随之释放。
实现这一安排,需要把原来的全局变量搬进请求结构,并把该结构作为参数传给若干函数;类型标注还要说明请求结构与相关参数、返回值之间的区域关系。程序员由此能把内存释放时机同请求处理联系起来,编译器则检查这些关系是否安全。对cfrac计算程序的尝试并不一样:大输入会让区域在释放前增长过大,作者认为垃圾回收更适合这段代码。
参考:[2]
移植代价与试验成绩
团队没有只展示新写的小例子,还移植了网络工具、压缩程序和计算密集程序。论文的基本移植组合计包含18,627行C代码,转为Cyclone时有1,452行新增或修改,约占原程序的8%。许多修改涉及指针类型写法,区域标注只是其中一部分;这些数字描述的是论文所选程序,不能替代所有系统软件的成本估计。
性能结果也并不一致。作者在750兆赫Pentium III、256兆字节内存的Linux机器上,每项试验运行二十一次。带检查的mini_httpd运行时间约为C版本的1.02倍,而计算密集型程序grobner约为2.85倍。边界检查和占用更多空间的指针表示都会带来开销,安全保证并没有消除这种取舍。
参考:[2]
从实现问题继续修订
早期发行说明记录了具体的兼容性障碍。例如,团队曾为Mac OS X上的编译器问题改用部分内联函数,又为程序增加更完整的库接口、行号信息和词法分析工具。用户得到的不是一份冻结的语言标准,而是一套仍在纠错、移植和扩展的研究实现。
到2002年6月柏林的程序设计语言设计与实现会议,研究者已经可以报告区域规则、编译实现和移植试验。他们也在论文中把提前释放区域、更灵活的区域寿命与改善指针性能列为后续工作。这些目标仍列在未来工作中,没有被作者写成已经完成的语言功能。