编程语言研究书库

文件 I/O 进阶与登录记账 — lseek、空洞文件、utmp/wtmp

文件 I/O 进阶与登录记账 — lseek、空洞文件、utmp/wtmp

这一章讲什么: 两件事。第一,lseek 的全部本事——把读写指针挪到 文件任意位置(包括文件尾之后一万字节),由此引出「文件空洞」和「稀疏 文件」:一个 ls -l 显示 1,000,020 字节的文件,实际只占 8,192 字节磁盘。 第二,登录记账体系——Unix 把谁何时登录登出记在三个二进制文件里 (lastlog、utmp、wtmp),本章读完它们的格式、语义差别和访问 API, 并仿写出 lastlog 和 last 两个命令。 项目密度最高的一章,也是「读真实系统文件」的第一次实战。

1. 顶层全景:为什么需要移动文件偏移

  第 4 章:文件 = 字节流,顺序读写(偏移自动前进)
      │  新问题:记录型文件(每个用户一条定长记录)要求「直接跳到第 N 条」
      ▼
  lseek:三个基准(SEEK_SET/CUR/END)× 任意偏移
      │  偏移可以越过文件尾 → 写数据 → 文件空洞(稀疏文件)
      ▼
  应用一:lastlog 文件 = 按 UID 索引的稀疏记录数组
  应用二:wtmp 文件 = 顺序追加的事件日志,但 last 命令要逆着读
      │  登录记录怎么读?→ utmpx API / getpwent 遍历用户库
      ▼
  项目:spl_lastlog、spl_utmpdump、spl_last

图说:一条 lseek 主线串起全章。 主线例子:spl_lastlog 读一条 用户登录记录——输入一个 UID,文件偏移从 0 跳到 UID × 结构大小 处, 读出 292 字节,判断 ll_time 是否为 0。每一步都有具体的数。

2. lseek 与文件空洞

三个参数,两个妙用

off_t lseek(int fd, off_t offset, int whence);

whence 决定从哪儿起跳:SEEK_SET(文件头)、SEEK_CUR(当前偏移)、 SEEK_END(文件尾);offset 可正可负1。成功的返回值是新偏移的 绝对位置,于是两个零成本小技巧: lseek(fd, 0, SEEK_CUR) 查当前位置、 lseek(fd, 0, SEEK_END) 查文件大小。挪到文件头之前是错误(EINVAL)2。

主线例子的第一步:makefilehole——先造一个带洞的文件

程序做三件事:写 "0123456789"(10 字节)→ lseek(fd, 1000000, SEEK_END) → 再写同样的 10 字节。结果:

$ ls -l file_with_hole
-rw-r--r-- 1 stewart stewart 1000020 file_with_hole     ← 逻辑大小:10 + 1,000,000 + 10
$ ls -s --block-size=1 file_with_hole
8192 file_with_hole                                    ← 实际占用:2 个 4096 字节块
$ du --block-size=1 file_with_hole
8192

图说:中间 1,000,000 字节是「洞」——读它时 read 不报错,而是返回 一串 0(NULL 字节);磁盘上根本不分配3。第二个数据块的起点必须 是 4096 的倍数:1,000,010 之前最大的 4096 倍数是 999,424,所以那 10 个 字符落在第二块内偏移 586 处,块内其余字节全是 04。od -a -Ad 能直接 看到:地址 0 处是 0123456789,地址 1,000,000 处又是 0123456789, 中间用 * 折叠的 16 字节全零行5。

稀疏文件就是「逻辑大小远大于实际磁盘占用」的文件。数据库、虚拟机 磁盘镜像、lastlog(下一节)都是。教训:ls -l 的大小不等于磁盘成本, ls -s / du 才是6。

3. lastlog:一个按 UID 索引的稀疏数据库

文件的形状,从 man 页的 CAVEATS 里推理出来

