LinuxC—标准IO与文件IO学习笔记

本文是关于Linux环境下C语言的标准IO和文件IO的学习笔记,详细介绍了标准IO与文件IO的区别,包括FILE结构体、文件打开与关闭、输入输出函数、文件位置指针操作等内容,同时探讨了标准IO的可移植性和系统IO的效率问题。

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

标准IO与文件IO

1 概述

  • stdio 标准IO(优先使用)
  • sysio 系统调用IO(文件IO)

sysio是直接实现用户态切换内核态,sysio和平台是有关系的,比如windows和linux,而stdio标准IO提供了一系列标准接口使用,不用去关心平台的限制(stdio底层是依赖sysio实现的),所以stdio的可移植性比sysio好。

2 标准IO

FILE结构体

  • FILE类型贯穿始终

2.1 文件打开与关闭

  • fopen() 打开文件

    //filename是要打开的文件的名称
    //modes是打开文件方式:读、写
    //打开文件产能共返回FILE指针,否则返回NULL,并且设置errno
    extern FILE *fopen (const char *__restrict __filename,
              const char *__restrict __modes) __wur;
    

    modes :

    • “r”,只读且当前流指针指向文件的开头(要求文件存在)
    • “r+”,可读可写,且当前流指针指向文件的开头(要求文件存在)
    • “w”,如果文件存在则清空文件,不存在则创建文件,同时流指针指向文件开头
    • “w+”,以读写形式打开文件,文件有则清空,无则创建,同时流指针指向文件开头
    • “a”,以追加写的形式打开文件,且文件不存在会创建文件,同时流指针指向文件末尾
    • “a+”,以读写形式打开文件,且文件不存在会创建文件,流指针如果是读则在文件开头,写则在文件末尾

    ‘b’,字符b用来表示字节流(二进制),不加b则表示是字符流,一个进程能够打开的文件个数是1024个,其中stdin,stdout和stderr是默认打开的,在shell中输入以下命令即可查看open files个最大个数

    ulimit -a
    
  • fclose() 关闭文件

    //关闭流
    extern int fclose (FILE *__stream);
    

示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main() {
    FILE * fp = fopen("tmp", "r");
    if (fp == NULL) {
        //fprintf(stderr, "fopen() failed!errno = %d\n", errno);//打印fopen() failed!errno = 2
        //perror("fopen()"); //打印fopen(): No such file or directory
        fprintf(stderr, "fopen()failed:%s\n", strerror(errno)); //打印fopen()failed:No such file or directory
        exit(1);
    }
    puts("ok");
    fclose(fp);

    exit(0);
}

2.2 文件输入输出函数

2.2.1 fgetc()/fputc() 字符操作
  • fgetc() 从一个流中读取一个字符,读到文件末尾时返回EOF,否则返回的就是读出来的字符

    /* Read a character from STREAM.
    
       These functions are possible cancellation points and therefore not
       marked with __THROW.  */
    extern int fgetc (FILE *__stream);
    
  • fuptc() 给一个流中写入一个字符

    /* Write a character to STREAM.
    
       These functions are possible cancellation points and therefore not
       marked with __THROW.
    
       These functions is a possible cancellation point and therefore not
       marked with __THROW.  */
    extern int fputc (int __c, FILE *__stream);
    
  • 示例一:文本文件复制

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main() {
    FILE *src, *dest;
    int c;
    src = fopen("./src", "r");
    if (src == NULL) {
        perror("fopen() src");
        exit(1);
    }
    dest = fopen("./dest", "w");
    if (dest == NULL) {
        perror("fopen() dest");
        exit(1);
    }

    while (1) {
        c = fgetc(src);
        if (c == EOF) break;
        fputc(c, dest);
    }

    fclose(dest);
    fclose(src);

    exit(0);
}
  • 示例二:统计文本字符
