linux不带缓冲的文件操作基本函数及用法示例

不带缓冲是指直接使用系统调用,在内核与调用者之间没有缓冲,但是在内核与设备之间还是有缓冲高速缓存或页面高速缓存存在的。这与使用标准IO函数不同,在使用标准IO函数时,在调用者与内核之间还存在缓冲。


内核为打开的文件分配一个文件标识符来表示这个打开的文件。用户后续对这个文件的操作不用再通过文件路径名,可以直接使用文件标识符。文件标识符是一个非负整数,而且在打开时总是分配一个最小可用的整数值,关闭时将文件描述符回收。每个进程维护一张文件描述符表,文件描述符表的每一个表项包括打开的文件描述符,对应的文件描述符标志以及指向的文件表指针。这个文件表是由内核维护的,内容包括文件状态标志,当前文件偏移,以及inode指针。inode则存储在硬盘上。同一个进程多次打开或不同的进程同时打开同一个文件,内核会为每一次打开分配一个独立的文件表,所以每次打开都有自己独立的当前文件偏移值。同一个进程内,可以有多个不同的文件描述符指向同一个文件表,也就是说,这些不同的文件描述符有共同的文件状态标志和当前文件偏移值,但是可以有不同的文件描述符标志。我们可以通过dup、dup2、fcntl函数进行文件描述符复制,使他们指向相同的文件表。


最常用的5个文件操作函数原型:

extern int open (__const char *__file, int __oflag, ...) __nonnull ((1));

extern ssize_t write (int __fd, __const void *__buf, size_t __n) __wur;

extern ssize_t read (int __fd, void *__buf, size_t __nbytes) __wur;

extern __off_t lseek (int __fd, __off_t __offset, int __whence) __THROW;

extern int close (int __fd);


open函数说明:

1)O_RDONLY,O_WRONLY,O_RDWR三个flag必须且只能指定一个,规定了对文件的操作权限。比如:如果以O_RDONLY的方式打开文件,则后面无法调用write函数对文件写入内容。其他的flag可以有,也可以没有。

2)如果文件不存在,调用open函数时flag没有设置O_CREATE,open返回出错

3)如果设置了O_CREATE,文件不存在,则会创建一个新的文件,这时可以通过mode设置文件的访问权限,否则根据umask值设置文件权限;如果文件存在,则只会打开文件。

4)如果成功打开或创建文件,当前文件访问偏移设置为0,从文件开始处开始读写文件

5)如果同时指定O_CREATE和O_EXCL,当文件存在时打开失败;当文件不存在时,创建文件。

6)O_EXCL必须与O_CREATE结合使用。如果单独使用O_EXCL,结果不确定。

7)O_APPEND标志在write文件之前总会把文件访问偏移值设为文件末尾,lseek设置不会影响write写的位置。而且设置文件偏移与write是原子操作。

8)O_TRUNC标志总会在打开时将文件内容阶段为0,如果文件不存在,则返回出错。


使用建议:

1)如果确定不创建文件,或确定文件已经存在,则不设置O_CREATE标志。这时如果文件存在,则正常打开;如果文件不存在,则返回出错

2)如果希望文件存在时打开文件,文件不存在时创建文件,则设置O_CREATE标志。这时需要注意文件访问偏移值初始值为文件开头,直接写可能覆盖原文件内容。

3)如果只希望创建一个新的文件,则同时设置O_CREATE和O_EXCL标志。如果文件已经存在,则返回出错。


lseek函数说明:

1)文件都有与当前读写位置相关联的当前文件偏移量,对于普通文件,当前文件偏移量为一个非负数。但是在特殊情况下,也可能是负数。

2)read、write都从当前文件偏移处开始读写。读写玩后当前文件偏移量前移读写的数据长度。

3)可以用lseek(fd, 0, SEEK_CUR)获取当前偏移量,也可以用这个函数测试fd是否是管道、FIFP、套接字等特殊文件,这时lseek返回-1

4)不要用返回值小于0的方式判断lseek是否正确,而要用返回值等于-1来判断出错


open函数的oflag可取一下值:

/* open/fcntl - O_SYNC is only implemented on blocks devices and on files
   located on a few file systems.  */
#define O_ACCMODE       0003
#define O_RDONLY         00
#define O_WRONLY         01
#define O_RDWR             02
#define O_CREAT           0100    /* not fcntl */
#define O_EXCL           0200    /* not fcntl */
#define O_NOCTTY       0400    /* not fcntl */
#define O_TRUNC          01000    /* not fcntl */
#define O_APPEND      02000
#define O_NONBLOCK      04000
#define O_NDELAY    O_NONBLOCK
#define O_SYNC           04010000
#define O_FSYNC         O_SYNC
#define O_ASYNC         020000

