Linux中无缓冲文件I/O API
1.什么是无缓冲I/O
我们首先要知道Linux操作系统提供给我们的用户程序调用的接口,除了使用库函数,还可以直接使用系统调用。而今天我要介绍的相关函数就是直接调用了内核的系统调用,所以说它是无缓冲的,它跟标准的I/O函数相对应。
2.打开或者创建文件的函数:open()
要操作相关文件,就必须获得该文件的句柄,专业点叫文件描述符。它是进程在打开一个文件或者创建一个文件时,内核返回给该进程的一个唯一的非负的整数,且一定是当前可以的描述符中最小的一个数。进程获得这个文件描述符后,就可以根据它来告诉read()或者write相关函数具体来操作哪个文件了。
Tips:通常系统对一个进程可打开的文件描述符数量有一个默认值,可以使用命令ulimit -f查看,也可以使用ulimit命令重新设置该值。
通常,系统有3个已经默认的文件描述符,即0代表标准输入,1表示标准输出,2表示标准错误,在unistd.h头文件中,把他们定义为常量,分别是STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO。
open()函数为我们提供了该文件描述符:
#include<fcntl.h>
int open( const char * pathname,int flags, mode_t mode);
pathname:即是我们要打开或者创建文件的名字,可以是相对路径,也可以是绝对路径。
flags:重点介绍一下,它的取值比较多,在<fcntl.h>头文件中定义了很多常量:
O_RDONLY:即以只读的方式打开该文件。
O_WRONLY:即以只写的方式打开该文件。
O_RDWR:即以读、写的方式打开该文件。
以上这三个常量,flags必选选择其一,且只能选择其一。还有几个可选常量可以跟这三个之一组合使用,以达到不同的效果:
O_CREAT:若欲打开的文件不存在则自动建立该文件.
O_TRUNC :若文件存在并且以可写的方式打开时,此旗标会令文件长度清为0,而原来存于该文件的资料也会消失。
O_APPEND :当读写文件时会从文件尾开始移动,也就是所写入的数据会以附加的方式加入到文件后面。
O_EXCL 如果O_CREAT 也被设置,此指令会去检查文件是否存在。文件若不存在则建立该文件,否则将导致打开文件错误。
比如,我们可以使用open("./filename" ,O_WRONLY|O_CREAT,mode),以写方式打开filename,如果filenam不存在则创建它。
再如,open("./filename" ,O_WRONLY|TRUNC),就会将原来filename中的内容清除掉,从新写入新的值。
还如,open("./filename" ,O_WRONLY|O_CREAT|O_EXCL),可以测试filename是否存在,存在则报错,不存在则创建filename.
当然flags还有几个其他的常量取值,就不一一介绍了,可以自己去查手册。
mode:仅当创建一个新文件需要指定该参数,该参数的意思是说文件以一个什么样的权限创建。
open()调用成功后将返回文件描述符,出错则返回-1。
系统还提供一个专门用来创建文件的函数create():
#include<fcntl.h>
int creat(const char * pathname, mode_tmode);
其相当于open("pathname",O_WRONLY|O_CREAT|O_TRUNC,mode)。该函数有个不好之处是,只能以写的方式打开文件。有了open(),我们可以这样定义:
open("pathname",O_RDWR|O_CREAT|O_TRUNC,mode)
3.读函数read(),写函数write()
#include<unistd.h>
ssize_t read(int fd,void * buf ,size_t count);
#include <unistd.h>
ssize_t write (int fd,const void * buf,size_t count);
write函数向fd中当前偏移量后面写入count大小字节的buf内容。如果成功返回写入的字节数大小,如果失败则返回-1.
4.设置文件偏移量的函数lseek()
每个打开的文件都有一个“当前文件偏移量”,在read(),write()的时候就可以根据该位置开始读写。使用lseek()则改变该位置。
#include<unistd.h>
off_t lseek(int fildes,off_t offset ,int whence);
offset即指要相对whence便宜多少字节。whence不同的值使offset的意思不一样:
SEEK_CUR:将偏移量设置为当前偏移量加offset个字节。
SEEK_SET:将偏移量设置为文件起始处偏移offset个字节。
SEEK_END:将偏移量设置为文件末尾加offset个字节。
有个小技巧,如果要获得该文件当前文件偏移量,怎么办呢?可以这样:currPos = lseek(fd,0,SEEK_CUR);
注意:当前文件偏移量不一定是非负的值,也可能是负值。所以判断lseek是否失败一定要测试其结果是否为-1,而不是小于0。
如果设置偏移量大于文件长度时,文件中将形成“空洞”,这种“空洞”并不占有磁盘的空间,它读取出来表现是0。
5.一个copy程序
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define BUFFER 4096
int main(int argc,char *argv[])
{
int n;
char buf[BUFFER];
int fd1 = open(argv[1],O_RDONLY);
int fd2 = open(argv[2],O_WRONLY);
while( (n=read(fd1,buf,BUFFER))>0 )
{
if( (write(fd2,buf,n))!=n )
{
printf("error\n");
exit(1);
}
}
close(fd1);
close(fd2);
exit(0);
}