头像


🎬 个人主页艾莉丝努力练剑

专栏传送门:《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录
Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平

🎬 艾莉丝的简介:

在这里插入图片描述



在这里插入图片描述


1 ~> 文件操作的系统调用和库函数各个接口

1.1 C语言文件操作常用接口

函数名 功能 头文件 参数说明
fopen 打开文件 stdio.h FILE* fopen(const char* filename, const char* mode)
fclose 关闭文件 stdio.h int fclose(FILE* stream)
fputc 写入一个字符 stdio.h int fputc(int char, FILE* stream)
fgetc 读取一个字符 stdio.h int fgetc(FILE* stream)
fputs 写入一个字符串 stdio.h int fputs(const char* str, FILE* stream)
fgets 读取一个字符串 stdio.h char* fgets(char* str, int n, FILE* stream)
fprintf 格式化写入数据 stdio.h int fprintf(FILE* stream, const char* format, ...)
fscanf 格式化读取数据 stdio.h int fscanf(FILE* stream, const char* format, ...)
fwrite 向二进制文件写入数据 stdio.h size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream)
fread 从二进制文件读取数据 stdio.h size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream)
fseek 设置文件指针的位置 stdio.h int fseek(FILE* stream, long int offset, int whence)
ftell 计算当前文件指针相对于起始位置的偏移量 stdio.h long int ftell(FILE* stream)
rewind 设置文件指针到文件的起始位置 stdio.h void rewind(FILE* stream)
ferror 判断文件操作过程中是否发生错误 stdio.h int ferror(FILE* stream)
feof 判断文件指针是否读取到文件末尾 stdio.h int feof(FILE* stream)

1.2 系统调用和库函数汇总

1.2.1 系统调用

系统调用 函数功能 头文件 参数模板 返回值
open() 打开或创建文件 <fcntl.h> int open(const char *pathname, int flags, mode_t mode); 文件描述符(成功)/-1(失败)
creat() 创建新文件 <fcntl.h> int creat(const char *pathname, mode_t mode); 文件描述符(成功)/-1(失败)
close() 关闭文件描述符 <unistd.h> int close(int fd); 0(成功)/-1(失败)
read() 从文件描述符读取数据 <unistd.h> ssize_t read(int fd, void *buf, size_t count); 读取字节数/-1(失败)
write() 向文件描述符写入数据 <unistd.h> ssize_t write(int fd, const void *buf, size_t count); 写入字节数/-1(失败)
lseek() 移动文件读写指针 <unistd.h> off_t lseek(int fd, off_t offset, int whence); 新偏移量/-1(失败)
pread() 从指定偏移处读取 <unistd.h> ssize_t pread(int fd, void *buf, size_t count, off_t offset); 读取字节数/-1(失败)
pwrite() 向指定偏移处写入 <unistd.h> ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset); 写入字节数/-1(失败)
fsync() 将文件数据同步到磁盘 <unistd.h> int fsync(int fd); 0(成功)/-1(失败)
fdatasync() 同步文件数据(不包括元数据) <unistd.h> int fdatasync(int fd); 0(成功)/-1(失败)
ftruncate() 截断/扩展文件大小 <unistd.h> int ftruncate(int fd, off_t length); 0(成功)/-1(失败)
dup() 复制文件描述符 <unistd.h> int dup(int oldfd); 新文件描述符/-1(失败)
dup2() 复制文件描述符到指定值 <unistd.h> int dup2(int oldfd, int newfd); 新文件描述符/-1(失败)
fcntl() 文件描述符控制操作 <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */); 依赖cmd/-1(失败)
ioctl() 设备I/O控制 <sys/ioctl.h> int ioctl(int fd, unsigned long request, ...); 依赖request/-1(失败)
mmap() 内存映射文件 <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 映射地址/MAP_FAILED
munmap() 解除内存映射 <sys/mman.h> int munmap(void *addr, size_t length); 0(成功)/-1(失败)
mprotect() 修改内存保护属性 <sys/mman.h> int mprotect(void *addr, size_t len, int prot); 0(成功)/-1(失败)
msync() 同步内存映射到文件 <sys/mman.h> int msync(void *addr, size_t length, int flags); 0(成功)/-1(失败)
stat() 获取文件状态信息 <sys/stat.h> int stat(const char *pathname, struct stat *statbuf); 0(成功)/-1(失败)
fstat() 通过fd获取文件状态 <sys/stat.h> int fstat(int fd, struct stat *statbuf); 0(成功)/-1(失败)
lstat() 获取符号链接状态 <sys/stat.h> int lstat(const char *pathname, struct stat *statbuf); 0(成功)/-1(失败)
access() 检查文件访问权限 <unistd.h> int access(const char *pathname, int mode); 0(成功)/-1(失败)
chmod() 修改文件权限 <sys/stat.h> int chmod(const char *pathname, mode_t mode); 0(成功)/-1(失败)
fchmod() 通过fd修改文件权限 <sys/stat.h> int fchmod(int fd, mode_t mode); 0(成功)/-1(失败)
chown() 修改文件所有者 <unistd.h> int chown(const char *pathname, uid_t owner, gid_t group); 0(成功)/-1(失败)
fchown() 通过fd修改文件所有者 <unistd.h> int fchown(int fd, uid_t owner, gid_t group); 0(成功)/-1(失败)
readlink() 读取符号链接目标 <unistd.h> ssize_t readlink(const char *pathname, char *buf, size_t bufsiz); 读取字节数/-1(失败)
rename() 重命名/移动文件 <stdio.h> int rename(const char *oldpath, const char *newpath); 0(成功)/-1(失败)
mkdir() 创建目录 <sys/stat.h> int mkdir(const char *pathname, mode_t mode); 0(成功)/-1(失败)
rmdir() 删除空目录 <unistd.h> int rmdir(const char *pathname); 0(成功)/-1(失败)