open函数的可变参数为mode_t,新建文件时需要指定,可取值:

#define    S_IRUSR    __S_IREAD    /* Read by owner.  */
#define    S_IWUSR    __S_IWRITE    /* Write by owner.  */
#define    S_IXUSR    __S_IEXEC    /* Execute by owner.  */


#define    S_IRGRP    (S_IRUSR >> 3)    /* Read by group.  */
#define    S_IWGRP    (S_IWUSR >> 3)    /* Write by group.  */
#define    S_IXGRP    (S_IXUSR >> 3)    /* Execute by group.  */


#define    S_IROTH    (S_IRGRP >> 3)    /* Read by others.  */
#define    S_IWOTH    (S_IWGRP >> 3)    /* Write by others.  */
#define    S_IXOTH    (S_IXGRP >> 3)    /* Execute by others.  */

其他的几个文件操作函数:

extern int creat(__const char *__file, __mode_t __mode) __nonnull ((1));

extern ssize_t pwrite (int __fd, __const void *__buf, size_t __n, __off_t __offset) __wur;

extern ssize_t pread (int __fd, void *__buf, size_t __nbytes,  __off_t __offset) __wur;

extern int fsync (int __fd);

extern int fdatasync (int __fildes);

extern int dup (int __fd) __THROW __wur;

extern int dup2 (int __fd, int __fd2) __THROW;

extern int fcntl (int __fd, int __cmd, ...);

fcntl函数的cmd可取值:

 /* Values for the second argument to `fcntl'.  */
#define F_DUPFD        0    /* Duplicate file descriptor.  */
#define F_GETFD        1    /* Get file descriptor flags.  */
#define F_SETFD        2    /* Set file descriptor flags.  */
#define F_GETFL        3    /* Get file status flags.  */
#define F_SETFL        4    /* Set file status flags.  */
#ifndef __USE_FILE_OFFSET64
# define F_GETLK    5    /* Get record locking info.  */
# define F_SETLK    6    /* Set record locking info (non-blocking).  */
# define F_SETLKW    7    /* Set record locking info (blocking).    */
#else
# define F_GETLK    F_GETLK64  /* Get record locking info.    */
# define F_SETLK    F_SETLK64  /* Set record locking info (non-blocking).*/
# define F_SETLKW    F_SETLKW64 /* Set record locking info (blocking).  */
#endif
#define F_GETLK64    12    /* Get record locking info.  */
#define F_SETLK64    13    /* Set record locking info (non-blocking).  */
#define F_SETLKW64    14    /* Set record locking info (blocking).    */

#if defined __USE_BSD || defined __USE_UNIX98 || defined __USE_XOPEN2K8
# define F_SETOWN    8    /* Get owner (process receiving SIGIO).  */
# define F_GETOWN    9    /* Set owner (process receiving SIGIO).  */
#endif


create函数说明:

1)如果文件不存在,创建文件;如果文件存在,打开文件,并清空文件内容。

2)等价于 open(pathname, O_WRONLY | O_CREATE | O_TRUNC, mode)