lastlog(8) 报告每个用户最近一次登录。man 页 CAVEATS 一句「UID 跳空 会让 lastlog 长时间无输出」,加上「文件是稀疏的」,原书推理出它的结构: 一个以 UID 为下标的巨型定长记录数组——UID 为 m 的用户的记录就在 字节偏移 m × sizeof(struct lastlog) 处;从没登录过的用户没有记录, 那一格就是文件洞,程序照样要读过去(所以「无输出但费时间」)7。

图说:整条「从文档碎片推理数据结构」的推理链值得记住—— CAVEATS 的抱怨 + 稀疏性 + 大小随 UID 增长,三条线索推出数组布局。

记录结构与一个跨架构的坑

struct lastlog 在 <lastlog.h>(它只有四行,真正定义在 <bits/utmp.h>)里8:

struct lastlog {
#if __WORDSIZE_TIME64_COMPAT32
    int32_t ll_time;        /* 32 位兼容模式下是 4 字节 */
#else
    __time_t ll_time;       /* 否则是 time_t(64 位机上 8 字节) */
#endif
    char ll_line[32];       /* 登录终端,如 pts/2 */
    char ll_host[256];      /* 远程主机 */
};

图说:没有 UID 字段——位置本身就是 UID;没有用户名字段——要查 passwd 库9。ll_time 的类型随 __WORDSIZE_TIME64_COMPAT32 而变 (x86-64 跑 32 位应用兼容模式时为 4 字节),所以不能直接把它强转成 time_t* 传给 localtime(),必须先赋给一个 time_t 变量—— 指针强转不改底层宽度,赋值才做转换10。原书的可移植读法:

time_t ll_time = ll_entry.ll_time;      /* 赋值,做宽度转换 */
bdtime = localtime(&ll_time);

配套的底层功夫

  • 系统文件路径别硬编码:<paths.h> 提供 _PATH_LASTLOG、 _PATH_WTMP 等宏,换发行版路径变了程序照样找得到11。
  • 找头文件的真身:cpp -v /dev/null -o /dev/null 列出预处理器的 include 搜索顺序,<bits/utmp.h> 就藏在其中某个目录(通常 /usr/include/x86_64-linux-gnu/bits/)12。
  • 读一个结构体的惯用法:read(fd, &rec, sizeof(rec)) 后三查—— <0 是错误、==sizeof 成功、其余是「不完整读」13。

4. passwd 数据库:从 UID 找名字

lastlog 记录里没有用户名,只有隐含的 UID。/etc/passwd 是明文数据库, 每行七个冒号分隔字段:用户名:口令占位:UID:GID:注释:主目录:登录shell。 (第二字段的 x 表示真口令存在 /etc/shadow)14。

接口一族(声明在 <pwd.h>)15:

函数作用
getpwuid(uid)按 UID 查,返回 struct passwd*
getpwnam(名)按用户名查
setpwent() / getpwent() / endpwent()遍历全部记录的三件套(迭代器)
getpwuid_r 等 _r 版线程安全版,调用者提供缓冲区

两个坑:返回的指针指向静态存储,再调同类函数会被覆盖,要用就先拷贝16; getpwent() 读到文件尾时会设 errno=ENOENT(man 页没写), 遍历循环判结束时得排除它17。

5. 主线例子走查:spl_lastlog 的一条记录

程序逻辑(每步有数):

  ① open(_PATH_LASTLOG, O_RDONLY)            → ll_fd
  ② ll_file_size = lseek(ll_fd, 0, SEEK_END)  → 比如 292,000 字节
  ③ highest_uid = 292000 / sizeof(struct lastlog) - 1
        (sizeof(struct lastlog) 在该系统上 = 292:8+32+256 含填充)
  ④ setpwent();                                → 开始遍历 passwd
  ⑤ 对每条 passwd 记录:uid = pw_uid
       uid > highest_uid?   → 打印 "**Never logged in**"(从没登录过)
       否则 lseek(ll_fd, 292 × uid, SEEK_SET)  → 跳到该用户的格子
       read(ll_fd, &ll_entry, 292)             → 读一条记录
       ll_entry.ll_time == 0? → "**Never logged in**"(格子是洞)
       否则 ll_time 赋给 time_t → localtime → strftime("%c") → 打印
  ⑥ close + endpwent