1.2.2 C语言标准库文件IO函数

库函数 函数功能 头文件 参数模板 返回值
fopen() 打开文件流 <stdio.h> FILE *fopen(const char *path, const char *mode); 文件指针/NULL
freopen() 重新打开文件流 <stdio.h> FILE *freopen(const char *path, const char *mode, FILE *stream); 文件指针/NULL
fclose() 关闭文件流 <stdio.h> int fclose(FILE *stream); 0(成功)/EOF
fread() 从文件流读取数据 <stdio.h> size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 读取元素数
fwrite() 向文件流写入数据 <stdio.h> size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); 写入元素数
fgetc() 从文件流读取字符 <stdio.h> int fgetc(FILE *stream); 字符/EOF
getc() 从文件流读取字符 <stdio.h> int getc(FILE *stream); 字符/EOF
getchar() 从标准输入读取字符 <stdio.h> int getchar(void); 字符/EOF
fputc() 向文件流写入字符 <stdio.h> int fputc(int c, FILE *stream); 字符/EOF
putc() 向文件流写入字符 <stdio.h> int putc(int c, FILE *stream); 字符/EOF
putchar() 向标准输出写入字符 <stdio.h> int putchar(int c); 字符/EOF
fgets() 从文件流读取字符串 <stdio.h> char *fgets(char *s, int size, FILE *stream); 字符串指针/NULL
fputs() 向文件流写入字符串 <stdio.h> int fputs(const char *s, FILE *stream); 非负数/EOF
fseek() 移动文件流指针 <stdio.h> int fseek(FILE *stream, long offset, int whence); 0(成功)/-1
ftell() 获取文件流当前位置 <stdio.h> long ftell(FILE *stream); 当前位置/-1
rewind() 重置文件流到开头 <stdio.h> void rewind(FILE *stream);
fgetpos() 获取文件流位置 <stdio.h> int fgetpos(FILE *stream, fpos_t *pos); 0(成功)/非0
fsetpos() 设置文件流位置 <stdio.h> int fsetpos(FILE *stream, const fpos_t *pos); 0(成功)/非0
feof() 检测文件结束标志 <stdio.h> int feof(FILE *stream); 非0(结束)/0
ferror() 检测文件错误标志 <stdio.h> int ferror(FILE *stream); 非0(错误)/0
clearerr() 清除错误和EOF标志 <stdio.h> void clearerr(FILE *stream);
fflush() 刷新文件流缓冲区 <stdio.h> int fflush(FILE *stream); 0(成功)/EOF
setvbuf() 设置文件流缓冲模式 <stdio.h> int setvbuf(FILE *stream, char *buf, int mode, size_t size); 0(成功)/非0
setbuf() 设置文件流缓冲区 <stdio.h> void setbuf(FILE *stream, char *buf);
fprintf() 格式化输出到文件流 <stdio.h> int fprintf(FILE *stream, const char *format, ...); 输出字符数/负数
printf() 格式化输出到标准输出 <stdio.h> int printf(const char *format, ...); 输出字符数/负数
sprintf() 格式化输出到字符串 <stdio.h> int sprintf(char *str, const char *format, ...); 输出字符数/负数
fscanf() 从文件流格式化输入 <stdio.h> int fscanf(FILE *stream, const char *format, ...); 匹配项数/EOF
scanf() 从标准输入格式化输入 <stdio.h> int scanf(const char *format, ...); 匹配项数/EOF
sscanf() 从字符串格式化输入 <stdio.h> int sscanf(const char *str, const char *format, ...); 匹配项数/EOF
tmpfile() 创建临时文件 <stdio.h> FILE *tmpfile(void); 文件指针/NULL
tmpnam() 生成临时文件名 <stdio.h> char *tmpnam(char *s); 文件名指针/NULL
remove() 删除文件 <stdio.h> int remove(const char *pathname); 0(成功)/-1
rename() 重命名文件 <stdio.h> int rename(const char *oldpath, const char *newpath); 0(成功)/-1
fileno() 获取文件描述符 <stdio.h> int fileno(FILE *stream); 文件描述符/-1
fdopen() 从文件描述符创建文件流 <stdio.h> FILE *fdopen(int fd, const char *mode); 文件指针/NULL