int main() {
    FILE *fp;
    int cnt = 0;
    fp = fopen("./src", "r");
    if (fp == NULL) {
        perror("fopen() src");
        exit(1);
    }
    while (fgetc(fp) != EOF) {
        cnt++;
    }
    printf("./src文件共有%d个字符\n", cnt);

    fclose(fp);

    exit(0);
}
2.2.2 fgets()/fputs() 字符串操作
  • fgets() 从流中读取最多n个字符到s中去,读完文件时返回一个NULL,否则返回的就是读出来的字符串

    /* Get a newline-terminated string of finite length from STREAM.
    
       This function is a possible cancellation point and therefore not
       marked with __THROW.  */
    extern char *fgets (char *__restrict __s, int __n, FILE *__restrict __stream)
         __wur;
    

    fgets()函数的结束条件:

    • 读取到了n-1个字符
    • 读到’\n’
  • fputs() 像一个流中写入一个字符串

    /* Write a string to STREAM.
    
       This function is a possible cancellation point and therefore not
       marked with __THROW.  */
    extern int fputs (const char *__restrict __s, FILE *__restrict __stream);
    
  • 示例:文本文件复制

    #define SIZE 5
    int main() {
        FILE *src, *dest;
        char buf[SIZE];
        src = fopen("./src", "r");
        if (src == NULL) {
            perror("fopen() src");
            exit(1);
        }
        dest = fopen("./dest", "w");
        if (dest == NULL) {
            perror("fopen() dest");
            exit(1);
        }
    
        while (fgets(buf, SIZE, src) != NULL) {
            fputs(buf, dest);
        }
    
        fclose(dest);
        fclose(src);
    
        exit(0);
    }
    
2.2.3 fread()/fwrite() 二进制数据操作
  • fread() 从stream流中读取n个对象,每个对象大小为size,并将读到的对象写到ptr中,所以ptr的大小为size*n,返回值是成功读到的对象个数

    /* Read chunks of generic data from STREAM.
    
       This function is a possible cancellation point and therefore not
       marked with __THROW.  */
    extern size_t fread (void *__restrict __ptr, size_t __size,
               size_t __n, FILE *__restrict __stream) __wur;
    
  • fwrite() 向s流中写入ptr中的数据,返回值是成功写的对象的个数

    /* Write chunks of generic data to STREAM.
    
       This function is a possible cancellation point and therefore not
       marked with __THROW.  */
    extern size_t fwrite (const void *__restrict __ptr, size_t __size,
                size_t __n, FILE *__restrict __s);
    
  • 示例:文件复制

    #define SIZE 5
    int main() {
        FILE *src, *dest;
        char buf[SIZE];
        src = fopen("./src", "r");
        if (src == NULL) {
            perror("fopen() src");
            exit(1);
        }
        dest = fopen("./dest", "w");
        if (dest == NULL) {
            perror("fopen() dest");
            exit(1);
        }
        size_t n = 0;
        while ((n = fread(buf, 1, SIZE, src)) > 0) {
            fwrite(buf, 1, n, dest);
        }
    
        fclose(dest);
        fclose(src);
    
        exit(0);
    }
    

2.3 printf()和scanf()函数族

printf()函数族

  • printf() 格式化输出到stdout

    /* Write formatted output to stdout.
    
       This function is a possible cancellation point and therefore not
       marked with __THROW.  */
    extern int printf (const char *__restrict __format, ...);
    
  • fprintf() 格式化初始到一个指定的stream中

    /* Write formatted output to STREAM.
    
       This function is a possible cancellation point and therefore not
       marked with __THROW.  */
    extern int fprintf (FILE *__restrict __stream,
              const char *__restrict __format, ...);
    
  • sprintf() 将格式化字符串输出到s中去

    /* Write formatted output to S.  */
    extern int sprintf (char *__restrict __s,
              const char *__restrict __format, ...) __THROWNL;
    
  • snprintf() 将格式化输出输出到s中去,最大不超过maxlen个字符,这个方法可以检查缓冲区溢出

    /* Maximum chars of output to write in MAXLEN.  */
    extern int snprintf (char *__restrict __s, size_t __maxlen,
               const char *__restrict __format, ...)
         __THROWNL __attribute__ ((__format__ (__printf__, 3, 4)));
    

