在APUE第三节——File I/O 中,主要描述的是一系列的 I/O 函数,通过一些函数来进行文件的一些操作。
这些操作基本上用下面这几个系统函数:open, read, write, lseek, close。在这一章中所有的系统函数
都属于“非缓冲文件”(unbuffered I/O)。开头第2段文字有这样一句话来描述 "unbuffered" : The term
unbuffered means that each read and write invokes a system call in the kernel. 也就
是说这些函数都是直接调用内核中的系统函数,它完全没有经过“缓冲区”,直接调用了。所以,这样不经过
“缓冲区”速度就肯定比经过“缓冲区”快了。这篇学习笔记将对“缓冲文件系统”和“非缓冲文件系统”作一个
比较,当然,主要还是放在“非缓冲文件系统”方面来描述。
一、缓冲文件系统简要描述:
第一段简单说到了, “缓冲文件系统”的文件操作要经过“缓冲区”,那么具体如何操作呢?比如说最常用的
read 和 write。首先操作系统会在内存区域开辟一个缓冲区,顾名思义缓冲区就是用于缓冲的。如果要执行
read操作,那么先从磁盘那里把数据内容读入了缓冲区,然后等到把缓冲区装满之后,CPU再从缓冲区把数据
读入特定的变量。如果是写操作,刚好是反过来了,CPU先把数据一个个写进缓冲区里面,等到写满之后就把
缓冲区刚刚写进的数据写入到文件里面了。
ANSI C 中函数库支持“缓冲区文件系统”, 所以文件操作函数像 fopen, fclose, fread, fwrite等都是
属于缓冲I/O。这里不多说这些函数了。
二、非缓冲文件系统描述:
在ANSI C 中函数库支持“缓冲区文件系统”,但不支持“非缓冲文件系统”,“非缓冲文件系统”是POSIX.1
和 UNIX specification 的一部分。
前面说到了,非缓冲I/O是系统直接的输入和输出,它不经过“缓冲区”,所以从速度和效率方面来说就显得
快一些了。下面对几个基本的unbuffered I/O 函数的功能通过配合代码描述一下。
1. int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
|
man手册上提供的函数原型。pathname 是路径名,是文件打开或者新建的文件名字。参数 flags 是选择
的描述符。man手册上有句话这样描述flags:The parameter flags must include one of the
following accessmodes:O_RDONLY, O_WRONLY, or O_RDWR. These request opening the file
read-only, write-only, or read/write, respectively.还有通过“位与”的模式来描述flags的,man手册上
都写得很具体了。比较上面两个open原型,发现第2个函数原型多了一个参数mode_t mode.有什么区别?
The argument mode specifies the permissions to use in case a new file is created. 所有只有当
是新建文件的时候,第3个参数才有它的作用,在手册上也一几个你列举了这个参数的一些值了。下面这样调用
open 就是等价于新建了一个文件:
open(pathname, O_WRONLY | O_CTREAT | O_TRUNC, mode);
|
2. ssize_t read(int fd, void *buf, size_t count);
ssize_t : functions that return a count of bytes(signed).
|
注意到第1个参数fd, 这是文件的描述符。对于内核来说,所有的文件都用文件描述符来描述。read函数作用:
从文件描述符 fd 中读 count 个字节 进入 buf区。函数执行成功返回读入的字节数,失败的话返回 -1, 并
对 errno 赋予相应的值。
3.ssize_t write(int fd, const void *buf, size_t count);
|
和 read 函数相反,从 buf 里写count个字节进入文件描述符fd里面。
4.off_t lseek(int fildes, off_t offset, int whence);
|
off_t : file sizes and offsets(signed);
lseek是随机访问文件函数。这个函数非常重要。因为每次对文件进行读写操作的位置总是在前一次操作之后。
这样有时候要访问文件的其他位置就麻烦了,就得依靠这个函数了。
fildes:文件描述符;
offset:访问的文件的位置
whence:值可以是0,1,2;分别表示从文件开始、从文件当前位置、从文件结束位置;
返回值:成功就返回新的文件位置,失败返回-1。
close 很简单,关闭一个文件描述符,这样就这个文件内容就不会被无意修改和使用了。
返回0表示执行成功,返回-1表示执行失败。
6. int creat(const char *pathname, mode_t mode);
|
用于创建一个新的文件。参数就不说了,参照open函数。 creat函数返回一个新文件描述符。
三、实例简单分析:
实例1:下面的程序是来自Figure 3.2,利用几个基本函数构造而成,非常简单,简单的分析都在注释里面
了;
#include "apue.h" #include <fcntl.h>
char buf1[] = "abcdefghij"; char buf2[] = "ABCDEFGHIJ";
int main(void) { int fd;
/*offset is 0 when the file is created.*/ if ((fd = creat("file.hole", FILE_MODE)) < 0) err_sys("creat error!");
/*offset is 10 after calling the write function. * write 10 bytes form buf1 to fd*/ if (write(fd, buf1, 10) != 10) err_sys("buf1 write error!"); /*offset is 16384 after calling the lseek function*/ if (lseek(fd, 16384, SEEK_SET) == -1) err_sys("lseek error!");
/*offset is 16394 because we write 10 bytes form buf2*/ if (write(fd, buf2, 10) != 10) err_sys("write error!");
exit (0); }
|
实例2 :read & write 的配合
#include "apue.h"
int main(void) { int n; char buf[4096];
/*read from the stdin file and write to stdout*/ while ((n = read(STDIN_FILENO, buf, 4096)) > 0) if (write(STDOUT_FILENO, buf, n) != n) err_sys("write error");
if (n < 0) err_sys("read error");
exit (0); }
|