9p基础知识
intro(9P) - Plan 9 from User Space (9fans.github.io)
9fans/plan9port: Plan 9 from User Space (github.com)
1. 简介
Plan 9 服务器是提供一个或多个分层文件系统(文件树)的代理,Plan 9 进程可以访问这些文件系统。服务器响应客户端层次结构以及创建、删除、读取和写入文件的请求。
客户端传输请求(T-message),然后服务器返回回复(R-messages)。
传输的信息message均由n个字节组成,其中用两字节表示数据长度n,即后面n字节都是数据。字符串用UTF-8编码,并且字符串不用空字符作为结尾,空字符在9p中是非法的。
以下七个字节是每个message中最基础的七个字节:
- size[4]:每个message开始都是一个4字节的代表整个message的长度,这个长度包括4字节自己。
- type[1]:接下来一个字节代表这个message的类型,在
fcall.h
这个头文件中可以看到有哪些类型 - tag[2]:接下来两个字节代表这个message的标签,用来标识验证,即返回的R-message和发送的T-message的tag应该相同
剩下的字节是不同大小的参数,例如有一个参数parameter[n],则具体表现为n[2]+parameter[n],即两个字节描述参数长度n,后面n个字节是参数本身。字符串string[s]同理是s[2]后面跟上s个字符。9p 名称可以包含除斜杠之外的任何可打印字符(即十六进制 00-1F 和 80-9F 之外的任何字符。)
1 | MESSAGES |
- 如果R-message的返回的tag比T-message大1或者是Rerror的话说明请求失败了,如果标签是Rerror后面ename字段还会包含一个描述失败原因的字符串。
- version的message代表协议版本并且表明了系统最大的可处理的消息大小。它还初始化连接并中止连接上所有未完成的 I/O。版本请求之间的消息集称为会话(session)。
- 大部分T-message包括fid,一个四字节32位,标志一个在服务器上的当前文件。fid和fd即文件描述符有些类似,但是fid不限于为I/O打开的文件:正在检查的目录、被 stat 调用访问的文件等等——所有被操作系统操作的文件都由 fids 标识。fid由客户端决定,一个连接上的所有请求共享同一个fid空间;当多个客户端共享一个连接时,管理共享的代理必须保证没有两个客户端选择相同的fid。
- 在attach类型的message中提供的fid会被服务端指向文件树的根。attach操作将用户标识到服务器,并且可以指定由服务器提供的特定文件树(对于拥有多个文件树的服务器)。
- 在attach中有个afid是用来代表权限的,afid是在auth阶段进行建立的,随后通过读写操作进行交换验证信息(不是由9p明确定义的),一旦验证协议完成了,在attach中使用afid就能获得访问权限。
- walk类型的message中使服务器将与 fid 关联的当前文件走到旧当前文件或其子目录之一的目录中的文件。Walk 返回一个指向结果文件的新 fid,通常客户端会保持一个根的fid,然后从这个根的fid去walk作为导航。
- 一个客户端可以同时发送多个T-message,不用等待R-message回复。但是不同的T-message必须要有不同的标签tags。
- 对auth、attach、walk、open、create请求的回复R-message都会包含一个qid,qid代表在服务端对于一个文件的独一无二的id,当且仅当它们的 qid 相同时,同一服务器层次结构上的两个文件才是相同的。(服务端可以用多个fid去指向服务器上的一个文件)。qid[13]是一个拥有13字节的标志,第一个字节代表这个文件是目录、仅追加文件(AOF)、等等。接下来是两个无符号整数,一共4+8字节,前4个字节代表qid的版本,版本代表这个文件的版本,即修改后文件的版本会增加;后8个字节代表qid的path路径,path路径是层次结构中所有文件中唯一的整数。如果在同一目录中删除并重新创建同名文件,则qids的新旧路径组件应该不同。
- 在当前目录一个已经存在的文件可以被打开open,或者一个新文件可以被创建create。在打开文件的给定偏移量处(offset)的字节数(count)操作的 I/O 是通过读取(read)和写入(write)完成的。
- 客户端需要clunk掉任何不需要的fid,remove操作是用来删除文件的。
- Openfd 是 Unix 实用程序使用的扩展,它允许传统的 Unix 程序将其输入或输出附加到 9P 服务器上的 fid。
- stat操作获得文件的信息,包括文件名字,访问权限(读、写、执行对于所有者、组、所有人的权限),访问和修改时间,以及所有者和组标识。所有者和组标识是文本名称。wstat操作允许修改文件的某些信息。
- flush操作是用来终止请求的,当客户端发送了一个Tflush请求时,服务端需要立刻回复一个Rflush,客户端必须等到Rflush回复到达才能重新使用旧的标签old tag。
- 因为message的大小是可变的,所以会出现message大小过长的情况(比如当文件名太长的时候),在大多数此类情况下,服务器应该生成错误而不是修改数据以适应,例如通过截断文件名。例外情况是,如果需要,应该截断 Rerror 消息中的长错误字符串,因为该字符串只是建议性的,并且在某种意义上是任意的。
- 大多数程序看不到9p协议,9p是在内核中将访问转为9p message。
目录
目录是通过在权限参数中设置 DMDIR 的 create 创建的。可以使用 read(9P) 找到目录的成员。所有目录都必须支持遍历目录 ..表示父目录,按照惯例,目录不包含 .. 或 . 的显式条目。 服务器树的根目录的父级是它自己。
2. 例子
1 | 与9p建立连接 |