图说:三类「没有登录记录」——UID 超出文件范围(②③)、读进的是洞 (ll_time==0)、passwd 里根本没有这个人(遍历天然排除)。 对照真命令的输出,差异只在日期格式(%c vs "%a %b %e %H:%M:%S %z %Y", 后者是从 shadow-utils 源码里查证的)18。

两种读文件范式的分工

原书在这里提炼出一对概念,spl_lastlog 正好是混合体19:

范式谁做偏移管理代价与收益
显式 seek+read你自己精确控制、无 API 依赖;但容易错、要自己管性能
API 式读取(getpwent/getutxent)库的程序员代码简单;API 变了你得跟着变

选择判据:文件格式稳定、随机访问需求强 → 显式;有现成库、顺序遍历 → API。

判断(我们的,不是书里的): 「API 式读取」还有个隐形的坑原书 没展开:这些迭代器(getpwent/getutxent)不是线程安全的、还依赖进程级 全局状态,多线程程序里一个线程在遍历 passwd,另一个线程调了 getpwuid, 迭代位置就被毁了。第 16 章之后写线程程序,要么用 _r 版,要么自己加锁。 如果错,会错在: 单线程程序里这个坑根本不存在——原书的教学场景 全是单线程,所以略过它是合理的简化,不是疏漏。

6. wtmp 与 utmp:事件流与它的两种读法

utmp 结构:不只记登录

struct utmp(Linux 上 <utmpx.h> 的 utmpx 与它是同一布局)的 核心是 ut_type 字段——同一个文件里混存十种事件20:

EMPTY=0  RUN_LVL=1  BOOT_TIME=2  NEW_TIME=3  OLD_TIME=4
INIT_PROCESS=5  LOGIN_PROCESS=6  USER_PROCESS=7  DEAD_PROCESS=8  ACCOUNTING=9

用户登录 = USER_PROCESS(带用户名、终端线 pts/1、来源主机); 登出 = DEAD_PROCESS(用户名常被清空,但不一定——man 页原话: 「空用户名表示该终端登出,反之不成立」)21。一条记录的其余字段: ut_line(终端)、ut_tv(时间戳)、ut_host(远程主机)、 ut_pid、ut_exit(进程退出状态)等22。

utmp 与 wtmp 的语义差别是全章最关键的一对23:

utmpwtmp
回答谁现在在线历史上发生过什么(审计)
登出时抹掉登录记录追加一条 DEAD_PROCESS
更新方式就地改写只追加

utmpx API:六个函数

POSIX 标准的是带 x 的一套(getutxent 们),Linux 里带不带 x 完全等价; 非 Linux 系统老的 utmp API 已废弃24:

setutxent();                          /* 回到文件头 */
while ( (u = getutxent()) != NULL )   /* 顺序取下一条 */
    ...
endutxent();                          /* 关闭 */
utmpxname(WTMPX_FILE);                /* 切换要读的文件(utmp 或 wtmp) */

spl_utmpdump:把二进制文件变成人话

这个「转储器」程序用 utmpx API 顺序读出每条记录,打印类型/PID/用户/ 终端/主机/时间。真实输出里能清楚看到事件的编年顺序:BOOT_TIME → RUN_LVL → 各 USER_PROCESS → 对应的 DEAD_PROCESS → 下一次 BOOT_TIME……25 它同时是调试 last 仿写版的探针。

7. 主线例子第二程:spl_last——逆着时间走,括号配对的算法

问题的抽象:把登录登出看成括号串

删掉不相关字段后,wtmp 简化成一串事件。把「在 pts/N 登录」记作 [N、 「同一终端登出」记作 ]N,样本数据变成26:

[a  [0  ]0  ]a  [a  ]a  [1  [2  [4  [5  [6  ]5  ]6  ]4  ]1  ]2