scanf()函数族

  • scanf() 从终端读取内容按照一定格式放到…中的地址中去

    /* Read formatted input from stdin.
    
       This function is a possible cancellation point and therefore not
       marked with __THROW.  */
    extern int scanf (const char *__restrict __format, ...) __wur;
    
  • fscanf() 从指定的流中获取内容按照一定格式放到…中的地址中去

    /* Read formatted input from STREAM.
    
       This function is a possible cancellation point and therefore not
       marked with __THROW.  */
    extern int fscanf (FILE *__restrict __stream,
             const char *__restrict __format, ...) __wur;
    
  • sscanf() 从制定字符串中获取内容按照一定格式放到…中的地址中去

    /* Read formatted input from S.  */
    extern int sscanf (const char *__restrict __s,
             const char *__restrict __format, ...) __THROW;
    

2.4 文件位置指针操作

  • fseek() 将流指针相对whence便宜off个字节,成功返回0,失败返回-1

    /* Seek to a certain position on STREAM.
    
       This function is a possible cancellation point and therefore not
       marked with __THROW.  */
    extern int fseek (FILE *__stream, long int __off, int __whence);
    

    whence有三种可选的位置:

    • SEEK_SET 文件开头
    • SEEK_CUR 当前位置
    • SEEK_END 文件末尾

    fseek()可以用来生成空洞文件,所谓的空洞文件就是所有字节处都是’\0’的文件,当下载文件时会先创建一个文件,然后直接通过fseek()定位到源文件的末尾处,然后再来下载填充这个文件

  • ftell() 返回当前流指针的位置

    /* Return the current position of STREAM.
    
       This function is a possible cancellation point and therefore not
       marked with __THROW.  */
    extern long int ftell (FILE *__stream) __wur;
    
  • rewind() 将文件指针置到文件的开头

    /* Rewind to the beginning of STREAM.
    
       This function is a possible cancellation point and therefore not
       marked with __THROW.  */
    extern void rewind (FILE *__stream);
    
  • fseeko()/ftello() 这两个函数将long替换成了off_t类型:

    原函数在32位机器上只能定义2G的文件大小,修改为off_t类型后再在编译上加上如下内容就能够定义更大的文件了

    On many architectures both off_t and long are 32-bit types, but compilation with
                  #define _FILE_OFFSET_BITS 64
           will turn off_t into a 64-bit type.
    
  • 示例:返回当前文件的大小

    int main() {
        FILE *fp;
        int cnt = 0;
        fp = fopen("./src", "r");
        if (fp == NULL) {
            perror("fopen():");
            exit(1);
        }
        fseek(fp, 0, SEEK_END);
        cnt = ftell(fp);
        printf("文件大小为%dB\n",cnt);
        fclose(fp);
        exit(0);
    }
    
  • 示例:从指定位置读取文件

    int main() {
        FILE *fp;
        int cnt = 0;
        fp = fopen("./src", "r");
        if (fp == NULL) {
            perror("fopen():");
            exit(1);
        }
        fseek(fp, 6, SEEK_SET);
        int c = 0;
        while ((c = fgetc(fp)) != EOF) {
            printf("%c", c);
        }
    
        fclose(fp);
        exit(0);
    }
    

2.5 fflush()刷新

刷新流所写的缓冲区

/* Flush STREAM, or all streams if STREAM is NULL.

   This function is a possible cancellation point and therefore not
   marked with __THROW.  */
extern int fflush (FILE *__stream);

缓冲区

  • 作用:大多数情况下是好事,合并系统调用,减少内核态和用户态的切换次数,标准IO的缓冲区是在用户空间
  • 分类:
    • 行缓冲:换行时候刷新,满了的时候刷新,强制刷新(标准输出是这样的,因为是终端设备)
    • 全缓冲:满了的时候刷新,强制刷新(默认,只要不是终端设备)
    • 无缓冲:如stderr,需要立即输出的内容

