大多时候,高级别I/O函数工作良好,没必要直接用Unix I/O,为何需学习?
- 了解Unix I/O将帮助理解其他系统概念。I/O是系统操作不可或缺的部分,因此经常遇到I/O和其他系统概念之间的循环依赖
- 有时必须用Unix I/O,用高级I/O不太可能或不合适,如标准I/O库没提供读取文件元数据的方式,此外I/O库存在一些问题
1. Unix I/O
输入/输出(I/O)是主存和外部设备之间复制数据的过程,在 Linux 中,文件就是字节的序列。所有的 I/O 设备(如网络、内核、磁盘和终端等)都被模型化为文件,而所有的输入和输出都被当作对相应文件的读和写来执行。这种将设备映射为文件的机制,允许内核引出简单、优雅的应用接口Unix I/O,使得所有输入和输出都以统一的方式执行,如 open()/close() 打开/关闭文件,read()/ write() 读/写文件。seek()改变当前文件位置。Unix I/O主要分为两大类:
为区分不同文件的类型,会有一个 type 来进行区别:
- 普通文件:包含任意数据
- 目录:相关文件组的索引
- Socket:用于另一台机器上的进程通信
还有一些特别的类型仅做了解:命名管道(FIFOs)、符号链接、字符和块设备
普通文件
普通文件包含任意数据,应用程序通常需区分文本文件和二进制文件。前者只包含 ASCII 或 Unicode 字符。除此之外的都是二进制文件(对象文件, JPEG 图片, 等等)。内核不能区分出区别。
文本文件是文本行的序列,每行以 \n 结尾,新行是 0xa,和 ASCII 码中LF一样。不同系统判断行结束的符号不同(End of line, EOL),Linux & Mac OS是\n(0xa)等价line feed(LF),而Windows & 网络协议是\r\n (0xd 0xa)等价Carriage return(CR) followed by line feed(LF)
目录
目录包含一个链接(link)数组,且每个目录至少包含两记录:.(dot) 当前目录、..(dot dot) 上层目录
操作命令主要有 mkdir, ls, rmdir。目录以树状结构组织,根目录是 /(slash)。
内核会为每个进程保存当前工作目录(cwd, current working directory),可用 cd 命令进行更改。通过路径名来确定文件的位置,分为绝对路径和相对路径。
2. 文件操作
2.1 打开文件
打开文件会通知内核已准备好访问该文件
int fd; // 文件描述符 file descriptor
if ((fd = open("/etc/hosts", O_RDONLY)) < 0)
{
perror("open");
exit(1);
}
返回值是一个小的整型称为文件描述符(file descriptor),若该值等于 -1 则说明发生错误。每个由 Linux shell创建的进程都会默认打开三个文件(注意这里的文件概念):
- 0: standard input(stdin)
- 1: standard output(stdout)
- 2: standar error(stderr)
2.2 关闭文件
关闭文件会通知内核已完成对该文件的访问
int fd; // 文件描述符
int retval; // 返回值
int ((retval = close(fd)) < 0)
{
perror("close");
exit(1);
}
关闭一个已经关闭的文件是线程程序中的灾难(稍后会详细介绍),所以一定要检查返回值,哪怕是看似良好的函数如 close()
2.3 读取文件
读取文件将字节从当前文件位置复制到内存,然后更新文件位置
char buf[512];
int fd;
int nbytes
// 打开文件描述符,并从中读取 512 字节的数据
if ((nbytes = read(fd, buf, sizeof(buf))) < 0)
{
perror("read");
exit(1);
}
返回值是读取的字节数量,是一个 ssize_t 类型(其实就是一个有符号整型),如果 nbytes < 0 那么表示出错。nbytes < sizeof(buf) 这种情况(short counts) 是可能发生的,而且并不是错误。
2.4 写入文件
写入文件将字节从内存复制到当前文件位置,然后更新当前文件位置
char buf[512];
int fd;
int nbytes;
// 打开文件描述符,并向其写入 512 字节的数据
if ((nbytes = write(fd, buf, sizeof(buf)) < 0)
{
perror("write");
exit(1);
}
返回值是写入的字节数量,如果 nbytes < 0 表示出错。nbytes < sizeof(buf) 这种情况(short counts) 是可能发生的,且不是错误。
2.5 读取目录
可用readdir系列函数读取目录的内容,每次对readdir的调用返回的都是指向流dirp中下一个目录项的指针,或没有更多目录项则返回NULL,每个目录项都有结构体:
struct dirent{
ino_t d_ino; /* inode number */
char d_name[256]; /* filename */
};
2.6 简单Unix I/O 例子
拷贝文件到标准输出,一次一个字节:
#include "csapp.h"
int main(int argc, char *argv[])
{
char c;
int infd = STDIN_FILENO;
if (argc == 2) {
infd = Open(argv[1], O_RDONLY, 0);
}
while(Read(infd, &c, 1) != 0)
Write(STDOUT_FILENO, &c, 1);
exit(0);
}
前面提到的 short count 会在下面的情形下发生:
- 读取的时遇到 EOF(end-of-file)
- 从终端中读取文本行
- 读和写网络 sockets
但下面的情况下不会发生
- 从磁盘文件中读取(除 EOF 外)
- 写入到磁盘文件中 dribbble.com/sfhjk/shots dribbble.com/DFR552/shot… dribbble.com/govcn/shots dribbble.com/govcom/shot… dribbble.com/govcc/shots dribbble.com/govii/shots dribbble.com/govcm/shots dribbble.com/govct/shots govcn.jimdofree.com govsh.jimdofree.com hzgov.jimdofree.com dribbble.com/govpp/shots dribbble.com/govss/shots dribbble.com/govqq/shots dribbble.com/govgz/shots dribbble.com/govxm/shots dribbble.com/govhz/shots dribbble.com/govcd/shots dribbble.com/govcq/shots dribbble.com/govtj/shots dribbble.com/hzgov/shots dribbble.com/govnj/shots dribbble.com/govjn/shots dribbble.com/govqd/shots dribbble.com/govdl/shots dribbble.com/govnb/shots dribbble.com/xmgov/shots dribbble.com/cdgov/shots dribbble.com/govwh/shots dribbble.com/govheb/shot… dribbble.com/govsy/shots dribbble.com/govxa/shots dribbble.com/ccgov/shots dribbble.com/govca/shots dribbble.com/govfz/shots dribbble.com/govzz/shots dribbble.com/govsjz/shot… dribbble.com/govsz/shots dribbble.com/govfs/shots dribbble.com/govdg/shots dribbble.com/govwx/shots dribbble.com/govyt/shots dribbble.com/govty/shots dribbble.com/govhf/shots dribbble.com/govnc/shots dribbble.com/govnn/shots dribbble.com/govkm/shots dribbble.com/govwz/shots dribbble.com/govzb/shots dribbble.com/govts/shots dribbble.com/govgy/shots dribbble.com/xagov/shots dribbble.com/govhk/shots dribbble.com/govlz/shots dribbble.com/govyc/shots dribbble.com/govxn/shots dribbble.com/govqz/shots dribbble.com/govbt/shots dribbble.com/govnt/shots dribbble.com/govdq/shots dribbble.com/govxz/shots dribbble.com/govwf/shots dribbble.com/govcz/shots dribbble.com/govzx/shots dribbble.com/jngov/shots dribbble.com/ycgov/shots dribbble.com/govhd/shots dribbble.com/govly/shots dribbble.com/lygov/shots dribbble.com/govdy/shots dribbble.com/govyz/shots dribbble.com/govtz/shots dribbble.com/govjx/shots dribbble.com/czgov/shots dribbble.com/govyl/shots dribbble.com/tzgov/shots dribbble.com/govzj/shots dribbble.com/govks/shots dribbble.com/sovjy/shots dribbble.com/govzjg/shot… dribbble.com/govyw/shots dribbble.com/govjh/shots dribbble.com/govbd/shots dribbble.com/govjl/shots dribbble.com/govas/shots dribbble.com/govta/shots dribbble.com/gocyc/shots dribbble.com/govxy/shots dribbble.com/govzs/shots dribbble.com/govhui/shot… dribbble.com/govny/shots dribbble.com/whgov/shots dribbble.com/govdz/shots dribbble.com/govyy/shots dribbble.com/govlc/shots dribbble.com/govde/shots dribbble.com/zzgov/shots dribbble.com/govbz/shots dribbble.com/govmm/shots dribbble.com/govha/shots dribbble.com/govjm/shots dribbble.com/govwu/shots dribbble.com/zjgov/shots dribbble.com/govlf/shots dribbble.com/govhe/shots dribbble.com/lzgov/shots dribbble.com/govbj/shots dribbble.com/govzh/shots dribbble.com/govmy/shots dribbble.com/govzhu/shot… dribbble.com/govlyg/shot… canyin.16835.com/ zhusu.16835.com/ zixunfei.16835.com/ peixun.16835.com/ yunshu.16835.com/ guanggao.qiaxun.cn/ yunshu.qiaxun.cn/ zixunfei.qiaxun.cn/ peixun.qiaxun.cn/ jiayou.qiaxun.cn/ peijian.qiaxun.cn/ jiancai.qiaxun.cn/ zulinfei.qiaxun.cn/ wuyefei.qiaxun.cn/ laowufei.qiaxun.cn/ huiyifei.qiaxun.cn/ huiwufei.qiaxun.cn/ canyin.qiaxun.cn/ zhusufei.qiaxun.cn/ www.aizhan.com/cha/canyin.… www.aizhan.com/cha/zhusu.1… www.aizhan.com/cha/zixunfe… www.aizhan.com/cha/peixun.… www.aizhan.com/cha/yunshu.… www.aizhan.com/cha/guangga… www.aizhan.com/cha/yunshu.… www.aizhan.com/cha/zixunfe… www.aizhan.com/cha/peixun.… www.aizhan.com/cha/jiayou.… www.aizhan.com/cha/peijian… www.aizhan.com/cha/jiancai… www.aizhan.com/cha/zulinfe… www.aizhan.com/cha/wuyefei… www.aizhan.com/cha/laowufe… www.aizhan.com/cha/huiyife… www.aizhan.com/cha/huiwufe… www.aizhan.com/cha/canyin.… www.aizhan.com/cha/zhusufe… 最好总是允许 short count,这样就可以避免处理这么多不同的情况。
#include "csapp.h"
#define BUFSIZE 64
int main(int argc, char *argv[])
{
char buf[BUFSIZE];
int infd = STDIN_FILENO;
if (argc == 2) {
infd = Open(argv[1], O_RDONLY, 0);
}
while((nread = Read(infd, buf, BUFSIZE)) != 0)
Write(STDOUT_FILENO, buf, nread);
exit(0);
}