感悟篇: 阅读 Unix 分时系统的进化

论文名: The Evolution of the Unix Time-sharing System -- Dannis M. Ritchie
【感悟篇: 阅读 Unix 分时系统的进化】丹尼斯-里奇 回顾了与 肯-汤普森 等在贝尔实验室创造 Unix 的历程, 然后介绍了一下 Unix 中最重要的几个部分, 包括:
文件系统

  1. i-list: 一个由 i-nodes 组成的数组, 每个 i-node 描述一个文件. i-node 里包含文件的元数据: 例如保护模式, 类型和大小, 以及内容所在的物理位置.
  2. 目录: 一种特殊的文件, 包含了一系列的文件名以及相关的 i-number.(我觉得 i-number 应该就是 i-node 的 ID 吧)
  3. 特殊的文件用于描述设备. 特定的 i-number 对应特定的设备.
进程控制机制 shell 执行命令的大概的步骤是这样:
  1. shell 从终端读取命令
  2. 新建一个进程
  3. 子进程执行命令
  4. 同时, shell 开始等待直到子进程执行完毕
  5. shell 回到第一步
感觉挺有趣的, 我用 elixir 模拟了一下:
defmodule MF.PC do def loop do cmd = IO.read(:line) pid = self()spawn(fn -> exec(cmd, pid) end)wait() enddefp exec(cmd, pid) do send(pid, {:done, String.upcase(cmd)}) enddefp wait do receive do {:done, msg} -> IO.puts(msg) loop() end end end

无论输入什么, 都返回大写的输入.
iex(2)> MF.PC.loop hello HELLOhow are u HOW ARE Ufine FINEgood bye GOOD BYE

好吧, 挺无聊的, 只是理解一下Unix 里进程消息传递的模式.
论文里提到很好玩的一个 bug, 就是一开始 chdir 命令, 也就是 cd, 也是像上面那样执行的, 结果, 只有子进程的工作目录变更了, 对 shell 本身完全不起作用. 所以他们把 cd 变为特殊命令, 只在当前进程里执行.
论文里还提到一个有趣的 bug, 如果在一个脚本文件 "comfile" 里有如下命令:
ls who

然后我们在 shell 里执行:
sh comfile >output

预想的结果是 lswho 的执行结果被依次写入到 output 文件中. 但实际上, 由于最初 unix 将文件的 io指针保存在了执行 "打开文件" 的进程里, 也就是主 shell 进程, 而执行写入操作的, 是我们用 sh 命令开启的子 shell, 所以文件的 io指针始终没有变化, 导致who的输出把 ls的输出覆盖了. 为了修复这个问题, 他们在系统中添加了一个表, 保存了全部的打开状态的文件的 io 指针.
说个题外话, 我突然想到在 elixir 里好像不能指定文件的写入位置, 查了一下是可以的:
iex(4)> :file.read_file "comfile" {:ok, "ls\nwho\n"} iex(5)> {:ok, device} = :file.open "comfile", [:raw, :write] {:ok, {:file_descriptor, :prim_file, %{ handle: #Reference<0.1011150086.1394212882.243351>, owner: #PID<0.110.0>, r_ahead_size: 0, r_buffer: #Reference<0.1011150086.1394212865.243949> }}} iex(6)> :file.pwrite(device, 1, 'hello') :ok iex(7)> :file.read_file "comfile" {:ok, <<0, 104, 101, 108, 108, 111>>} iex(8)> <<104, 101, 108, 108, 111>> "hello"

最后是还提到了一下管道操作符 | 和 C 语言的来历.

    推荐阅读