2 ~> 文件IO部分代码演示

2.1 文件操作1:打开文件方式

Makefile

myfile:myfile.c
	gcc -o $@ $^
.PHONY:clean
clean:
	rm -f myfile

myfile.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    umask(0);   // 就近原则
    int fd = open("log.txt",O_CREAT | O_WRONLY | O_TRUNC,0666); // OS本身自带的系统调用,相当于w
    if(fd < 0)  // 打开文件失败
    {
        perror("open");
        return 1;
    }
    //const char *message = "1234567890abcdefg\n";
    const char *message = "ccc";
    write(fd,message,strlen(message));

    close(fd);
    return 0;
}

//#define ONE (1<<0)   // 1
//#define TWO (1<<1)   // 2
//#define THREE (1<<2)   // 4
//#define FOUR (1<<3)   // 8
//#define FIVE (1<<4)   // 16
//
//void Print(int flags)
//{
//    if(flags & ONE)
//        printf("ONE\n");
//    if(flags & TWO)
//        printf("TWO\n");
//    if(flags & THREE)
//        printf("THREE\n");
//    if(flags & FOUR)
//        printf("FOUR\n");
//    if(flags & FIVE)
//        printf("FIVE\n");
//}
//
//int main()
//{
//    Print(ONE);
//    printf("\n");
//    Print(TWO);
//    printf("\n");
//    Print(ONE | TWO);
//    printf("\n");
//    Print(ONE | TWO | THREE);
//    printf("\n");
//    Print(ONE | TWO | THREE | FOUR);
//    printf("\n");
//    Print(TWO | THREE | FOUR | FIVE);
//}

