代码仓库地址:https://gitee.com/tgwTTT/linux-learning-dai/tree/master/glibc

文件:

1.狭义理解:

• ⽂件在磁盘⾥

• 磁盘是永久性存储介质,因此⽂件在磁盘上的存储是永久性的

• 磁盘是外设(即是输出设备也是输⼊设备)

• 磁盘上的⽂件 本质是对⽂件的所有操作,都是对外设的输⼊和输出 简称 IO

2.广义理解:

Linux 下⼀切皆⽂件(键盘、显⽰器、⽹卡、磁盘…… 这些都是抽象化的过程)

3.文件特点:

• 对于 0KB 的空⽂件是占⽤磁盘空间的 •

⽂件是⽂件属性(元数据)和⽂件内容的集合(⽂件 = 属性(元数据)+ 内容)

• 所有的⽂件操作本质是⽂件内容操作和⽂件属性操作

下面以c文件接口为例去介绍IO

c文件接口:

fwrite()

fread()

除此之外会默认打开三个输⼊输出流stdin & stdout & stderr

接下来是linux系统IO接口介绍

打开⽂件的⽅式不仅仅是fopen,ifstream等流式,语⾔层的⽅案,其实系统才是打开⽂件最底层的⽅案。不过,在学习系统⽂件IO之前,先要了解下如何给函数传递标志位,该⽅法在系统⽂件IO接⼝中会使⽤到:

例如这种方式:

#include 
#define ONE 0001 //0000 0001
#define TWO 0002 //0000 0010
#define THREE 0004 //0000 0100
void func(int flags) {
	if (flags & ONE) printf("flags has ONE! ");
	if (flags & TWO) printf("flags has TWO! ");
	if (flags & THREE) printf("flags has THREE! ");
	printf("\n");
} 
int main() {
	func(ONE);
	func(THREE);
	func(ONE | TWO);
	func(ONE | THREE | TWO);
	return 0;
}

open函数:

pathname: 要打开或创建的⽬标⽂件

flags: 打开⽂件时,可以传⼊多个参数选项,⽤下⾯的⼀个或者多个常量进⾏“或”运算,构成flags。

write函数:

文件描述符fd

文件数组buf

大小count

下面是用系统函数调用实现c库中的函数

#include 
#include 
#include 
#include 
#include 

// 刷新方式
#define LINE_FLUSH 0x1

// 自定义 FILE 结构
struct MYFILE {
    int fileno;               // 文件描述符
    int flag;                 // 打开标志
    int bufferlen;            // 缓冲区中已用字节数
    char outbuffer[1024];     // 输出缓冲区
    int flush_method;         // 刷新策略
};

// 创建并初始化 MYFILE 对象
static MYFILE* BuyFile(int fd, int flag) {
    MYFILE* f = new MYFILE();
    if (f == nullptr) return nullptr;

    f->bufferlen = 0;
    f->fileno = fd;
    f->flag = flag;
    f->flush_method = LINE_FLUSH;
    memset(f->outbuffer, 0, sizeof(f->outbuffer));

    return f;
}

// 自定义 fopen
MYFILE* MYFopen(const char* path, const char* mode) {
    int fd = -1;
    int flag = 0;

    if (strcmp(mode, "w") == 0) {
        flag = O_CREAT | O_WRONLY | O_TRUNC;
        fd = open(path, flag, 0666);
    }
    else if (strcmp(mode, "a") == 0) {
        flag = O_CREAT | O_WRONLY | O_APPEND;
        fd = open(path, flag, 0666);
    }
    else if (strcmp(mode, "r") == 0) {
        flag = O_RDONLY;
        fd = open(path, flag);
    }
    else {
        // 非法模式
        return nullptr;
    }

    if (fd < 0) return nullptr;

    return BuyFile(fd, flag);
}

// 自定义 fclose
void MYFclose(MYFILE* file) {
    if (file) {
        MYFflush(file);  // 关闭前刷新
        close(file->fileno);
        delete file;
    }
}

// 自定义 fflush
void MYFflush(MYFILE* file) {
    if (file == nullptr || file->bufferlen == 0) return;

    ssize_t n = write(file->fileno, file->outbuffer, file->bufferlen);
    if (n < 0) {
        perror("write failed");
    }
    file->bufferlen = 0;
}

// 自定义 fwrite
int MYFwrite(MYFILE* file, const void* str, int len) {
    if (file == nullptr || str == nullptr || len <= 0) return 0;

    // 如果缓冲区装不下,先刷新
    if (file->bufferlen + len > (int)sizeof(file->outbuffer)) {
        MYFflush(file);
    }

    // 拷贝到缓冲区
    memcpy(file->outbuffer + file->bufferlen, str, len);
    file->bufferlen += len;

    // 行缓冲:遇到 '\n' 就刷新
    if ((file->flush_method & LINE_FLUSH) && file->outbuffer[file->bufferlen - 1] == '\n') {
        MYFflush(file);
    }

    return len;
}

// 测试用例
int main() {
    MYFILE* fp = MYFopen("test.txt", "w");
    if (!fp) {
        perror("MYFopen failed");
        return 1;
    }

    const char* msg1 = "Hello, world!\n";
    const char* msg2 = "This is a test.\n";

    MYFwrite(fp, msg1, strlen(msg1));
    MYFwrite(fp, msg2, strlen(msg2));

    MYFclose(fp);
    return 0;
}
下面是运行结果:

今天的更新就到这里,如有错误欢迎评论区指出,谢谢