最常用的5个文件操作函数使用示例程序:
#include    "unp.h"
#include "apue.h"
#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
         //#include 
        
          //#include 
         
           int main(int argc, char *argv[]) { int fd; int fd2; char buf[100]; char rd_buf[100]; ssize_t wr_size; ssize_t rd_size; off_t curr_pos; //mode_t f_mode; #if 0 //用O_CREAT 和O_EXCL打开文件,如果文件已经存在,则回返回错误 //S_IRUSR/S_IWUSR用来设置文件所有者的访问权限,如果不设置,程序默认是没有读写权限的,后面的程序就无法读写文件的内容 if((fd = open("/home/yang/exam_file/exam_file_op.dat",O_RDWR | O_CREAT | O_EXCL | O_RSYNC | O_DSYNC | O_SYNC, S_IRUSR | S_IWUSR)) < 0) { err_sys("create file error"); } #endif #if 0 //O_TRUNC打开文件时文件必须存在,否则报错 if((fd = open("/home/yang/exam_file/exam_file_op.dat",O_RDWR | O_TRUNC)) < 0) { err_sys("open file error"); } #endif #if 1 //这种方式先检测文件是否存在,如果不存在,则创建一个新文件,如果存在,则回返回错误 if((fd = open("/home/yang/exam_file/exam_file_op.dat",O_RDWR | O_CREAT | O_EXCL | O_RSYNC | O_DSYNC | O_SYNC, S_IRUSR | S_IWUSR)) < 0) { if((fd = open("/home/yang/exam_file/exam_file_op.dat",O_RDWR | O_TRUNC)) < 0) { err_sys("open file error"); } } #endif printf("file descriptor: %d\n", fd); strcpy(buf, "hello file\n"); printf("buf string len: %d\n", strlen(buf)); if( (wr_size = write(fd, buf, strlen(buf))) < 0 ) { puts("write file error"); } printf("wr_size: %d\n", wr_size); //为了读文件内容,需要先关闭文件,然后重新打开文件,否则读不到刚刚写入的内容 if(close(fd) < 0) { puts("close file error"); } if((fd = open("/home/yang/exam_file/exam_file_op.dat",O_RDWR )) < 0) { err_sys("open file error"); } //printf("sizeof(rd_buf): %d\n", sizeof(rd_buf)); if( (rd_size = read(fd, rd_buf, sizeof(rd_buf))) < 0) { puts("read file error"); } else if(rd_size == 0) { puts("alread reach file end"); } printf("wr_size: %d\n", rd_size); printf("rd_buf string len: %d\n", strlen(rd_buf)); printf("rd_buf string : %s", rd_buf); close(fd); // //测试lseek和O_APPEND // if((fd = open("/home/yang/exam_file/exam_file_op.dat",O_RDWR )) < 0) { err_sys("open file error"); } //获取当前的文件偏移量 curr_pos = lseek(fd, 0, SEEK_CUR); //当前文件偏移量在文件的开始处 printf("fd: %d\n", fd); printf("open without O_APPEN, fd: %d\n", curr_pos); if((fd2 = open("/home/yang/exam_file/exam_file_op.dat",O_RDWR | O_APPEND )) < 0) { err_sys("open file error"); } //获取当前的文件偏移量 curr_pos = lseek(fd, 0, SEEK_CUR); //当前的文件偏移量在文件的末尾 printf("fd2: %d\n", fd2); printf("open with O_APPEN, curr_pos: %d\n", curr_pos); //将文件偏移量值设为末尾值+5 curr_pos = lseek(fd2, 5, SEEK_END); printf("open with O_APPEN, curr_pos: %d\n", curr_pos); //将文件偏移量值设为5 curr_pos = lseek(fd2, 5, SEEK_SET); printf("open with O_APPEN, curr_pos: %d\n", curr_pos); //虽然当前文件偏移量为5,但是往文件写数据时,还是写到了文件末尾 wr_size = write(fd2, buf, strlen(buf)); curr_pos = lseek(fd2, 0, SEEK_CUR); printf("after write, curr_pos: %d\n", curr_pos); close(fd2); //重新读文件时,打印结果显示内容确实写到了文件末尾 if( (rd_size = read(fd, rd_buf, sizeof(rd_buf))) < 0) { puts("read file error"); } else if(rd_size == 0) { puts("alread reach file end"); } printf("rd_size: %d\n", rd_size); printf("rd_buf string len: %d\n", strlen(rd_buf)); printf("rd_buf string : %s", rd_buf); close(fd); exit(0); } 
          
         
       
      
      
     
     
    
    
   
   

 

其它几个文件操作函数示例程序:

#include    "unp.h"
#include "apue.h"
#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
         //#include 
        
          //#include 
         
           #if 1 //不带缓冲的文件操作函数 int main(int argc, char *argv[]) { int fd; ssize_t rd_size; ssize_t wr_size; char wr_buf[100]; char rd_buf[100]; int fd2; int fd3; int fd_flag; int fl_flag; //这种方式先检测文件是否存在,如果不存在,则创建一个新文件,如果存在,则打开它 if((fd = open("/home/yang/exam_file/exam_file_op.dat",O_RDWR | O_CREAT | O_EXCL | O_RSYNC | O_DSYNC | O_SYNC, S_IRUSR | S_IWUSR)) < 0) { if((fd = open("/home/yang/exam_file/exam_file_op.dat",O_RDWR )) < 0) { err_sys("open file error"); } } //原子操作写文件,相当与lseek+write memcpy(wr_buf, "hello filexxxxxx\n", strlen("hello filexxxxxx\n")+1); if( (wr_size = pwrite(fd, wr_buf, strlen(wr_buf), 5)) < 0 ) { puts("pwrite error"); } /* fsync(fd); fdatasync(fd); */ //原子操作读文件,相当与lseek+read if((rd_size = pread(fd, rd_buf, 40, 0)) < 0) { puts("pread error"); } else if(rd_size == 0) { puts("alread reach file end"); } else { printf("rd_size: %d\n", rd_size); printf("read string: %s", rd_buf); } close(fd); // /dev/fd/1相当与标准输出,打开/dev/fd/1后,向fd写数据,相当于向标准输出输出数据 // /dev/fd/0: 标准输入;/dev/fd/1:标准输出;/dev/fd/2:标准出错 if((fd = open("/dev/fd/1",O_WRONLY )) < 0) { err_sys("open /dev/fd/1 error"); } memcpy(wr_buf, "hello filexxxxxx\n", strlen("hello filexxxxxx\n")+1); if( (wr_size = write(fd, wr_buf, strlen(wr_buf))) < 0 ) { puts("write /dev/fd/1 error"); } //复制文件描述符,内核为fd2分配一个新的描述符值,但fd2的文件指针指向的文件表是 //文件描述符fd的文件指针指向的文件表,所以向fd2写,就是向标准输出写 fd2 = dup(fd); printf("fd2: %d\n", fd2); memcpy(wr_buf, "hello fileyyy\n", strlen("hello fileyyy\n")+1); if( (wr_size = write(fd2, wr_buf, strlen(wr_buf))) < 0 ) { puts("write fd2 error"); } //内核定义了STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO三个常量,分别表示标准输入/标准输出/标准出错的文件标识符 //向文件描述符STDOUT_FILENO写,就是向标准输出写 memcpy(wr_buf, "hello file000\n", strlen("hello file000\n")+1); if( (wr_size = write(STDOUT_FILENO, wr_buf, strlen(wr_buf))) < 0 ) { puts("write STDOUT_FILENO error"); } //指定文件描述符的描述符复制,如果fd3=fd2,回先close fd2 fd3 = fd2 + 1; //fd3 = fd2; fd3 = dup2(STDOUT_FILENO, fd3); printf("fd3: %d\n", fd3); memcpy(wr_buf, "hello file001\n", strlen("hello file001\n")+1); if( (wr_size = write(fd3, wr_buf, strlen(wr_buf))) < 0 ) { puts("write fd3 error"); } close(fd); close(fd2); close(fd3); // //fcntl测试 // //这种方式先检测文件是否存在,如果不存在,则创建一个新文件,如果存在,则打开它 if((fd = open("/home/yang/exam_file/exam_file_op.dat",O_RDWR | O_CREAT | O_EXCL | O_RSYNC | O_DSYNC | O_SYNC, S_IRUSR | S_IWUSR)) < 0) { if((fd = open("/home/yang/exam_file/exam_file_op.dat",O_RDWR )) < 0) { err_sys("open file error"); } } //相当于dup fd2 = fcntl(fd, F_DUPFD, 0); printf("fd2: %d\n", fd2); //相当于dup2,不同的是,如果fd3已经打开,会先close fd3,然后打开fd3,这时dup2是原子操作,但fcntl不是原子操作 //fd3 = fd2 + 1; fd3 = fcntl(fd, F_DUPFD, (fd2 + 1)); printf("fd3: %d\n", fd3); //get 文件描述符标志 fd_flag = fcntl(fd, F_GETFD); printf("fd_flag: %d\n", fd_flag); //set 文件描述符标志为FD_CLOEXEC fd_flag = fcntl(fd, F_SETFD, FD_CLOEXEC); printf("set fd_flag: %d\n", fd_flag); //获取文件表标志值 fl_flag = fcntl(fd, F_GETFL); switch(fl_flag & O_ACCMODE) { case(O_RDONLY): puts("access mode is read only"); break; case(O_WRONLY): puts("access mode is write only"); break; case(O_RDWR): puts("access mode is write only"); break; default: puts("access mode is error"); } if(fl_flag & O_APPEND) { puts("O_APPEND is set"); } else { puts("O_APPEND is not set"); } //以下代码显示没有设置O_APPEND时,可以从指定位置写数据 //原子操作写文件,相当与lseek+write memcpy(wr_buf, "hello fileO_APP\n", strlen("hello fileO_APP\n")+1); if( (wr_size = pwrite(fd, wr_buf, strlen(wr_buf), 5)) < 0 ) { puts("pwrite error"); } //原子操作读文件,相当与lseek+read if((rd_size = pread(fd, rd_buf, 40, 0)) < 0) { puts("pread error"); } else if(rd_size == 0) { puts("alread reach file end"); } else { printf("rd_size: %d\n", rd_size); printf("read string: %s", rd_buf); } //为文件状态标志设置O_APPEND fl_flag |= O_APPEND; fl_flag = fcntl(fd, F_SETFL, fl_flag); //以下代码显示,设置O_APPEND后,只能从文件末尾写数据 //原子操作写文件,相当与lseek+write memcpy(wr_buf, "hello fileO_APP\n", strlen("hello fileO_APP\n")+1); if( (wr_size = pwrite(fd, wr_buf, strlen(wr_buf), 5)) < 0 ) { puts("pwrite error"); } //原子操作读文件,相当与lseek+read if((rd_size = pread(fd, rd_buf, 40, 0)) < 0) { puts("pread error"); } else if(rd_size == 0) { puts("alread reach file end"); } else { printf("rd_size: %d\n", rd_size); printf("read string: %s", rd_buf); } close(fd); exit(0); } #endif 
          
         
       
      
      
     
     
    
    
   
   



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值