2.6 getline()读取一行

getline()底层其实就是先malloc()一块内存,内存字节数赋值给n,并将读取到的一行的首地址赋值给*lineptr,当malloc()的内存不够一行使用的时候就会realloc()一块更大的内存,并经内存字节数赋值给n后使用

返回值表示能够读取到的字符的个数,不包含尾0,返回-1则表示失败了

/* Like `getdelim', but reads up to a newline.

   This function is not part of POSIX and therefore no official
   cancellation point.  But due to similarity with an POSIX interface
   or due to the implementation it is a cancellation point and
   therefore not marked with __THROW.  */
extern _IO_ssize_t getline (char **__restrict __lineptr,
             size_t *__restrict __n,
             FILE *__restrict __stream) __wur;

示例:

int main() {
    FILE *fp;
    char *buf;
    size_t linesize;
    fp = fopen("./src", "r");
    if (fp == NULL) {
        perror("fopen()");
        exit(1);
    }
    buf = NULL;
    linesize = 0;
    while (getline(&buf, &linesize, fp) >= 0) {
        printf("%s", buf);
        printf("%ld ", strlen(buf));
        printf("%ld\n", linesize);
    }

    fclose(fp);
    exit(0);
}

结果:可以看到两行的src文件第一行分配了120字节的空间,第二行分配了240字节的空间

hello world!
 13 120
we have a dream, hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
 194 240

需要注意的是这个函数有一个可控的内存泄漏,因为*lineptr指向的空间并没有函数来进行释放

  • getline()模拟实现

    #define SIZE 120
    
    int my_getline(char **lineptr, size_t *linesize, FILE *fp) {
        char *ret = malloc(SIZE);
        char *tmp;
        *linesize = SIZE;
        while (1) {
            tmp = fgets(ret, *linesize, fp);
            if (tmp == NULL) return -1;
            if (ret[strlen(ret) - 1] == '\n') {
                *lineptr = ret;
                return 0;
            }
            fseek(fp, -*linesize + 1, SEEK_CUR);
            *linesize += *linesize;
            ret = realloc(ret, *linesize);
        }
    }
    

2.7 临时文件

  • tmpnam() 返回一个临时文件名

    /* Generate a temporary filename.  */
    extern char *tmpnam (char *__s) __THROW __wur;
    

    该函数存在并发问题,因为拿到文件名后还需要用fopen()来创建,可能出现两个进程拿到同一个临时文件名的情况

  • tmpfile() 创建一个匿名文件,以二进制读写的形式打开(w+b),可以通过fclose()来进行关闭

    /* Create a temporary file and open it read/write.
    
       This function is a possible cancellation point and therefore not
       marked with __THROW.  */
    #ifndef __USE_FILE_OFFSET64
    extern FILE *tmpfile (void) __wur;
    

3 文件IO

3.1 文件描述符

文件描述符实际上就是一个整型数,即一个数组下标

  • 在标准IO中当我们调用fopen()打开一个文件的时候会返回一个FILE*供我们使用
  • 在系统IO中当我们调用open()时会返回一个整型的文件描述符给我们使用,其实当调open()时也会创建一个该文件的结构体来存放该文件的属性,该结构体的指针被存放在一个数组中,系统将这个数组的下标返回给我们来使用,从而将这个结构体对用户屏蔽了

注意点

  • 文件描述符数组的大小就是前面ulimit -a命令中看到的open files的大小,即1024,且文件描述符优先使用可用范围中的最小的

  • 文件描述符数组是每个进程私有的

  • 一个进程中多次打开同一个文件时打开多少次就会产生多少个文件描述符

  • 也存多多个文件描述符指向同一个文件结构体的情况,所以文件结构体中有一个文件打开计数器,标记了该文件被多少个文件描述符指向,只有当这个计数器为0的时候后才会最终被free掉

