1 引言
不带缓冲区的I/O(unbuffered I/o)是指每个read 和 write 都调用内核中的一个系统调用,这些不带缓冲区的I/O 不是ISO C 的组成部分。
2 文件描述符
每个打开的文件都通过文件描述符引用,文件描述符是一个非负整数。open 和 create 函数返回的文件描述符供 read write 和 close 使用。
unix中文件描述符0与标准输入相关联,文件描述符1与标准输出相关联,文件描述符2与标准出错输出相关联。
3 不带缓冲区的I/O函数
下面介绍6个常用的I/O函数 open,creat,read,write,lseek,close.
3.1 open函数
#include <fcntl.h> int open(const char *pathname,int oflag,.../* mode_t mode*/) 成功返回文件描述符,出错返回-1 open函数返回的文件描述符一定是最小的未用的描述符数值。
对于open而言,只有创建新文件的时候才会用到第三个参数 mode
pathname是打开或创建文件的名字,oflag说明此函数的选项,由以下常量进行按位或构成oflag(这些常量定义在<fcntl.h>中)
必选:且只能选一个
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 读 写打开
可选:
O_APPEND 每次写操作都写在文件的尾端
O_CREAT 若此文件不存在则创建它,使用此选项时,必须指定第三个参数 mode 用于指定该文件的访问位权限。
O_EXCL 如果同时指定了O_CREAT,若此文件存在则出错返回 -1 用于测试一个文件是否存在,测试和创建新文件是一个原子操作
O_TRUNC 如果此文件存在,而且为只写或者读写成功打开,则此文件截断为0.
O_NOCTTY 如果pathname是终端设备,则不将此设备分配作为进程的控制终端
O_NONBLOCK 如果pathname是一个FIFO,一个块特使文件,一个字符特殊文件,则此选项为本次打开操作及后续的I/O操作设置为非阻塞模式
下面三个标志也是可选的:
O_DSYNC 每次写等待物理I/O完成,如果写操作并不影响读取刚刚写入的数据,则不等待文件的属性更新。
O_RSYNC 是每一个以文件描述符作为参数的read操作等待,直至任何对文件同一部分进行的未决写操作完成。
O_SYNC 使每次write都等待物理I/O操作完成,
O_DSYNC 在重写现有部分内容时,文件时间属性不会同步更新,而O_SYNC每次write操作都会在返回前等新文件时间。
3.2 creat函数
#include <fcntl.h> int creat(const char *pathname,mode_t mode) 若成功返回以只写方式打开的文件描述符,失败返回 -1
现在可以用以下方式调用open来代替creat
open(pathname,O_WRONLY | O_CREAT | O_TRUNC,mode);
3.3 close函数
#include <fcntl.h> int close(int filedes); 若成功返回0,出错返回 -1 ;
当一个进程终止时,会自动关闭所有打开的文件描述符,所以很多程序利用这一功能不显示调用close关闭文件描述符;
3.4 lseek函数
#include <unistd.h> off_t lseek(int filedes,off_t offset,int where); 成功返回新的文件偏移量,出错返回 -1 ;
offset 的含义与where的选择有关:
SEEK_SET 将文件偏移量设定为距文件开始处offset个字节;
SEEK_CUR 将文件偏移量设定为当前值加上offset (offset可正可负);
SEEK_END 将文件偏移量设定为文件长度加上offset(offset可正可负);
调用lseek(fd,0,SEEK_CUR)可以确定打开文件的当前偏移值。也可以用此来测试文件是否可以设置偏移量。若fd为管道,FIFO,和网络套接字 则返回 -1,并将errno设为ESPIPE;
#include <stdio.h> #include <unistd.h> int main() { if(lseek(STDIN_FILENO,0,SEEK_CUR) == -1) printf("connot seek\n"); else printf("seek OK\n"); return 0; }
编译该程序,运行。
[email protected]:/program# ./3-1 < /etc/motd seek OK [email protected]:/program# cat < /etc/motd | ./3-1 connot seek
3.4.1 具有空洞的文件
当文件偏移量大于当前文件长度时候,就在文件中形成一个空洞,位于文件中但没有写过的字节都被读为0。
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #define FILE_MODE S_IRUSR | S_IWUSR | S_IXUSR int main() { char buf1[] = "abcde"; char buf2[] = "ABCDE"; int fd; if((fd = creat("foo",FILE_MODE)) < 0) { printf("creat file error\n"); return -1; } if(write(fd,buf1,5) != 5) { printf("write error\n"); return -1; } if(lseek(fd,1024,SEEK_SET) == -1) { printf("seek error\n"); return -1; } if(write(fd,buf2,5) != 5) { printf("write error\n"); return -1; } return 0; }
查看文件大小[email protected]:/program# ls -l foo -rwx------ 1 root root 1029 Nov 2 20:48 foo [email protected]:/program#
查看文件内容 [email protected]:/program# od -c foo 0000000 a b c d e \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 0000020 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 * 0002000 A B C D E 0002005 [email protected]:/program#
可以看出文件中未写的字节全部填充为0.
3.5 read函数
#include <unistd.h> ssize_t read(int filedes,void *buf,ssize_t nbytes); 若成功返回读取到的字节数,若已到达文件末尾返回0,出错返回-1;
读操作从当前的文件偏移量开始,在read返回前,文件偏移量将增加实际读取的字节数。实际读取的字节数可能比nbytes小。
3.6 write函数
#include <unistd.h> ssize_t write(int filedes,const void *buf,size_t nbytes); 若成功返回写入的字节数,一般返回值等于nbytes,出错返回 -1;
对于普通文件,write操作从文件的当前偏移量处开始,如果打开文件时制定了O_APPEND,则将文件偏移量设置为文件的结尾处,成功写之后,文件偏移量将增加写的字节数。
/*将标准输入复制到标准输出*/#include <stdio.h> #include <unistd.h> #define BUFFSIZE 1024 int main() { int n; char buf[BUFFSIZE]; while((n = read(STDIN_FILENO,buf,BUFFSIZE)) > 0) if(write(STDOUT_FILENO,buf,n) != n) { printf("write error\n"); return -1; } if(n < 0) printf("read error\n"); return 0; }
[email protected]:/program# ./3-3 hello world! hello world!