industrialized-c-2022

VIP免费
2025-01-13 0 0 452.46KB 99 页 5.9玖币
侵权投诉
Industrialized C 语言参考手册
SY2206214 武润璠 SY2206208 苏东泽
2022 12 12
1总体设计
1.1 背景
软件应用于工业场景,由来已久。这里的工业场景,不仅仅是指狭义的工业场景,
而是一种泛指。工业场景包括多种类别:
航空航天:飞控、卫星等
生命攸关的领域:汽车、医疗等
嵌入式和固件:引导加载器、固件、微控制器、TPM
工业控制:PLCSCADA
基础设施:能源、供水、交通、通信等
这些场景与一般的民用领域主要的不同有三点。
其一是软件开发者需要有对整个供应链的控制能力。如果对供应链的控制欠缺一
环,不仅需要多信任一个实体,也容易受人摆布。
其二是需要验证软件的鲁棒性。这里的验证包括静态分析、动态分析、实际测试
乃至形式化验证等多种方法,需要交叉火力,以此方法之长弥补彼方法之短。
1
其三是软件的可靠性必须保证。可靠性并不仅仅指功能上的可靠性,还有对软件
可靠性的切实依赖。如果软件出现了不可靠的状况,会有较为严重的损失,甚至
会结束人的生命1
这三点是互为因果、相辅相成的,某软件有一环有较大的缺陷,就不能称之为工
业领域软件。
一般意义上工业领域软件通常用 CC++ 开发,主要原因有以下几点。
C C++ 是开放标准,没有也不容易受特定组织的控制。C标准中最常用的部
分也比较简单,一个完整的编译器可以只需要约 5000 行代码2
C C++ 是通用语言,可招募的程序编写者多,且与小众语言相比,更有实力
吸引人学习。
C C++ 可以通过各种工具进行静态和动态分析。C的静态分析工具主要有
CppcheckParasoftPVS Studio 等。CC++ 在软件业的主流动态分析工具
ValgrindASAN 等。但是 C++ 毕竟标准较为复杂,边界情况corner cases
也较多,在可分析性方面较 C优势较弱。
C C++ 有发达的库和生态系统,同时编译方式也比较自由。虽然在普通的软
件行业,编译的自由性是一种缺点,但是在环境相对固定的工业场景,它的不利
之处大大减小。
C C++ 没有垃圾回收器,也没有动态内存分配,能够保证内存布局的确定性
和硬实时性。
但是我们要抛出一个问题:
除了 CC++,还有没有适合工业领域软件的语言?
1.1.1 语言概览
实际上,适合系统软件开发的语言有限,适合工业领域软件开发的语言就更有限
了。我们以较为主流的系统软件开发语言为例,阐述一下原因。
1这里不得不提的是 Therac-25 事件。
2可以参考早期版本的 TCCTiny C Compiler
2
使用 RustRust 在最近炙手可热,它本来是作为替换火狐浏览器引擎的 Servo
目的一部分开发的,原初的目标也是替换一款重要的系统软件。Rust 不行,有几处
严重的硬伤。考虑到 Mozilla 的战略,尽管 Linux 内核等软件开始拥抱 RustRust
年内不可能适用于工业场景。
支持的体系结构不够多,只是较为主流的一些。
语法变化太快,且软件包的新版本也要跟上最新语法。这种不稳定性会影响工业
场景软件的开发。
编译 Rust 需要 Rust 编译器本身,这里是需要二进制的编译器。这种对二进制编
译器的天然需求,使得任何依赖 Rust 的软件都必须信任 Mozilla 基金会,没有
对整个供应链的控制。有几个项目可以从源代码自举(bootstrap)出 Rust 的二
进制编译器,如 GCC Rust 后端、mrustc 等,意图,但是并不能跟上最新版
本,而且作者可能会放弃,所以可靠性较差,不值得依赖。
编译 Rust 的包管理器 Cargo目前也需要二进制的 Cargo 本身了。更致命的是,
Cargo 可能依赖非 Mozilla 基金会出品的 Rust 软件包,信任实体的个数不可知。
不仅是对供应链的控制没了,对供应链控制的控制权,或者说“元控制,实际
上也失去了。
不能实现依赖厂商化dependency vendoring即固定某个软件或软件包的所有
确定版本的依赖,且随原软件或软件包发行。用户可以一起编译,不需要联网
Rust 实际上有非官方的依赖厂商化解决方案,但是个人项目靠得住吗?
使用 GoGo 有垃圾回收机制,不能保证硬实时。它同步机制轻量、成熟,更适合
网络和服务器编程。更重要的一点是,尽管 Go 支持官方的依赖厂商化,它的模块引入
系统与在线 Git 服务高度绑定。事实上,本地模块成了二等公民,软件编译离开互联
网便麻烦重重。将其用于对自主性有较高要求的工业场景,显然是不适合的。
使用 ErlangErlang 在基础设施中的电信行业使用较多,本身能保证软实时性。
Erlang 通用性较差,也较为小众。适合一部分工业软件的开发,但对项目的前景也
有一定的长期影响。
使用 AdaAda 遵循开放标准,也是专门为工业场景设计的。但是,它仍然是美国
国防部主导设计的语言,自主可控性难以保证。
3
使用 PascalPascal 在上世纪 80 年代左右,是在通用编程语言的竞争中,实力与
C不相上下的竞争者。它与 C定位类似,作用也类似。但是现在即使是 Object Pascal
Delphi 等衍生语言,都早已沦为小众语言,尽管这一系列语言也应用于一些工业软
件的开发中。
使用 Objective CObjective C 虽然是开放标准,但是也由苹果公司把控。虽然它
没有虚拟机或运行时,但是很多方面是为桌面软件优化的,开销很大且在工业场景显
得多余,如方法调用等。因此也不适合工业场景。
使用 JavaJava 经过多年的积淀,稳定性有了很大的提高。它的定位也是适合系统
编程的。但是,Java 虚拟机需要较高的硬件条件,本身也有垃圾回收。因此,它也适
合一部分工业软件的开发,但是本身要求较高。
使用 C#C# 主要的定位和特点类似 Java同样地,缺点也和 Java 类似,同样需
要较高的硬件条件。但是,C# 实际上的实现以微软为主流,开放性不高。事实上,C#
也应用于一部分工业软件的开发。
使用 Common LispCommon Lisp 是专门用于系统编程的 Lisp 方言,但是在工
业场景中一定适合。Lisp 一系列的语言重在求值,不太关心硬件底层的实现,
在工业场景中可能会出与硬件磨合的问题。况且,Common Lisp 应用与工业场景中
的先例应该很少,它在这方面的应用没有经过实战检验。
使用 ZigZig 是一种新型的系统语言,其目标之一就是取代 C但是 Zig 毕竟较为
年轻,没有经过实战检验。且去除了宏系统,重视编译期的相关特性,C兼容性差。
因此,从目前来看,也不适合工业场景。
通过十种语言的奥德赛,我们可以看出:
总的来说,近年来的系统编程语言,竟无一广泛适合一般的工业软件开发!
1.1.2 C 的缺点
因此,我们还是要回到改造 CC++ 的路子上来。C++ 标准过于庞杂,且可以
相对方便地制造出新的领域专门语言Domain-Specic LanguageDSL同时,它也
比较难以享受到静态分析带来的程序稳定性提升。因此,我们选择 C进行改造。那么,
C有什么缺点呢?总体上来说,C变得越来越老态龙钟,在工业场景也是这样。
4
1. 过于数据中心,结构体只是数据,连封装也不能实现。继承也不能实现。
2. 没有官方的实现多态的语言机制。
3. 某些语法十分难以掌握,如函数指针3等(见表 5
4. 类型语法不明晰,由于可以使用 typedef 关键词,类型可以只由一个标识符表
示。这样会使得类型的解析上下文相关。
5. 类型的位宽不固定,与平台高度相关。同时,标准整数头文件 stdint.h 也并不
完全可靠4
6. 预处理与程序模块化高度绑定,头文件要么将头尾包装起来,要么使用 #pragma once
7. 不能直接与终端进行交互。在没有终端交互库等组件的情况下,只能将目前的虚
拟终端当作原始的电传终端(teletype)来使用。
8. 宏机制简陋,只基于字符串替换。
9. 类型系统原始,没有泛型机制。通常的泛型是由宏来实现的。
10. 没有语言级别的并发原语。
但是这些问题均可以通过转译轻松解决。这就是我们的大体技术路线。
1.2 词源
我们的语言叫做 Industrialized C,简称 IC。中文名叫“工业 C。下文通常用
称。这里 “Industrialized” 是个有意双关,因为这个单词有两个含义:一为“经过工业
化改造的”,一为“适合工业场景的”。相对应的“工业”作为定语,同样是有意双关。
1.3 设计策略
总体来说,IC 的核心是将源代码转译为 C,设计策略主要有三条。
适合在工业场景中使用。
3我们在批判函数指针的过程中,才学会了函数指针。
4基本上每个大型的软件要么使用 GNU autoconf 等两万行 shell 脚本的怪物,要么自己现ad-hoc
半个平台兼容层。
5
特性 解决 1.1.2 节中的缺点编号 大致描述 对应节
基本类型系统 12struct trait 5.4
函数重载 2修饰原始名称 5.5
内存模型 39new delete 5.6
语法清晰化 345去除难懂的语法 5.7
模块系统 6屏蔽 #include 的细节 5.8
终端交互 7内置 ncurses 5.9
宏系统 8同像性宏 5.10
泛型系统 9类型替换 5.11
协程 10 基于泛型的无栈协程 5.12
1: IC 的主要特性一览
基于 C语言,但克服 C语言的诸多缺点和历史包袱。
最大限度保证与已有 C生态系统的兼容性。
从这三条策略出发,我们的愿望是
从现代语言中窃取可用性,将其藏入 C的普遍性与长久之中,赋能工业软
件的独立、可自举开发!
1.4 特性与实现
IC 的总体实现为由 Python 编写的转译器对 IC 代码进行转译,得到相应的 C
码及头文件。之后由 C编译器编译即可。
1IC 中特性的概览。主要的特性及其实现见第 5章。
1.5 与对标语言的差异
根据 1.1.1,我们选Rust C++ 作为主要的对标语言。在比较中还会穿插其
它语言。
在内存模型方面,IC 仍然是手动管理内存,在未来可以考虑引入 RAII这样
是为了考虑已有程序编写者的认知成本。Rust 的生命周期管理系统,既破坏了兼容性,
又不能很好地适应工业场景中多变的数据结构。
6
在语法方面,IC 尽量向 C靠拢,同时又从 Rust Python 中吸收新特性,如更
清晰的函数类型语法(见 3.2 节)等。C++ 的函数类型过于庞杂、不太自然,且对程
序编写者的负担过重。
在类型系统方面,IC 尽量从已有的 C代码中汲取特性。汲取的结果是向 Rust
拢,有类型和特质,没有继承;泛型通过将类型具体化的方式实现。实际上第二点也像
C++,但是 IC 的泛型没有 SFINAE 原则,不是图灵完备的,避免类型有效性验证时
的不可判定性。
在输出支持方面,IC 封装了 ncurses 库,也有类似帧缓冲的机制,可以做到更新
结构体的内容即为更新屏幕上显示的内容。这样使得输出方式比 Rust C++ 都要先
进,直接与二维的终端交互,而不是单纯地把终端当作电传打字机。
在并行支持方面,IC 将协程的实现融入语言机制之中,大大方便了对协程执行流
程的控制,如复制多个协程、协程分叉forking乃至延续continuation等等。总体
上来说,在实现复杂度与程序编写者习惯程度之间取得了平衡。Rust 依赖过多的内置
语言特性,且与结构体等其它语言特性结合不紧密;C++ 暴露出太多的内部细节,
且在很晚近的标准 C++20 之前没有官方支持。二者与 IC 的实现相比,均有一定的欠
缺。
2词法设计
2.1 空白字符
总体来说,IC 采用类 C的语法5。因此忽略空白字符,程序结构主要由大括号维
持。
1<ignore > ::= " " | "\t" | "\n"
2.2 标识符和关键字
除了空白字符是作为单词分隔符而出现外,IC 的词法分析器在读入阶段不区分标
识符和关键字。若标识符与已经存在的关键字重合,则自动转换为关键字。这样就避
免了关键字与普通标识符重名的问题,因为此时最终会产生语法错误。标识符和关键
字均以下划线或字母开头,并后跟字母、数字或下划线。
1/* identifiers */
5实际上源于 Algol 60
7
2<ident > ::= regex "[_a -zA -Z ]\ w+"
IC 中所有的关键字及其用途如表 2。大部分含义是望文生义的。
2.3 特殊符号
IC 中的特殊符号主要为程序结构需要的字符以及操作符。所有用到的特殊字符如
下。
1+ - . , ( ) -> [ ] * / ! ^ * & || &&
2== != < <= > >= + - | ^ % << >> = ; { }
2.4 字面量
IC 中的字面量分为三种。
整数字面量 由一连串的数字8构成。词法分析器读到连续数字时,会生成整数字面量。
字符串字面量 由双引号括起的一串字符组成。类似 C此处支持使用反斜杠转义。
此字符串字面量以 "开始,并以未被转义的 "结束。
字符字面量 由单引号括起的一个字符组成。也类似 C支持反斜杠转义。开始和结束
条件也类似。
浮点数字面量由整数字面量、特殊符号和关键字 e构成。这里的实现目的是为了
增强前端的工程性,让词法分析器轻量化,更多的复杂逻辑转向语法分析。
3语法设计
3.1 字面量
IC 主要有以下几种类型的字面量:布尔型、整数型、浮点型、字符串型、字符型、
空指针型。其中空指针型字面量可以作为任何指针类型(见 3.2 节)的值。
1/* literals */
2<bool -lit > ::= "true" | "false "
3<int -lit > ::= regex "[0 -9]+"
8符合正则表达式 [0-9]+
8
关键字类别 关键字(以空格分隔)
布尔值 true false
浮点数字面量 e inf nan
空指针字面量 nullptr
内置有符号整数类型 i8 i16 i32 i64
内置无符号整数类型 u8 u16 u32 u64
内置浮点数类型 f32 f64
类型变量声明前缀 typevar
异步函数标识 async
函数前缀 fn
实现标识 impl
结构体翻译无前缀标识6__builtin_noprefix
结构体标识 struct
特质标识 trait
类型转换 as
堆上内存管理 new delete
声明 var const
控制结构7if elif else while do for
跳转 continue break
返回、让渡与恢复 return yield resume
模块导入 import
宏展开 expand
2: IC 中的关键字
9
4<float -lit > ::= [ regex "[\+ -]" ] <int -lit > "."
5| [ regex "[\+ -]" ] <int -lit > "." <int -lit >
6| [ regex "[\+ -]" ] <int -lit > [ "." <int -lit > ] "
e" [ regex "[\+ -]" ] <int -lit >
7| [ regex "[\+ -]" ] " inf "
8| [ regex "[\+ -]" ] " nan "
9<str -lit > ::= regex "\"[^\"]*\""
10 <char - lit > ::= regex "\ '[^\ ']\ '"
11 <nullptr - lit > ::= " nullptr "
12 <lit > ::= <bool -lit > | <int -lit > | <float -lit > | <str -lit > | <
nullptr -lit >
3.2 类型
IC 中类型的语法表示与 C相比,有以下几点不同:
内置类型明确位宽。内置类型的位宽均明确表示,消除了在不同平台下的二义性。
类型均为关键字打头,取消 typedef。这样做一方面能够使程序编写者“望文生
义”,快速判断出类型含义;另一方面也解除了 typedef 带来的二义性,使语法
分析真正做到上下文无关9。还有一个附加的好处,就是降低语言分析工具(如
LSP 服务器等)的分析难度。
使用可读性更强的函数指针语法。C中原来的函数指针语法出名地难记,且存在
指代不清的问题。此处我们对其进行了改造,不仅可读性更强,也减少了语法上
预读(lookahead)的次数。同时,也可以大大降低程序编写者的出错概率。
指明类型变量。在支持泛型的语言中,泛型的类型变量声明放在类型以前,且仅
仅表示为单独的标识符。代入类型参数时,参数通常位于类型名之后。IC 中,
类型变量声明与类型参数位置统一,简化了语法。
保持与 C的兼容性。struct 的可选选项 __builtin_noprefix 代表将其转译为
C时,类型名称使用 struct 的名称本身,而不是加上前缀 structC中不少结
构体类型均为无前缀的 struct 类型,这样做能够保持语法、语义和 API 层面对
C的兼容性。
9TODO:相关的文章
10
摘要:

IndustrializedC语言参考手册SY2206214武润璠SY2206208苏东泽2022年12月12日1总体设计1.1背景软件应用于工业场景,由来已久。这里的工业场景,不仅仅是指狭义的工业场景,而是一种泛指。工业场景包括多种类别:•航空航天:飞控、卫星等•生命攸关的领域:汽车、医疗等•嵌入式和固件:引导加载器、固件、微控制器、TPM等•工业控制:PLC、SCADA等•基础设施:能源、供水、交通、通信等这些场景与一般的民用领域主要的不同有三点。•其一是软件开发者需要有对整个供应链的控制能力。如果对供应链的控制欠缺一环,不仅需要多信任一个实体,也容易受人摆布。•其二是需要验证软件的鲁棒性...

展开>> 收起<<
industrialized-c-2022.pdf

共99页,预览20页

还剩页未读, 继续阅读

声明:本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。玖贝云文库仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知玖贝云文库,我们立即给予删除!
分类:计算机 价格:5.9玖币 属性:99 页 大小:452.46KB 格式:PDF 时间:2025-01-13

开通VIP享超值会员特权

  • 多端同步记录
  • 高速下载文档
  • 免费文档工具
  • 分享文档赚钱
  • 每日登录抽奖
  • 优质衍生服务
/ 99
客服
关注