/dev/fd

这是一个虚目录,打开这个目录,能够看到当前打开这个目录的进程的文件描述符信息

3.2 open()/close()打开/关闭文件

  • open() pathname就是一个文件名,flags是一个位图,代表了使用权限

    /* Open FILE and return a new file descriptor for it, or -1 on error.
       OFLAG determines the type of access used.  If O_CREAT or O_TMPFILE is set
       in OFLAG, the third argument is taken as a `mode_t', the mode of the
       created file.
    
       This function is a cancellation point and therefore not marked with
       __THROW.  */
    extern int open (const char *__file, int __oflag, ...) __nonnull ((1));
    
    int open(const char *pathname, int flags);
    

    成功则返回一个文件描述符fd,失败则返回-1

    flags描述:

    The argument flags must include one of the following access modes: O_RDONLY, O_WRONLY, or O_RDWR.  These request opening the file read-only, write-only, or read/write, respectively.
    In  addition,  zero or more file creation flags and file status flags can be bitwise-or'd in flags.  The file creation flags are O_CLOEXEC, O_CREAT, O_DIRECTORY, O_EXCL, O_NOCTTY, O_NOFOL‐LOW, O_TMPFILE, and O_TRUNC.  The file status flags are all of the remaining flags listed below.  The distinction between these two groups of flags is that the file creation  flags  affect the semantics of the open operation itself, while the file status flags affect the semantics of subsequent I/O operations.  The file status flags can be retrieved and (in some cases) modified; see fcntl(2) for details.
    
    • O_CREAT 表示创建
    • O_TRUNC 表示截断
    • O_EXCL 表示必须打开一个新文件
    • O_APPEND 表示追加

    和fopen()中的modes的比较如下:

    • r -> O_RDONLY
    • r+ -> O_RDWR
    • w -> O_WRONLY|O_CREAT|O_TRUNC
    • w+ -> O_RDWR|O_CREAT|O_TRUNC
    • a -> O_WRONLY|O_APPEND|O_CREAT
    • a -> O-RDWR|O_APPEND|O_CREAT
  • close()关闭一个文件描述符,返回0表示成功,-1表示失败

    /* Close the file descriptor FD.
    
       This function is a cancellation point and therefore not marked with
       __THROW.  */
    extern int close (int __fd);
    

3.3 read()/write()读写文件

  • read() 企图读一个文件描述符fd中的nbytes字节内容到buf中去,成功返回读取到的字节总数,返回0则表示读到文件尾,失败返回-1同时设置errno

    /* Read NBYTES into BUF from FD.  Return the
       number read, -1 for errors or 0 for EOF.
    
       This function is a cancellation point and therefore not marked with
       __THROW.  */
    extern ssize_t read (int __fd, void *__buf, size_t __nbytes) __wur;
    
  • write() 读取n字节的buf的内容到文件描述符fd中去,返回写的字节总数或-1

    /* Write N bytes of BUF to FD.  Return the number written, or -1.
    
       This function is a cancellation point and therefore not marked with
       __THROW.  */
    extern ssize_t write (int __fd, const void *__buf, size_t __n) __wur;
    

3.4 lseek()文件描述符定位

将fd文件中的位置指针相对whence位置便宜offset字节,返回值是文件开始处到当前位置的字节数

/* Move FD's file position to OFFSET bytes from the
   beginning of the file (if WHENCE is SEEK_SET),
   the current position (if WHENCE is SEEK_CUR),
   or the end of the file (if WHENCE is SEEK_END).
   Return the new file position.  */
extern __off_t lseek (int __fd, __off_t __offset, int __whence) __THROW;

练习:文件复制

