第1页
依赖类型的窥视和戳
托马斯·哈格伦
ProgLog会议演讲2006-12-13
(基于2006年1月的Programatica会议演讲)
第2页
今日房屋(1)
使用GHC(格拉斯哥Haskell编译器)
编译器很大!
运行时系统很大!
如果我们在Haskell中实现微内核,这将是
最大的部分。
它能被信任吗?
能否正式验证关键属性?
第3页
今日之家(2)
Haskell类型的系统能满足我们的需要吗?
H(硬件)monad接口[1]依赖于
运行时检查
为了安全
虽然安全性没有受到影响,但直到
运行时。
对于某些事情,
无声截断
被使用,所以bug会
导致错误行为,而不是错误消息。
我们不知道如何安全地处理DMA。
第4页
在机器级别。。。
用于内存访问的基本体
键入Addr=Word32
peek∈地址
→
H字8
poke∈地址
→
单词8
→
H()
对应CPU直接支持的指令。
不安全的!
允许覆盖任何内存位置。
由于使用不受限制,没有有趣的运行时不变量可以
放心。
第5页
通过抽象实现安全
安全=保持不变。。。
不变量只能保证用于在特定抽象之上编写的程序
水平。
显然,大多数代码都应该在安全的抽象之上编写!
第6页
硬件Monad示例
安全依据
抽象数据类型
+
无声截断
:
类型PAddr=(物理页面,POffset)
类型PhysPage--摘要
类型POffset=Word12
getPAddr∈PAddr
→
H字8
setPAddr∈PAddr
→
单词8
→
H()
allocPhysPage∈H(可能是PhysPage)
安全依据
抽象数据类型
+
运行时边界检查
:
data MemRegion——抽象
类型偏移=字32
pokeByteOff∈Storable存储字节
⇒
MemRegion公司
→
抵消
→
一
→
H()
peekByteOff∈Storable可存储
⇒
MemRegion(MemRegion)
→
抵消
→
H a公司
第7页
另一个实验:阿尔法版本的房子?
(1) 从一个最小的编译器和运行时系统开始
希望可以正式验证关键属性
在不牺牲正式验证的情况下扩展它
(2) 利用依赖类型
更多安全约束可以作为类型安全捕获
更少的错误!
更多的安全检查可以从运行时转移到编译时
潜在的更好性能!
第8页
一个最小编译器和运行时系统
编译器:
Front-end:Alfa(编程环境,类型检查)
Back-end:~750行Haskell代码(不包括标准库)
输入:阿尔法支持的语言的一个有用子集
输出:C的一个小子集
运行时系统大小:~630行C
包括一个简单的复制垃圾收集器:约120行C
编译时间可配置,用于托管和裸机使用
第9页
编译器和运行时系统
基于<ν,G>机器[2]
设计用于支持多处理器机器上的并行执行
易于支持低延迟中断处理
非常简单的内存管理
没有堆栈,一切都在堆中
GC的根集:只有一个指针(Γ,当前的redex)
第10页
一个例子
你好,世界!
在电脑屏幕上显示消息的可引导可执行文件。
需要写入位于特定位置的文本屏幕缓冲区
PC物理内存布局中的绝对地址。
第11页
再次偷窥
用于内存访问的基本体
键入Addr=Word32
peek16∈加法器
→
H单词16
poke16∈地址
→
单词16
→
H()
不安全的!
允许覆盖任何内存位置。
由于使用不受限制,没有有趣的运行时不变量可以
放心。
第12页
针对有限内存访问的抽象
安全界面:
可读∈地址
→
道具
可写∈地址
→
道具
peek16∈(a∈Addr)
→
可读a
→
H单词16
poke16∈(a∈Addr)
→
可写a
→
单词16
→
H()
用于声明构成安全内存访问的元素:
不安全可修改∈(a∈Addr)
→
可读a
不安全可写∈(a∈Addr)
→
可写a
依赖类型!
命题作为类型!
让我们谈谈类型级别的值(内存地址)。
第13页
文本屏幕缓冲区访问(1)
限制对文本屏幕缓冲区的访问
数据范围=范围{lo,hi∈Addr}
屏幕∈范围
屏幕=范围0xb8000 0xb8fff
InRange∈范围
→
地址
→
道具
screenPoke∈(a∈Addr)
→
InRange屏幕a
→
单词16
→
H()
实施
屏幕扑克
可写屏幕∈(a∈Addr)
→
InRange屏幕a
→
可写a
writableScreen a r=不安全.writable
screenPoke∈(a∈Addr)
→
InRange屏幕a
→
单词16
→
H()
screenPoke a r d=poke16 a(可写Screen a r)d
第14页
文本屏幕缓冲区访问(2)
使用基于谓词的子类型
让
∑T P
表示满足P的T型子集
值是对:(value
x个
∈T,P的证明
x个
)
我们现在可以定义
类型ScreenAddr=∑Addr(InRange屏幕)
screenPoke∈屏幕地址
→
单词16
→
H()
第15页
用于显示文本的抽象
在特定屏幕位置放置具有属性的字符
类型属性=。。。
x范围=范围0 79
yRange=范围0 24
type Column=∑Word32(InRange x范围)
type Row=∑Word32(InRange yRange)
putac∈柱
→
排
→
属性
→
字符
→
H()
输出文本(必要时滚动)
putString∈String
→
H()
需要处理额外安全约束的代码是
小而本地化!
第16页
阿尔法证明(或缺乏阿尔法证明)
计算给定屏幕坐标的内存地址:
charAddr∈列
→
排
→
屏幕地址
charAddr(x,xr)(y,yr)=(屏幕+2*(80*x+y),
?
)
的引理
字符地址
:
所有x,y。
InRange x范围x
⇒
InRange y范围y
⇒
InRange屏幕(低屏幕+2*(80*x+y))
可决定,应证明自己:
将其公式化为类型的表达式
布尔
.
布尔值是
真的
是
琐碎的
.
但是
阿格达的口译员似乎是
效率太低
来处理这个。。。
其他人对此问题进行了研究[3]。
第17页
扩展静态检查示例
显示适合屏幕的字符串
适合∈Word32
→
字符串
→
道具
put∈(x,y∈Word32)
→
(s∈字符串)
→
适合x s
→
InRange y范围y
→
H()
对“Hello world”程序的正确性有用:-)
第18页
你好,世界!
main=puts 35 12“你好,世界!”()()
静态检查:
文本屏幕缓冲区外没有戳。
消息会显示在屏幕上(不会换行到下一行)。
第19页
问题
模运算
简单的不等式不成立!
i<基础+尺寸
⇔
i-base<尺寸?
基数≤i
⇒
基数≤i+1?
终止检查
终止检查器无法识别使用的递归形式。
第20页
未来的工作?
对当前的H monad接口进行更多操作。
使更复杂的内存访问模式安全:
操作链接的数据结构。
安全DMA编程。
一个独立类型的H单子(略图)?
H M型
之前
M(M)
邮递
一
peek∈(a∈Addr)
→
月日(月日)
poke∈(a∈Addr)
→
T型
→
H月M日
→
T] ()
类型中的分离逻辑?
[4]
验证编译器和运行时系统属性。
(使用[5]中的想法?)
第21页
结束
还有问题吗?
第22页
工具书类
[1] 我们:
A原则
在Haskell中构建操作系统的方法
,
ICFP 2005年
.
[2] Lennart Augustsson和Thomas Johnsson:
平行
用<Γ,G>机器进行图约简
,
1989年FPCA。
[3] 本杰明·格雷戈雷(Benjamin Gregoire)和泽维尔·勒罗伊(Xavier Leroy),
A类
强还原的编译实现
,
ICFP 2002年
.
[4] 约翰·雷诺兹(John C.Reynolds):
《分离逻辑:共享可变数据结构的逻辑》,LICS 2002。
第23页
工具书类
[5]
泽维尔·勒罗伊
:
正式
编译器后端的认证,或:使用
举证助理
,
POPL 2006年
.