//int main()
//{
//    //chdir("/home/Alice/118/linux-git"); // 可以修改文件新建的路径
//    //char pwd[64];
//    //getcwd(pwd, sizeof(pwd));
//    //printf("cwd: %s\n", pwd);
//
//
//    FILE *fp = fopen("log.txt", "w");
//    if(NULL == fp)
//    {
//        perror("fopen");
//        return 0;
//    }
//
//    char inbuffer[1024];
//    while(1)
//    {
//        long pos = ftell(fp);
//        printf("pos: %ld\n", pos);
//        int ch = fgetc(fp);
//        if(ch == EOF)
//        {
//            break;
//        }
//        printf("%c\n", ch);
//        //if(!fgets(inbuffer, sizeof(inbuffer), fp))
//        //{
//        //    break;
//        //}
//        printf("file : %s", inbuffer);
//    }
//
//    //const char *message = "abcd\n";
//    //fputs(message, fp);
//    ////int cnt = 0;
//    //while(cnt < 10)
//    //{
//    //    //fwrite(message, 1, strlen(message) + 1, fp);  // 文件的字符串这里不用+1,+1即算上了\0(C语言)
//    //    fputs(message, fp);
//    //    //fprintf(fp, "hello Alice: %d\n", cnt);
//    //    cnt++;
//    //}
//
//    fclose(fp);
//    return 0;
//}

log.txt

在这里插入图片描述

2.2 文件操作2:重定向

Makefile

myfile:myfile.c
	gcc -o $@ $^
.PHONY:clean
clean:
	rm -f myfile

myfile.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    //int fda = open("loga.txt",O_RDONLY);
    //dup2(fda,0);

    //int a = 0;
    //float f = 0.0f;
    //char c = 0;
    //scanf("%d %f %c",&a,&f,&c);

    //printf("%d %f %c\n",a,f,c);

    //// 联系重定向
    //// 格式化输出:上层的用户层,stdout -> 1
    //printf("hello printf\n");   // stdout -> 1
    //fprintf(stdout,"hello fprintf\n");  // stdout -> 1

    //// 三个标准流,默认打开的
    //printf("stdin: %d\n",stdin->_fileno);
    //printf("stdout: %d\n",stdout->_fileno);
    //printf("stdout: %d\n",stderr->_fileno);

    FILE *fp = fopen("log.txt","w");
    printf("log.txt: %d\n",fp->_fileno);    // 3(fd数组下标)

    fprintf(stdout,"hello stdout\n");
    close(1);
    int fda = open("loga.txt",O_CREAT | O_WRONLY | O_TRUNC,0666);
    int fdb = open("logb.txt",O_CREAT | O_WRONLY | O_TRUNC,0666);
    int fdc = open("logc.txt",O_CREAT | O_WRONLY | O_TRUNC,0666);

    fprintf(stdout,"fda : %d\n",fda);
    fprintf(stdout,"fdb : %d\n",fdb);
    fprintf(stdout,"fdc : %d\n",fdc);

    //printf(stdout,"fda : %d\n",fda);
    //printf(stdout,"fdb : %d\n",fdb);
    //printf(stdout,"fdc : %d\n",fdc);
    
    close(fda);
    close(fdb);
    close(fdc);

    return 0;
}

//// cat file.txt
//int main(int argc,char *argv[])
//{
//    if(argc != 2)   // 没有第二个参数
//    {
//        printf("Ussage: %s filename\n",argv[0]);    // ./myfile filename
//        return 1;
//    }
//    int fd = open(argv[1],O_RDONLY);    // 只读
//    if(fd < 0)  // 读错了
//    {
//        perror("open");
//        return 2;
//    }
//
//    char inbuffer[128];
//    while(1)
//    {
//        ssize_t n = read(fd,inbuffer,sizeof(inbuffer)-1);   // 预备留给\n
//        if(n > 0)
//        {
//            inbuffer[n] = 0;
//            printf("%s",inbuffer);
//        }
//        else if(n == 0)
//        {
//            printf("end of file!\n");   // 到达文件结尾
//            break;
//        }
//        else
//        {
//            perror("read");
//            break;
//        }
//    }
//
//    close(fd);
//    return 0;
//}