匹配规则:同一 N 的左右括号,越靠左的右括号配越靠左的左括号; 配对成功的 → 一次完整会话(有起止时间);配不上的左括号 → 「still logged in」或「gone - no logout」。注意括号可以交错([1 [2 ]1 ]2), 栈解决不了(得弹出再压回),要用双向链表:倒着扫,遇到右括号存进 链表,遇到左括号去链表里找第一条同线右括号,找到就配对、删除、打印27。

倒着读文件:get_prev_utrec 的偏移芭蕾

last 从新到旧输出,所以文件要倒读。关键函数用两个 static 变量记住 状态:is_first(首调定位到 lseek(fd, -utsize, SEEK_END))和 saved_offset(下次要读的位置)28。每读完一条,文件偏移停在该条 之后,要读前一条得先 lseek(fd, -2*utsize, SEEK_CUR) 退两条; saved_offset 减到 <0 就是文件头,收工29。

处理规则(逆时间视角)

遇到的记录动作
DEAD_PROCESS(有 line)存入 saved_ut_recs 链表
USER_PROCESS链表找同 line 的登出:找到→打印起止+时长、删节点;没找到→看 last_shutdown_time:>0 打「gone - no logout」,==0 打「still logged in」
BOOT_TIME记录 last_boot_time;清空链表(更早的登录都属于上一个时代,不可能再配对了)
SHUTDOWN_TIME(由 RUN_LVL + user=="shutdown" 识别,自定义类型值 32)记录 last_shutdown_time;打印时结束时间是下一次 boot 的时间;清空链表

图说:关机/重启是「时间纪元分界线」——分界线之前没配对成功的登出, 永远等不到配对了。 最后一行照原样打印 wtmp begins <第一条记录的时间> 30。

用户空间缓冲:留给习题的优化

一条一条 read 效率低(第 4 章的教训)。思路:一次读 NRECS 条进数组, 倒着从数组里取(索引从返回值-1 减到 0 再重新装载),文件偏移一次退 NRECS × UTSIZE31。接口设计成了四个函数(init_wtmp / load_buf / get_next_utrec / wtmp_finalize)——「先设计接口再填实现」的示范。

8. 作者的判断与证据

有明证的:

  • lastlog 的数组布局是从 man 页 CAVEATS + 稀疏性推理出来的, 推理链完整展示7。
  • 「x86-64 兼容 32 位应用 ⇒ ll_time 是 int32_t」有 <bits/wordsize.h> 的头文件原文佐证10。
  • wtmp 登出记录「空用户名 ⇏ 登出」的单向蕴含,直接引 man 页原话21。
  • spl_lastlog 与真 lastlog 的输出 diff 对照,差异定位到格式串,并追到 shadow-utils 源码18。

作者的判断(工程取舍):

  • 「passwd 无序 UID 是否先排序再读 lastlog」——明确说本版不排序, 「生产版要研究这个问题」,留作习题31。
  • 「spl_last 一次读一条、不做用户缓冲」——教学优先于性能,明说31。
  • 「shutdown 的识别用 RUN_LVL+user=='shutdown'+line=='~'」—— 这是经验规则而非文档规范,作者自己也说「其他 runlevel 记录忽略」30。

9. 边界与局限

  • utmp 格式机器相关(man 页两次警告),程序只能在编译它的架构上 读这些文件22。
  • POSIX 只标准化 utmpx;Linux 的 utmp/utmpx 是同一结构,别的 Unix 上未必24。
  • PAM、systemd-logind 等新式记账路径没展开——现代发行版里 utmp 的写入者已不只是 getty/login23。
  • spl_last 不处理 -f 指定别文件、用户/终端过滤等真命令的选项; wtmp 轮转(wtmp.1)不支持25。
  • 用户缓冲方案只给了接口设计,实现留作习题31。

10. 可带走的

