一切皆XX,这一看就是本质论以及整体论信徒的呐喊!
引.关于树型模型
树型模型和一切皆文件没有直接的关系,只是它们的经历很类似。
近期看了一本老婆买的书,《视觉繁美》。在古代,人们对树情有独钟,最终包括组织架构,包括分类都变成了树,读到了后面的章节,我才发现,原来树型模型并不是一切的本质,它是在公元前10000左右的美索不达米亚的新月地带人为产生的,即农业革命导致了人们产生了树型模型的观念。
树型模型,很有秩序感,任意两节点之间都是单向连通,任何节点都可以追溯和被追溯,一切显得很完美。它甚至可以囊括世间万物。以分类学为例子,古希腊哲学家波菲利的树是一个典型,他事先定义了一切的属性,然后用“有该属性”还是“没有该属性”对事物进行分类,最终所有的东西都可以在这棵树上找到一个叶子作为自己的位置。
当一切在树型模型下都运行得很美好的时候(包括经济,政治,公司经营等),人们发现了原来更本质的思想是互联的思想,于是当链接成了一切的时候,树型模型就不再适用了。今天,我们处在一个复杂网络的时代,复杂网络无所不在,包括交际圈,城市,股票,气象,以及我们的大脑本身。然而并不能因此否定提出“一切都是树”的古人,正是因为他们的模型简单,人类文明才会诞生并发展,今天我们迷茫于混沌,最终我们会发现,混沌是有序的混沌。
我想说的是,虽然一些观点最终被证明是错的,但是一开始的时候必须那么说,因为简单是引起前进的动力,当遇到阻碍物或者瓶颈的时候,事情会横向发展,即朝着复杂和混乱方面发展,但是复杂和混乱不是目标,而是试图找一个继续向上的洞,或许要做些调整,然后越过障碍后继续简单向上,正如植物生长一样,生机勃勃。一切皆文件是UNIX的信条之一,如今它也遇到了一些挑战。
关于一切皆文件的反例
procfs,进程文件系统。是UNIX系统中展示进程状态以及相关数据的一个内存文件系统。它有着古老的历史,可能在最开始就成了“一切皆文件”的事实上的布道者,你看,就连进程状态也都可以表现成文件。
一切皆文件,其最原始的含义是这样的,即文件操作拥有统一且简单的接口,在计算机的公元前10000年那个年代,人们把所有的操作都可以归结为读,写,控制,因此read,write,ioctl就成了最古老的文件操作集,如果试图将所有的操作都归到文件操作,那就要建立一系列的映射,这些映射抽象出了机制和策略,这个映射是一对多的,一个统一的操作原语表示机制,而多个不同的操作实现表现为策略,最终,VFS诞生了!
有了VFS,人们就方便了,将一切都实现为一个文件系统。直到今天Linux还在做这样的事,并且好像不像UNIX那样会停下来,当然,这是后话。当这种事情做的足够多的时候,当安全需求越来越多的时候,正如IP网络后来面对的问题一样,简单的基于属主的ACL便不足以映射一切的安全控制规则。问题的关键在于,VFS导致出现的文件系统类型以及数量是不受控制的,而UNIX的文件ACL却是确定的,因此就需要作平行于VFS的另一个映射,也就是另一个机制到策略的映射,如果可以,我可以管它叫VACL。
然而,没有出现VACL,因为ACL的粒度太粗,其语义仅仅针对文件属主,它只是说“能还是不能”,并无法表示“能的话,必须怎么做”,可是后来,出现了类似波菲利树型分类的一个东西,叫做“能力”,就是说,将能想到的所有的操作都用一个二进制位表示,如果一个实体有这个操作的权限,则其为1,否则为0,这样就产生了UNIX的能力模型,即POSIX Cap,一切都显得很完美。但是,不同于后来的大众分类索引法取代树型分类索引法(实际上,直到今天还有人鼓吹树型模型!),POSIX Cap并不是很好用。procfs同样也不适合用Cap来管理安全性!
procfs中应有尽有。该文件系统的内容是自动生成的,每一个进程在里面有一个目录,目录下存在该进程的属性,试问,谁来定义针对这些文件的操作的Cap,如果是系统,那么系统在生成一个进程的时候,如何知道怎么定义;如果是用户,那么无疑在fork/exec和procfs之间增加了一个HOOK,这太复杂了。procfs的本来的目的很简单,有两类:
- 导出系统信息
- 导出进程信息
不管怎样都是为了增强其调试功能。不管怎样,都不能试图使用procfs来做一些违反UNIX原则的事。第一个问题在于procfs导出的信息包括进程的地址空间,隔离进程地址空间是UNIX乃至所有操作系统的根本原则,只要将其展示在procfs,就可能会被read,write,mmap...很多的UNIX,包括BSD,都因为这个出过事情,因此后来的版本就干脆去掉了procfs;第二个问题在于内核空间该不该处理信息格式的问题,由于VFS是HOOK在内核态的,因此各种实际文件系统的操作也在内核态实现,于是就会有大量的格式化的操作在内核进行,然而不应该再内核做这些,如果直接导出二进制数据,却又违反了procfs的本意。为什么不修补漏洞和问题而干脆让procfs下课,这正是体现了UNIX设计上的纯粹主义,与之相反是Linux的折中主义。
#p#
围绕着procfs的去留,有很多的辩论,辩论围绕两方面:
- procfs应该下课:完全使用sysctl接口来代替procfs。既然作为进程属性一部分的地址空间无法导出,那么何必保留另外90%+呢?
- procfs应该保留:考虑到sysctl并不是在每一个UNIX系统都是标准的工具集的一部分导致的互操作的问题,建议保留统一接口的procfs。
不管怎样,都是围绕着UNIX哲学问题的争论。Linux却完全抛开这些,实现了自己的procfs。
Linux的折中
Linux没有抛弃procfs,而是修补了它的关键问题,至于另外一些不关键的问题,Linux社区并不在意。在procfs的VFS操作集的定义上,Linux采用了以下的定义:
- #define mem_write NULL
- #ifndef mem_write
- //一个吓人的注释!
- /* This is a security hazard */
- static ssize_t mem_write(struct file * file, const char * buf,
- ...
- #endif
- static struct file_operations proc_mem_operations = {
- .llseek = mem_lseek,
- //read操作有很多限制,不允许访问别的进程的地址空间
- .read = mem_read,
- //NULL定义write操作
- .write = mem_write,
- .open = mem_open,
- //没有mmap的实现
- };
这就避免了安全问题!
Linux正在一切皆文件的路上越走越远,并且还将继续走下去,如今你能看到很多非常规的FS,比如procfs,sysfs,devfs,debugfs,cpuset,cgroup,sockfs等等...Linux小心翼翼地对包括procfs在内的一切非常规文件系统进行管理,哪些可以read,哪些可以write,哪些要严禁怎样等等,都要经过周密考虑,想来也只有Linux这种开放的开发平法平台才敢这么做吧,有了任何漏洞可以马上被找出来然后最快的速度给与修正!