//// 追加
//int main()
//{
//    umask(0);
//    int fd = open("log.txt",O_CREAT | O_WRONLY | O_APPEND,0666);
//    if(fd < 0)
//    {
//        perror("open");
//        return 1;
//    }
//
//    const char *msg = "hello Alice\n";
//    int cnt = 10;
//    while(cnt--)
//    {
//        write(fd,msg,strlen(msg));
//    }
//
//    close(fd);
//    return 0;
//}

//// 写入
//int main()
//{
//    umask(0);   // 就近原则
//    int fd = open("log.txt",O_CREAT | O_WRONLY | O_TRUNC,0666); // OS本身自带的系统调用,相当于w
//    if(fd < 0)  // 打开文件失败
//    {
//        perror("open");
//        return 1;
//    }
//    //const char *message = "1234567890abcdefg\n";
//    const char *message = "ccc";
//    write(fd,message,strlen(message));
//
//    close(fd);
//    return 0;
//}

//#define ONE (1<<0)   // 1
//#define TWO (1<<1)   // 2
//#define THREE (1<<2)   // 4
//#define FOUR (1<<3)   // 8
//#define FIVE (1<<4)   // 16
//
//void Print(int flags)
//{
//    if(flags & ONE)
//        printf("ONE\n");
//    if(flags & TWO)
//        printf("TWO\n");
//    if(flags & THREE)
//        printf("THREE\n");
//    if(flags & FOUR)
//        printf("FOUR\n");
//    if(flags & FIVE)
//        printf("FIVE\n");
//}
//
//int main()
//{
//    Print(ONE);
//    printf("\n");
//    Print(TWO);
//    printf("\n");
//    Print(ONE | TWO);
//    printf("\n");
//    Print(ONE | TWO | THREE);
//    printf("\n");
//    Print(ONE | TWO | THREE | FOUR);
//    printf("\n");
//    Print(TWO | THREE | FOUR | FIVE);
//}

//int main()
//{
//    //chdir("/home/Alice/118/linux-git"); // 可以修改文件新建的路径
//    //char pwd[64];
//    //getcwd(pwd, sizeof(pwd));
//    //printf("cwd: %s\n", pwd);
//
//
//    FILE *fp = fopen("log.txt", "w");
//    if(NULL == fp)
//    {
//        perror("fopen");
//        return 0;
//    }
//
//    char inbuffer[1024];
//    while(1)
//    {
//        long pos = ftell(fp);
//        printf("pos: %ld\n", pos);
//        int ch = fgetc(fp);
//        if(ch == EOF)
//        {
//            break;
//        }
//        printf("%c\n", ch);
//        //if(!fgets(inbuffer, sizeof(inbuffer), fp))
//        //{
//        //    break;
//        //}
//        printf("file : %s", inbuffer);
//    }
//
//    //const char *message = "abcd\n";
//    //fputs(message, fp);
//    ////int cnt = 0;
//    //while(cnt < 10)
//    //{
//    //    //fwrite(message, 1, strlen(message) + 1, fp);  // 文件的字符串这里不用+1,+1即算上了\0(C语言)
//    //    fputs(message, fp);
//    //    //fprintf(fp, "hello Alice: %d\n", cnt);
//    //    cnt++;
//    //}
//
//    fclose(fp);
//    return 0;
//}

loga.txt

fda : 1
fdb : 4
fdc : 5

2.3 文件操作3:理解缓冲区

Print.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/fcntl.h>

int main()
{
    printf("hell world");   // 语言缓冲区 -> stdout->outbuffer
    sleep(40);  // 测试杀死这个进程

    //// C库函数
    //printf("hello printf\n");
    //fprintf(stdout,"hello fprintf\n");
    //const char *msg1 = "hello fputs\n";
    //fputs(msg1,stdout);

   // // 系统调用
   // const char *msg2 = "hello write\n";
   // write(1,msg2,strlen(msg2));

    //fork(); // 最后
    return 0;
}

