Xv6 I/O 与文件描述符
Xv6 I/O 与文件描述符
参考: xv6-riscv-book 1.2 I/O and File descriptors
Xv6 I/O 系统调用
本文会使用到如下 Xv6 的 I/O 系统调用:
系统调用 | 说明 |
---|---|
int open(char *file, int flags) | 打开一个文件,flags 用来指示读or写,返回一个文件描述符 |
int write(int fd, char *buf, int n) | 从 buf 写 n 个字节到文件描述符 fd,返回写入的字节数 |
int read(int fd, char *buf, int n) | 从文件描述符 fd 读 n 个字节到 buf,返回读取的字节数或 0 表示 EOF(文件结束) |
int close(int fd) | 释放打开的文件描述符 fd |
int dup(int fd) | 返回一个新的文件描述符,指向与 fd 相同的文件 |
open 的 flags 由 kernel/fcntl.h:1-5 提供:
flag | 说明 |
---|---|
O_RDONLY | 只读 |
O_WRONLY | 只写 |
O_RDWR | 读和写 |
O_CREATE | 不存在时新建 |
O_TRUNC | 把文件截断到 0 长度 |
这些都是用 bit 描述的,可以做或运算:
1 | fd = open("/dir/file", O_CREATE|O_WRONLY); |
Xv6 文件描述符
文件描述符就是一个整数,用来代表一个打开的 IO 对象(如文件),通过文件描述符就可以对 IO 对象进行读写操作。程序一开始就会被分配给如下惯例文件描述符:
文件描述符 | 说明 |
---|---|
0 | stdin 标准输入 |
1 | stdout 标准输出 |
2 | stderr 标准错误 |
read & write
1 | // useio.c |
这个程序从标准输入读,写到标准输出,相当于一个简化的 cat:
1 | $ useio > fff |
重定向的实现
在通过如 open 的系统调用打开一个文件时,被分配给的文件描述符总是当前可用的描述符中最小的。
用这个特性就可以实现输入输出的重定向。Xv6 的 Shell (user/sh.c:82) 里就是这么实现的。
下面的程序实现一个 cat < input.txt
的效果:
1 | // uredirection.c |
编译运行:
1 | $ cat > input.txt |
fork 和 exec 分离的一个好处就是 shell 可以在 fork 和 exec 之间优雅实现重定向,如上面的程序。如果把二者合并,提供一个 forkexec
系统调用,重定向的实现就很烦了:需要多传参数;或者在调用 forkexec 前设置 shell 进程自己的描述符,然后又改回去;或者让每个程序自己去支持重定向。
共享偏移
fork 的时候会拷贝文件描述符表,但每个文件的偏移量(读/写到哪)会在父子进程间共享。
1 | // usharedoffset.c |
运行效果:
1 | $ echo "" > output.txt |
dup
dup “复制”一个现有的文件描述符,返回的新描述符指向和原来一样的 I/O 物体(比如文件)。类似于 fork,新旧文件描述符共享 offset。
1 | // udup.c |
运行效果:
1 | $ udup > output.txt |
利用 dup,shell 就可以实现 ls existing-file non-existing-file > tmp1 2>&1
了。 2>&1
就是 2 = dup(1)
,让标准错误和标准输出指向同一个文件,并且共享偏移(一直往后写)。
EOF
1 | # By CDFMLR 2021-02-18 |
顶部图片来自于小歪API,系随机选取的图片,仅用于检测屏幕显示的机械、光电性能,与文章的任何内容及观点无关,也并不代表本人局部或全部同意、支持或者反对其中的任何内容及观点。如有侵权,联系删除。