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

本文详细介绍了文件操作的基础概念,包括文件标识符的作用、文件描述符表的结构及其维护方式。此外,还深入探讨了五个核心文件操作函数:open、write、read、lseek和close的具体用法及注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

不带缓冲是指直接使用系统调用,在内核与调用者之间没有缓冲,但是在内核与设备之间还是有缓冲高速缓存或页面高速缓存存在的。这与使用标准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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值