#define BUFSIZE 1024
int main() {

    int fd_src = -1, fd_dest = -1, len = 0, ret = 0, pos = 0;
    char *src = "src";
    char *dest = "dest";
    char *buf[BUFSIZE];
    fd_src = open(src, O_RDONLY);
    if (fd_src < 0) {
        perror("open()");
        exit(1);
    }
    fd_dest = open(dest, O_WRONLY | O_CREAT, O_TRUNC, 0600);
    if (fd_dest < 0) {
        close(fd_src);//到这儿说明src已经打开了,需要释放空间防止内存泄漏
        perror("open()");
        exit(1);
    }
    while ((len = read(fd_src, buf, BUFSIZ)) > 0) {
        /**
         * 可能出现读写速度不一致的情况,所以这里就是调用一次write时并没有将buf中的数据全部写完
         * 所以write应该放在一个循环中,同时需要有一个pos来指定从buf的哪个偏移处开始读取数据
         */
        pos = 0;
        while (len > 0) {
            ret = write(fd_dest, buf + pos, len);
            if (ret < 0) {
                perror("write()");
                exit(1);
            }
            pos += ret;
            len -= ret;
        }
    }

    close(fd_dest);
    close(fd_src);
    exit(0);
}

3.5 文件共享

文件共享:多个任务共同操作一个文件或者协同完成任务

  • truncate() 将一个未打开的文件file截断到length字节
/* Truncate FILE to LENGTH bytes.  */
extern int truncate (const char *__file, __off_t __length)
  • ftruncate() 将一个已打开的文件截断到length字节
/* Truncate the file FD is open on to LENGTH bytes.  */
extern int ftruncate (int __fd, __off_t __length) __THROW __wur;

练习:写程序删除一个文件的第10行

#define BUFSIZE 1024
int main() {

    int fd_r, fd_w;
    char buf[2];
    int rows; //记录行数
    int trunc_num = 0; //记录截断处
    //用两个fd以不同方式打开同一个文件
    //fd_r负责从第line+1行开始读,fd_w负责从第line行开始写
    fd_r = open("src", O_RDONLY);
    if (fd_r == -1) {
        perror("open()");
        exit(1);
    }
    fd_w = open("src", O_RDWR);
    if (fd_w == -1) {
        close(fd_r);
        perror("open()");
        exit(1);
    }
    //fd_r定位到第11行
    while (1) {
        int n = read(fd_r, buf, 1);
        if (n < 0) {
            perror("read()");
            break;
        }
        if (buf[0] != '\n') continue;
        rows++;
        if (rows == 10) break;
    }
    //fd_w定位到第10行
    rows = 0;
    while (1) {
        int n = read(fd_w, buf, 1);
        if (n < 0) {
            perror("read()");
            break;
        }
        if (buf[0] != '\n') continue;
        rows++;
        if (rows == 9) break;
    }
    //开始读写文件
    char buf_[BUFSIZE];
    int len, pos, ret;
    while ((len = read(fd_r, buf_, BUFSIZE)) > 0) {
        pos = 0;
        while (len > 0) {
            ret = write(fd_w, buf_ + pos, len);
            if (ret < 0) {
                perror("write()");
                exit(1);
            }
            pos += ret;
            len -= ret;
        }
    }
    //截断文件
    trunc_num = lseek(fd_w, 0, SEEK_CUR);
    ftruncate(fd_w, trunc_num);

    close(fd_r);
    close(fd_w);
    exit(0);
}

3.6 原子操作和程序中的重定向

原子操作

  • 定义

    原子操作就是不可分割的操作

  • 作用

    解决竞争和冲突

程序中的重定向

  • dup() 拷贝一份fd到当前可用最小的fd上去

    /* Duplicate FD, returning a new file descriptor on the same file.  */
    extern int dup (int __fd) __THROW __wur;
    
  • dup2() 关闭fd2并将fd的一个副本拷贝到原先fd2的位置上

    /* Duplicate FD to FD2, closing FD2 and making it open on the same file.  */
    extern int dup2 (int __fd, int __fd2) __THROW;
    

