文件I/O可分为两类:第一类是非缓冲式的文件操作;该类型一般由系统调用,主要函数有open,write,read,lseek,close;第二类是缓冲式文件操作;该类型由标准输入输出提供操作;本文记录的是第一类文件I/O。
文件描述符
内核通过文件描述符打开的文件,它是一个非负整数。通常,文件描述符0对应标准输入;文件描述符1对应标准输出;文件描述符2对应标准出错。依照POSIX,这些整数应替换为符号常量 STDIN_FILENO 、 STDOUT_FILENO 、 STDERR_FILENO ,定义在 <unistd.h> 中。
/* * this file is the operation about file descriptor. * by chenhanzhun 2014.10.21 */ #include <stdio.h> #include <stdlib.h> int main(void) { printf("print the fileno:\n"); printf("fileno of stdin =%d\n",fileno(stdin)); printf("fileno of stdout =%d\n",fileno(stdout)); printf("fileno of stderr =%d\n",fileno(stderr)); return 0; }
/* *函数功能:打开或创建一个文件; *返回值:若打开成功返回文件描述符,若出错则返回-1; */ /* *函数原型: */ #include <fcntl.h> int open(const char* pathname, int oflag); int open(const char* pathname, int oflags, mode_t mode); /* *参数解释: *pathname是打开或创建的文件名称; *oflag只能是下面三个中的一个或与其它选项组成: *只能指定一个:O_RDONLY(只读),O_WRONLY(只写),O_RDWR(可读写) *其它选项: *O_APPEND :写操作追加到文件尾 *O_CREAT :若打开的文件不存在,则创建,需要使用第三个参数mode *O_EXCL :测试文件是否存在,不存在则创建此文件 *O_TRUNC :若文件存在,且只写或读写打开,则将文件长度截取为0 *O_NOCTTY :若文件名称pathname是终端设备,则该设备不能作为进程控制的终端 *O_NONBLOCK :如果pathname值的是一个FIFO,一个块特殊文件或一个字符特殊文件,则此选项为文件的本次打开操作和后续的I/O操作设置非阻塞模式。 *mode是提供新建文件的权限,选项有O_CREAT时必须需要mode参数 */creat函数
creat函数 /* *函数功能:创建一个新文件; *返回值:若创建成功则返回只写文件描述符,若出错则返回-1; */ /* *函数原型: */ #include <fcntl.h> int creat(const char* pathname, mode_t mode); /*等效于:open(pathname,O_WRONLY | O_CREAT | O_TRUNC, mode);*/ /* *参数解释: *pathname是打开或创建的文件名称; *mode是提供新建文件的权限,选项有O_CREAT时必须需要mode参数 */close函数
close函数 /* *函数功能:关闭一个已经打开的文件;同时会释放所有进程加载该文件的记录锁; *返回值:若关闭成功返回0,若出错则返回-1; */ /* *函数原型: */ #include <unistd.h> int close(int filedes); /* *参数解释: *filedes已打开的文件描述符; */lseek函数
/* *函数功能:当前文件偏移量; *返回值:若成功返回新的文件偏移量,若出错则返回-1; */ /* *函数原型: */ #include <unistd.h> off_t lseek(int filedes, off_t offset, int whence); /* *参数解释: *filedes是文件描述符; *offset表示偏移字节数; *whence参数有一下三种: *SEEK_SET :表示偏移量设置为文件开始处的offset个字节; *SEEK_CUR :表示偏移量设置为当前值加offset个字节; *SEEK_END :表示偏移量设置为文件长度加offset个字节; */read函数
/* *函数功能:读取已打开文件的数据; *返回值:若成功返回读取的字节数,若到文件尾则返回0,若出错则返回-1; */ /* *函数原型: */ #include <unistd.h> ssize_t read(int filedes, void *buf, size_t nbyte); /* *参数解释: *filedes是指所要读取文件的文件描述符; *buf存储所读取数据; *nbyte表示最多读取的字节数; */write函数
/* *函数功能:向已打开文件写入数据; *返回值:若成功返回已写的字节数,若出错则返回-1; */ /* *函数原型: */ #include <unistd.h> ssize_t write(int filedes, const void *buf, size_t nbyte); /* *参数解释: *filedes是指所要写入数据文件的文件描述符; *buf存储所存储的数据; *nbyte表示最多写入的字节数; */
程序测试:
#include "apue.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define buf_size 4096 #define offset 1024 int main(void) { const char* src_path = "./10_22.txt"; const char* des_path ="./test.txt"; int fd,fs; ssize_t len; off_t num; char buf[buf_size]; fd = open(src_path,O_RDONLY); if(fd == -1) printf("open the file error.\n"); fs = open(des_path,O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if(fs == -1) printf("open or creat the file error.\n"); num = lseek(fd,offset,SEEK_CUR); printf("the offset of lseek is %d.\n",num); while((len = read(fd,buf,sizeof(buf))) > 0) write(fs,buf,len); close(fd); close(fs); return 0; }
UNIX支持不同进程共享打开的文件。内核使用三种数据结构表示打开的文件:
Linux将v节点和i节点实现为独立于文件系统的i节点和依赖文件系统的i节点。
不同进程共享文件时,每个进程都有一个该文件的文件表项,指向同一个v节点表。多个文件描述符也可能指向同一个文件表项,如使用 dup 函数和 fork 后的父子进程。以下是不同进程共享同一个文件的示意图:
#include <unistd.h> /* 函数功能:定位并读取数据 * 返回值:成功返回读到的字节数,已到文件尾则返回0,出错返回-1 ; */ ssize_t pread(int fd, void *buf, size_t count, off_t offset); /* 函数功能:定位并写入数据 * 返回值:成功返回写入的字节数,出错返回-1 ; */ ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
dup函数
/* *函数功能:复制已有的文件描述符; *返回值:若成功返回当前可用的文件描述符最小值,若出错则返回-1; */ /* *函数原型: */ #include <unistd.h> int dup(int filedes); /* *参数解释: *filedes是指当前文件的文件描述符; */dup2函数
/* *函数功能:复制已有的文件描述符; *返回值:若成功返回参数filedes2指定的文件描述符,如果filees2已经打开,则现将其*关闭,如果filedes1等于filedes2,则返回filedes2,不关闭;若出错则返回-1; */ /* *函数原型: */ #include <unistd.h> int dup2(int filedes1,int filedes2); /* *参数解释: *filedes是指当前文件的文件描述符; */
刷新缓冲区函数
unix系统在写数据时是采用文件延迟写,但是我们可以自己刷新缓冲区,将数据写入磁盘。以下是刷新缓冲区,将数据写入磁盘的函数sync,fsync,fdatasync;
#include <unistd.h> /* 将所有修改过的块缓冲区排入写队列,然后返回,不等待写磁盘结束 */ void sync(void); /* 对指定文件刷新块缓冲区,等待写磁盘结束,更新文件属性 * 返回值:成功返回0,出错返回-1 */ int fsync(int fd); /* 对指定文件刷新块缓冲区,等待写磁盘结束,不更新文件属性 * 返回值 成功返回0,出错返回-1 */ int fdatasync(int fd);
fcntl函数
/* *函数功能:可以改变已打开文件的性质。 *函数原型: */ #include <unistd.h> #include <fcntl.h> /* * 返回值:成功依赖于cmd,出错返回-1 */ int fcntl(int filedes, int cmd, ... /*int arg */ ); /* *参数解释: *filedes表示文件描述符; *cmd参数,根据 cmd 值的不同,有以下5种功能: * *(1)F_DUPFD ,复制一个现有的描述符。新描述符为大于等于 arg 的最小可用值。 *(2)F_GETFD 、 F_SETFD ,获取/设置文件描述符标记。文件描述符标志只有 FD_CLOEXEC ,但一般使用0或1,分别代表 exec 时不关闭或关闭,0为默认值。 *(3)F_GETFL 、 F_SETFL ,获取/设置文件状态标志。可获取的文件状态标志见 open 函数的 flags 可取值,可设置的文件状态标志不包括访问模式位和创建模式位。 *(4)F_GETOWN 、 F_SETOWN ,获取/设置异步I/O所有权,即接收 SIGIO 和 SIGURG *信号的进程ID或进程组ID, arg 为正为进程ID, arg 为负为等于其绝对值的进程组ID。 *(5)F_GETLK 、 F_SETLK 、 F_SETLKW ,获取/设置记录锁。 *agr是指定的整数值; */
《unix高级环境编程》