//int main()
//{
//    fprintf(stdout,"hello stdout\n");
//    fprintf(stderr,"hello error\n");
//
//    const char *msg1 = "hello 1\n";
//    const char *msg2 = "hello 2\n";
//    write(1,msg1,strlen(msg1));
//    write(2,msg2,strlen(msg2));
//
//    return 0;
//}

2.4 文件操作4:实现stdio

Makefile

testlibc:main.c my_stdio.c
	gcc -o $@ $^
.PHONY:clean
clean:
	rm -f testlibc

main.c

#include "my_stdio.h"
#include <string.h>
#include <unistd.h>

int main()
{
    My_FILE *fp = Myfopen("log.txt","a"); // 相当于把这个log.txt当显示器了
    if(!fp) // 打开文件失败或者底层malloc失败
        return 1;

    int cnt = 10;
    //const char *msg = "hello world \n";
    const char *msg = "hello world ";
    while(cnt--)
    {
        Myfwrite(msg,1,strlen(msg),fp); // 模拟缓存的行为
        Myfflush(fp);
        sleep(2);
    }

    Myfclose(fp);

    return 0;
}

my_stdio.h

#pragma once

typedef struct{
    int fd;
    int flags;
    int mode;   // 刷新策略
    char outbuffer[1024];
    int cap;    // 缓冲区总容量
    int size;   // 缓冲区当前的数据量
    //char inbuffer[1024];
}My_FILE;

// 定义宏,是什么类型的缓冲
#define NONE_CACHE 1
#define LINE_CACHE 2
#define FULL_CACHE 4    // 1 2 4只有一个比特位为1(0001/0010/0100),检测起来比较容易

My_FILE *Myfopen(const char *pathname,const char *mode); // r w a方式 --> 打开文件
//int Myfputs(const char *message,My_FILE *fp);   // Myfputs可能太偏字符串,一会儿还要刷新,刷新还要写入,就用Myfwrite了
int Myfwrite(const char *message,int size,int num,My_FILE *fp); // 写文件
void Myfflush(My_FILE *fp); // 刷新
void Myfclose(My_FILE *fp); // 关闭文件

my_stdio.c

#include "my_stdio.h"
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h> // malloc
#include <unistd.h>

static mode_t global_mode = 0666;

// My_FILE *fp = Myfopen("log.txt","a");
// 我写的和库的基本的细节肯定不一样,但是核心思想在大的轮廓上是一样的
My_FILE *Myfopen(const char *pathname,const char *mode) // r w a方式(在内部选择不同的模式)
{
    if(pathname == NULL || mode == NULL)   
        return NULL;
    umask(0);
    int fd = 0;
    int flags = 0;
    if(strcmp(mode,"w") == 0)
    {
        flags = O_CREAT | O_WRONLY | O_TRUNC;
        fd = open(pathname,flags,global_mode);
        (void)fd;
    }
    if(strcmp(mode,"r") == 0)
    {
        flags = O_RDONLY;
        fd = open(pathname,flags);   // 传两参数的open
        (void)fd;
    }
    if(strcmp(mode,"a") == 0)
    {
        flags = O_CREAT | O_WRONLY | O_APPEND;
        fd = open(pathname,flags,global_mode);
        (void)fd;
    }
    else
    {}

    if(fd < 0)  // 文件打开失败
        return NULL;

    // 创建My_FILE对象
    My_FILE* fp = (My_FILE*)malloc(sizeof(My_FILE));
    if(!fp) // 声明失败,直接返回
        return NULL;

    fp->fd = fd;
    fp->flags = flags;
    fp->mode = LINE_CACHE;
    fp->cap = 1024; // 容量,因为缓冲区大小定义的就是1024,这一块也可以定义成宏
    fp->size = 0;   // 当前缓冲区里没有数据,size是0
    //fp->outbuffer[0] = '\0';    // 我们把它当做字符串做的,我们这里默认这样就清空了,如果受不了这样写,可以像下面这样写
    memset(fp->outbuffer,0,sizeof(fp->outbuffer));
    return fp;  // 至此上层拿到了这个被打开的文件
}