一个小demo:将终端输出stdout关闭后再fd=1的位置放上自己的打开文件

  • 以下代码执行后会创建一个out文件,文件中的内容就是hello world!
int main() {

    int fd;

    close(1);//关闭stdout

    fd = open("out", O_WRONLY | O_CREAT | O_TRUNC, 0600);
    if (fd < 0) {
        perror("open()");
        exit(1);
    }


    puts("hello world!");

    exit(0);
}
  • 使用dup
int main() {

    int fd;

    fd = open("out", O_WRONLY | O_CREAT | O_TRUNC, 0600);
    if (fd < 0) {
        perror("open()");
        exit(1);
    }

    close(1);
    dup(fd);
    close(fd);//此处关闭原来的fd,只留fd=1位置上的副本


    puts("hello world!");

    exit(0);
}

dup操作存在的问题:

  • 他是先关闭想要拷贝到的位置的fd,然后再dup到该位置上,这不是一个原子操作,在多线程情况下,会出现线程安全的问题,比如线程1close掉1后挂起,线程2打开一个fd此时会占用1的位置,从而导致线程1再执行dup时并不会达到想要的效果。

  • 再就是如果本身fd=1的话在执行close(1)后再调用dup(fd)是失败的

  • 所以引入了dup2()函数,以上代码可以将close(1);dup(fd);合并为一个dup2调用即dup2(fd, 1);同时该函数也能避免第二个问题,因为当fd和fd2相同时会不做任何事

###3.7 同步函数

  • sync() 同步内核的cache到磁盘中

    /* Make all changes done to all files actually appear on disk.  */
    extern void sync (void) __THROW;
    
  • fsync() 同步一个文件的cache到磁盘中

    /* Make all changes done to FD actually appear on disk.
    
       This function is a cancellation point and therefore not marked with
       __THROW.  */
    extern int fsync (int __fd);
    
  • fdatasync() 同步一个文件,只刷数据,不刷亚数据

    /* Synchronize at least the data part of a file with the underlying
       media.  */
    extern int fdatasync (int __fildes);
    

3.8 fcntl()/ioctl()

  • fcntl() 针对文件描述符的所有函数都起源于该函数,对fd执行cmd操作,不同的cmd操作需要不同的参数

    /* Do the file control operation described by CMD on FD.
       The remaining arguments are interpreted depending on CMD.
    
       This function is a cancellation point and therefore not marked with
       __THROW.  */
    extern int fcntl (int __fd, int __cmd, ...);
    
  • iocntl() 设备相关的内容,对于fd设备执行request对应的操作

    /* Perform the I/O control operation specified by REQUEST on FD.
       One argument may follow; its presence and type depend on REQUEST.
       Return value depends on REQUEST.  Usually -1 indicates error.  */
    extern int ioctl (int __fd, unsigned long int __request, ...) __THROW;
    

4 标准IO和文件IO的区别

  • 标准IO的吞吐量大,系统的IO的响应速度快

  • 注意:标准IO和系统IO不能够混用,转换函数

    • fileno() 得到一个流的系统fd
    /* Return the system file descriptor for STREAM.  */
    extern int fileno (FILE *__stream) __THROW __wur;
    
    • fdopen() 得到一个系统IO打开的fd对应的流
    /* Create a new stream that refers to an existing system file descriptor.  */
    extern FILE *fdopen (int __fd, const char *__modes) __THROW __wur;
    
  • 示例:如下代码会输出

    a
    bbbaa
    

    第一个putchar后输出’\n’会刷新缓冲区从而输出’a’

    后面两个putchar会先将两个a都存放在缓冲区,待程序结束时刷新缓冲区,而write每调用一次都会直接输出

    int main() {
        putchar('a');
        putchar('\n');
        write(1, "b", 1);
    
        putchar('a');
        write(1, "b", 1);
    
        putchar('a');
        write(1, "b", 1);
        
        exit(0);
    }
    

关系图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V2T9vesa-1673012008012)(../牛客项目/temp/image-20230106213133097.png)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值