主线例子,一行复述: lseek(fd, 0, SEEK_END) 得文件大小 → 最高 UID = 大小/292 − 1 → lseek(fd, 292×uid, SEEK_SET) → read 一条 292 字节记录 → ll_time==0 就是「Never logged in」。

  1. lseek 三基准两妙用:SEEK_SET/CUR/END;查当前位置、查文件大小。
  2. 偏移可以越过文件尾,写数据就造出「洞」;洞读作 0;ls -l 是逻辑 大小,du/ls -s 才是磁盘占用——稀疏文件的核心认知。
  3. 系统文件路径用 <paths.h> 宏(_PATH_LASTLOG 等),不硬编码。
  4. 跨架构的类型宽度陷阱:条件编译出的 int32_t/time_t 字段,必须 「赋值给 time_t 变量」再传给 localtime,不能强转指针。
  5. 读定长记录三查:<0 错、=sizeof 成、其余不完整读。
  6. getpwuid 返回静态存储,要用先拷贝;getpwent 遍历到尾会设 ENOENT(man 页不写)。
  7. 两种读文件范式:显式 seek(精确、自担风险)vs API 迭代(省事、 跟随库);spl_lastlog 是混合体。
  8. utmp=谁现在在线(登出抹记录);wtmp=历史审计(登出追加)。
  9. last 的括号匹配:同线同配、左配最近左;栈不够(可交错),用双向 链表;倒读文件靠 static 记住偏移。
  10. BOOT/SHUTDOWN 是时间纪元分界线:遇到就清空未配对链表。

11. 原文地图