int Myfwrite(const char *message,int size,int num,My_FILE *fp)  // 一定程度上减少系统调用次数,因为不一定所有的刷新条件都满足 --> 这个Myfwrite就是之前写的fwrite,是在进行把数据刷新到缓冲区里
{
    if(message == NULL || fp == NULL)
        return -1;
    // 向C语言文件里面写,本质是向(不是操作系统,而是)缓冲区写
    // fwrite这样的接口在我们看来90%的情况不是IO函数,而是一种拷贝函数,就好比用系统调用,比如write,
    // 用write的时候把用户数据拷贝到内核当中(也只是拷贝),包括此时把内核中的数据(内存中)搬到了磁盘上,
    // 不也相当于一种拷贝嘛!只不过把存储到外设这个拷贝起名叫IO
    // 在计算机里面充满了大量的IO,包括未来学习网络部分的时候,把数据写到网络里面,从网络里读,
    // 其实本质上依然是从设备当中拷贝数据到内存,内存当中拷贝数据到外设,所以一切皆拷贝,
    // 当它都是拷贝时每个阶段不同的拷贝起个名字就叫IO
    // 所以向C语言文件里面写,本质是向缓冲区写,至于怎么办就不用我们管了
    int total = size * num;
    //if(total + fp->size > fp->cap)  // 没有考虑\0的问题
    if(total + fp->size > fp->cap - 1)  // 保证不会越界 --> 总容量给\0留个位置
        return -1;
    
    // 写入 --> 就相当于在进行数据拷贝 --> 核心
    memcpy(fp->outbuffer + fp->size,message,total); // 没有考虑\0的问题,保险起见就加个1,这里已经考虑到了
    fp->size +=  total;
    fp->outbuffer[fp->size] = 0;

    
    if(fp->outbuffer[fp->size - 1] == '\n' && (fp->mode & LINE_CACHE))
        Myfflush(fp);   // 如果字符串结尾是\n,并且模式是LINE(行缓冲),就直接强制刷新

    //// fwrite判断是否刷新,这不就是刷新吗(所谓的刷新就是把数据拷贝给内存)!!!
    //if(fp->size > 0 && fp->outbuffer[fp->size - 1] == '\n' && (fp->mode & LINE_CACHE))  // 刷新条件满足时才会调用系统调用
    //{
    //    // 系统调用
    //    write(fp->fd,fp->outbuffer,fp->size);
    //    fp->size = 0;   // 清空
    //    //fp->outbuffer = 0;  // 不清空也行
    //    // printf格式化也是输出到缓冲区
    //}

    return num; // num: 一共写了多少字节
}

void Myfflush(My_FILE *fp)
{
    if(!fp)
        return;

    // fwrite判断是否刷新,这不就是刷新吗(所谓的刷新就是把数据拷贝给内存)!!!
    if(fp->size > 0) // 强制刷新,只要有数据就刷新
    {
        // 系统调用
        // 所谓的刷新就是把数据从用户缓冲区拷贝到内核
        // 像从用户缓冲区拷贝到内核这种模式叫做WB模式
        // WB: Write Back(写回)
        write(fp->fd,fp->outbuffer,fp->size);
        fp->size = 0;   // 清空
        //fp->outbuffer = 0;  // 不清空也行
        // printf格式化也是输出到缓冲区
        
        // WT模式,Write Though
        // 不仅仅要写入到内核缓冲区,还必须写到对应的硬件上
        fsync(fp->fd);  // 强度做大一点
    }   
}


void Myfclose(My_FILE *fp) // 关闭文件
{
    if(!fp)
        return;

    Myfflush(fp); // 只要有数据,无脑刷新
    close(fp->fd);  // 先刷新再关闭!先刷新,再关闭文件描述符!
}

结尾

uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!

结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主“一键四连”哦!

往期回顾

【Linux进程控制(三)】实现自主Shell命令行解释器

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡
૮₍ ˶ ˊ ᴥ ˋ˶₎ა

在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