主题原书章原文位置
lseek 三基准与妙用Chapter 5: File I/O and Login Accountingtext/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:67(搜「off_t lseek」) · :118(搜「current_pos」)
文件空洞与 makefilehole同上:147(搜「NULL bytes」) · :211(搜「1000020」) · :220(搜「8192」) · :262(搜「od -a」)
稀疏文件定义同上:281(搜「sparse files」)
lastlog 布局推理同上:404(搜「Large gaps」) · :420(搜「proportional to the user's user ID」)
struct lastlog 与条件类型同上:504(搜「struct lastlog」) · :963(搜「typecast」)
paths.h 与头文件搜索同上:328(搜「_PATH_WTMP」) · :477(搜「search starts here」)
读结构体三查同上:549(搜「READING STRUCTURES」)
/etc/passwd 七字段同上:699(搜「linus:x:501」)
getpwuid/getpwent 与 ENOENT 坑同上:712(搜「endpwent」) · :728(搜「static memory」) · :855(搜「ENOENT」)
spl_lastlog 程序同上:1040(搜「spl_lastlog.c」) · :1077(搜「highest_uid」)
输出对照与格式串考证同上:1167(搜「%a %b %e %H:%M:%S %z %Y」)
显式读 vs API 读同上:1196(搜「explicit seeking」)
utmp 结构十种类型同上:1403(搜「EMPTY」) · :1433(搜「struct utmp」)
utmp/wtmp 语义差别同上:1558(搜「audit file」) · :1623(搜「essentially erased」)
utmpx API同上:1506(搜「obsolete」) · :1541(搜「endutxent」)
空用户名的单向蕴含同上:1874(搜「NULL username」)
spl_utmpdump 输出样本同上:1828(搜「RUN_LVL」)
括号抽象与链表算法同上:1934(搜「[a」) · :1949(搜「squirrel」) · :1958(搜「stack」)
倒读文件的偏移逻辑同上:2165(搜「is_first」) · :2256(搜「2*utsize」)
spl_last 主循环规则同上:2474(搜「switch」)
用户空间缓冲设计同上:2621(搜「NRECS」)

Footnotes

  1. 出处:「Chapter 5: File I/O and Login Accounting」第 54–88 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:67,搜「off_t lseek」)。 ↩

  2. 出处:「Chapter 5: File I/O and Login Accounting」第 109–127 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:118,搜「current_pos」)。 ↩

  3. 出处:「Chapter 5: File I/O and Login Accounting」第 138–153 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:147,搜「NULL bytes」)与第 164–169 段的 makefilehole 示例。 ↩

  4. 出处:「Chapter 5: File I/O and Login Accounting」第 232–245 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:240,搜「999,424」)。 ↩

  5. 出处:「Chapter 5: File I/O and Login Accounting」第 257–277 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:262,搜「od -a」)。 ↩

  6. 出处:「Chapter 5: File I/O and Login Accounting」第 278–281 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:281,搜「sparse files」)。 ↩

  7. 出处:「Chapter 5: File I/O and Login Accounting」第 396–435 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:404,搜「Large gaps」) · :420(搜「proportional to the user's user ID」)。 ↩ ↩2

  8. 出处:「Chapter 5: File I/O and Login Accounting」第 437–513 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:504,搜「struct lastlog」)。 ↩

  9. 出处:「Chapter 5: File I/O and Login Accounting」第 568–577 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:570,搜「user ID」)。 ↩

  10. 出处:「Chapter 5: File I/O and Login Accounting」第 952–974 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:963,搜「typecast」)。 ↩ ↩2

  11. 出处:「Chapter 5: File I/O and Login Accounting」第 321–345 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:328,搜「_PATH_WTMP」)。 ↩

  12. 出处:「Chapter 5: File I/O and Login Accounting」第 455–486 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:477,搜「search starts here」)。 ↩

  13. 出处:「Chapter 5: File I/O and Login Accounting」第 549–566 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:549,搜「READING STRUCTURES」)。 ↩

  14. 出处:「Chapter 5: File I/O and Login Accounting」第 689–710 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:699,搜「linus:x:501」)。 ↩

  15. 出处:「Chapter 5: File I/O and Login Accounting」第 708–722 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:712,搜「endpwent」)。 ↩

  16. 出处:「Chapter 5: File I/O and Login Accounting」第 726–732 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:728,搜「static memory」)。 ↩

  17. 出处:「Chapter 5: File I/O and Login Accounting」第 843–861 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:855,搜「ENOENT」)。 ↩

  18. 出处:「Chapter 5: File I/O and Login Accounting」第 1146–1170 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:1167,搜「%a %b %e %H:%M:%S %z %Y」)。 ↩ ↩2

  19. 出处:「Chapter 5: File I/O and Login Accounting」第 1194–1214 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:1196,搜「explicit seeking」)。 ↩

  20. 出处:「Chapter 5: File I/O and Login Accounting」第 1399–1424 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:1403,搜「EMPTY」)。 ↩

  21. 出处:「Chapter 5: File I/O and Login Accounting」第 1871–1876 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:1874,搜「NULL username」)。 ↩ ↩2

  22. 出处:「Chapter 5: File I/O and Login Accounting」第 1431–1462 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:1433,搜「struct utmp」)。 ↩ ↩2

  23. 出处:「Chapter 5: File I/O and Login Accounting」第 1551–1560 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:1558,搜「audit file」) · :1623(搜「essentially erased」)。 ↩ ↩2

  24. 出处:「Chapter 5: File I/O and Login Accounting」第 1502–1549 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:1506,搜「obsolete」) · :1541(搜「endutxent」)。 ↩ ↩2

  25. 出处:「Chapter 5: File I/O and Login Accounting」第 1826–1859 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:1828,搜「RUN_LVL」)。 ↩ ↩2

  26. 出处:「Chapter 5: File I/O and Login Accounting」第 1884–1936 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:1934,搜「[a」)。 ↩

  27. 出处:「Chapter 5: File I/O and Login Accounting」第 1937–1960 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:1949,搜「squirrel」) · :1958(搜「stack」)。 ↩

  28. 出处:「Chapter 5: File I/O and Login Accounting」第 2160–2210 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:2165,搜「is_first」)。 ↩

  29. 出处:「Chapter 5: File I/O and Login Accounting」第 2212–2270 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:2256,搜「2*utsize」)。 ↩

  30. 出处:「Chapter 5: File I/O and Login Accounting」第 2466–2549 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:2474,搜「switch」)。 ↩ ↩2

  31. 出处:「Chapter 5: File I/O and Login Accounting」第 2594–2658 段(text/11-ch05-chapter-5-file-i-o-and-login-accounting.txt:2621,搜「NRECS」)。 ↩ ↩2 ↩3 ↩4

On this page