69天探索操作系统-第25天:文件系统基础

1.介绍

文件系统是操作系统的基本组件,用于管理数据存储和检索。本文探讨了文件系统的基本概念和实现细节,重点关注构成复杂文件系统结构的基础文件概念

2.文件系统核心概念

基本要素:

  • 文件

    • 解释:文件是存储在次要存储器上的相关数据的命名集合。文件是文件系统中的基本存储单元。文件可以包含程序、数据或两者兼有。操作系统抽象了物理存储细节,将文件作为逻辑存储单元展示。
  • 目录

    • 解释:目录是包含其他文件和目录信息的一种特殊文件。它们提供了组织结构,并包含有关所包含文件的元数据,包括文件名、位置和属性。
  • 路径

    • 解释:路径表示目录层次结构中的文件位置。它们可以是绝对的(从根目录),也可以是相对的(从当前目录)。理解路径解析对于文件系统操作至关重要。

3.文件属性及其操作

以下是一个代表基本文件属性的 C 结构体:

c 复制代码
#include <time.h>

typedef struct FileAttributes {
    char name[256];
    size_t size;
    mode_t permissions;
    time_t created_time;
    time_t modified_time;
    time_t accessed_time;
    uid_t owner;
    gid_t group;
} FileAttributes;

基本文件操作实现:

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

typedef struct FileOperations {
    int (*open)(const char *path, int flags);
    ssize_t (*read)(int fd, void *buffer, size_t count);
    ssize_t (*write)(int fd, const void *buffer, size_t count);
    int (*close)(int fd);
    off_t (*seek)(int fd, off_t offset, int whence);
} FileOperations;

FileOperations create_file_operations() {
    FileOperations ops = {
        .open = open,
        .read = read,
        .write = write,
        .close = close,
        .seek = lseek
    };
    return ops;
}

typedef struct FileSystem {
    FileOperations ops;
    char root_path[PATH_MAX];
} FileSystem;

FileSystem* create_file_system(const char *root) {
    FileSystem *fs = malloc(sizeof(FileSystem));
    if (!fs) return NULL;

    fs->ops = create_file_operations();
    strncpy(fs->root_path, root, PATH_MAX - 1);
    return fs;
}

int fs_open_file(FileSystem *fs, const char *path, int flags) {
    char full_path[PATH_MAX];
    snprintf(full_path, PATH_MAX, "%s/%s", fs->root_path, path);
    return fs->ops.open(full_path, flags);
}

ssize_t fs_read_file(FileSystem *fs, int fd, void *buffer, size_t count) {
    return fs->ops.read(fd, buffer, count);
}

ssize_t fs_write_file(FileSystem *fs, int fd, const void *buffer, size_t count) {
    return fs->ops.write(fd, buffer, count);
}

int fs_close_file(FileSystem *fs, int fd) {
    return fs->ops.close(fd);
}

4. 文件访问方法

常见访问模式:

  • 顺序访问

    • 解释:数据按照顺序处理,一条记录接一条记录地读取或写入。这是最简单的访问方法,通常用于文本文件和流数据。文件指针在读取数据或写入数据时向前移动。
  • 随机访问

    • 解释:数据可以使用文件定位操作以任何顺序进行访问。这对于需要跳转到文件不同部分的数据库系统和应用程序至关重要。
  • 索引访问

    • 解释:使用索引结构快速定位记录。索引维护键-值对,其中键是搜索词,值是文件位置。

访问模式的实现:

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Record {
    int id;
    char data[100];
} Record;

void sequential_access(const char *filename) {
    FILE *file = fopen(filename, "rb");
    if (!file) return;

    Record record;
    while (fread(&record, sizeof(Record), 1, file) == 1) {
        printf("Record ID: %d, Data: %s\n", record.id, record.data);
    }

    fclose(file);
}

Record* random_access(const char *filename, int record_number) {
    FILE *file = fopen(filename, "rb");
    if (!file) return NULL;

    Record *record = malloc(sizeof(Record));
    if (!record) {
        fclose(file);
        return NULL;
    }

    fseek(file, record_number * sizeof(Record), SEEK_SET);
    if (fread(record, sizeof(Record), 1, file) != 1) {
        free(record);
        record = NULL;
    }

    fclose(file);
    return record;
}

typedef struct IndexEntry {
    int key;
    long position;
} IndexEntry;

typedef struct Index {
    IndexEntry *entries;
    int size;
} Index;

Index* build_index(const char *filename) {
    FILE *file = fopen(filename, "rb");
    if (!file) return NULL;

    Index *index = malloc(sizeof(Index));
    if (!index) {
        fclose(file);
        return NULL;
    }

    // Count records
    fseek(file, 0, SEEK_END);
    long file_size = ftell(file);
    index->size = file_size / sizeof(Record);
    rewind(file);

    // Allocate index entries
    index->entries = malloc(index->size * sizeof(IndexEntry));
    if (!index->entries) {
        free(index);
        fclose(file);
        return NULL;
    }

    // Build index
    Record record;
    for (int i = 0; i < index->size; i++) {
        long pos = ftell(file);
        if (fread(&record, sizeof(Record), 1, file) != 1) break;
        index->entries[i].key = record.id;
        index->entries[i].position = pos;
    }

    fclose(file);
    return index;
}

5.文件类型和结构

常见文件类型:

  • 普通文件

    • 解释:包含用户数据或程序代码。这些文件按字节流(或记录序列)组织。
  • 目录

    • 解释:维护文件系统结构的特殊文件。它们包含将文件名称映射到其元数据和磁盘位置的条目。
  • 特殊文件

    • 解释:表示设备或其他系统资源。这些包括字符特殊文件(用于终端等设备)和块特殊文件(用于磁盘驱动器)。

文件类型处理实现:

c 复制代码
#include <sys/stat.h>

typedef enum FileType {
    FILE_TYPE_REGULAR,
    FILE_TYPE_DIRECTORY,
    FILE_TYPE_CHARACTER_DEVICE,
    FILE_TYPE_BLOCK_DEVICE,
    FILE_TYPE_PIPE,
    FILE_TYPE_SYMLINK,
    FILE_TYPE_SOCKET,
    FILE_TYPE_UNKNOWN
} FileType;

FileType get_file_type(const char *path) {
    struct stat st;
    
    if (lstat(path, &st) < 0) {
        return FILE_TYPE_UNKNOWN;
    }

    if (S_ISREG(st.st_mode)) return FILE_TYPE_REGULAR;
    if (S_ISDIR(st.st_mode)) return FILE_TYPE_DIRECTORY;
    if (S_ISCHR(st.st_mode)) return FILE_TYPE_CHARACTER_DEVICE;
    if (S_ISBLK(st.st_mode)) return FILE_TYPE_BLOCK_DEVICE;
    if (S_ISFIFO(st.st_mode)) return FILE_TYPE_PIPE;
    if (S_ISLNK(st.st_mode)) return FILE_TYPE_SYMLINK;
    if (S_ISSOCK(st.st_mode)) return FILE_TYPE_SOCKET;

    return FILE_TYPE_UNKNOWN;
}

6. 文件组织

基本文件组织方法:

  • 连续组织

    • 解释:文件存储在磁盘上作为连续的块。这提供了卓越的读取性能,但会导致碎片,并使文件增长变得困难。
  • 链接组织

    • 解释:文件存储为块的链接列表。这消除了外部碎片,但不支持高效的随机访问。
  • 索引组织

    • 解释:使用索引块来跟踪文件块。这既支持随机访问也支持文件增长,但维护索引会有成本。

7. 实施细节

以下是文件系统接口的基本实现:

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define MAX_PATH_LENGTH 256
#define MAX_OPEN_FILES 100

typedef struct FileHandle {
    int fd;
    char path[MAX_PATH_LENGTH];
    off_t position;
    int flags;
} FileHandle;

typedef struct FileSystem {
    FileHandle open_files[MAX_OPEN_FILES];
    int file_count;
    char mount_point[MAX_PATH_LENGTH];
} FileSystem;

FileSystem* fs_init(const char *mount_point) {
    FileSystem *fs = malloc(sizeof(FileSystem));
    if (!fs) return NULL;

    memset(fs, 0, sizeof(FileSystem));
    strncpy(fs->mount_point, mount_point, MAX_PATH_LENGTH - 1);
    fs->file_count = 0;

    return fs;
}

int fs_open(FileSystem *fs, const char *path, int flags) {
    if (fs->file_count >= MAX_OPEN_FILES) {
        errno = EMFILE;
        return -1;
    }

    // Find free handle
    int handle = -1;
    for (int i = 0; i < MAX_OPEN_FILES; i++) {
        if (fs->open_files[i].fd == 0) {
            handle = i;
            break;
        }
    }

    if (handle == -1) {
        errno = EMFILE;
        return -1;
    }

    // Open the actual file
    char full_path[MAX_PATH_LENGTH];
    snprintf(full_path, MAX_PATH_LENGTH, "%s/%s", fs->mount_point, path);
    
    int fd = open(full_path, flags);
    if (fd < 0) return -1;

    fs->open_files[handle].fd = fd;
    strncpy(fs->open_files[handle].path, path, MAX_PATH_LENGTH - 1);
    fs->open_files[handle].position = 0;
    fs->open_files[handle].flags = flags;
    fs->file_count++;

    return handle;
}

// Read from file
ssize_t fs_read(FileSystem *fs, int handle, void *buffer, size_t size) {
    if (handle < 0 || handle >= MAX_OPEN_FILES || fs->open_files[handle].fd == 0) {
        errno = EBADF;
        return -1;
    }

    ssize_t bytes = read(fs->open_files[handle].fd, buffer, size);
    if (bytes > 0) {
        fs->open_files[handle].position += bytes;
    }
    return bytes;
}

// Write to file
ssize_t fs_write(FileSystem *fs, int handle, const void *buffer, size_t size) {
    if (handle < 0 || handle >= MAX_OPEN_FILES || fs->open_files[handle].fd == 0) {
        errno = EBADF;
        return -1;
    }

    ssize_t bytes = write(fs->open_files[handle].fd, buffer, size);
    if (bytes > 0) {
        fs->open_files[handle].position += bytes;
    }
    return bytes;
}

int fs_close(FileSystem *fs, int handle) {
    if (handle < 0 || handle >= MAX_OPEN_FILES || fs->open_files[handle].fd == 0) {
        errno = EBADF;
        return -1;
    }

    int result = close(fs->open_files[handle].fd);
    if (result == 0) {
        memset(&fs->open_files[handle], 0, sizeof(FileHandle));
        fs->file_count--;
    }
    return result;
}

8.系统调用和API

常见的文件系统调用:

  • open() / close()

    • 解释:创建或打开一个文件并返回一个文件描述符。close() 会释放与该文件相关的系统资源。
  • read() / write()

    • 解释:在文件和内存之间传输数据。这些操作可以根据实现情况进行缓冲或非缓冲。
  • lseek()

    • 解释:用于随机访问操作,改变文件位置。非顺序文件访问的必备条件。

9. 文件描述符

文件描述符管理实现:

c 复制代码
#include <fcntl.h>
#include <unistd.h>

#define MAX_FD 1024

typedef struct FDTable {
    int fd_array[MAX_FD];
    int count;
} FDTable;

FDTable* fd_table_create() {
    FDTable *table = malloc(sizeof(FDTable));
    if (table) {
        memset(table->fd_array, -1, sizeof(table->fd_array));
        table->count = 0;
    }
    return table;
}

int fd_table_add(FDTable *table, int fd) {
    if (!table || table->count >= MAX_FD) return -1;
    
    for (int i = 0; i < MAX_FD; i++) {
        if (table->fd_array[i] == -1) {
            table->fd_array[i] = fd;
            table->count++;
            return i;
        }
    }
    return -1;
}

int fd_table_remove(FDTable *table, int index) {
    if (!table || index < 0 || index >= MAX_FD) return -1;
    
    if (table->fd_array[index] != -1) {
        int fd = table->fd_array[index];
        table->fd_array[index] = -1;
        table->count--;
        return fd;
    }
    return -1;
}

10.缓冲机制

实现缓冲系统:

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFFER_SIZE 4096

typedef struct Buffer {
    char data[BUFFER_SIZE];
    size_t size;
    size_t position;
    int dirty;
} Buffer;

typedef struct BufferedFile {
    int fd;
    Buffer buffer;
    int mode;  // READ or WRITE
} BufferedFile;

BufferedFile* create_buffered_file(int fd, int mode) {
    BufferedFile *bf = malloc(sizeof(BufferedFile));
    if (!bf) return NULL;

    bf->fd = fd;
    bf->mode = mode;
    bf->buffer.size = 0;
    bf->buffer.position = 0;
    bf->buffer.dirty = 0;

    return bf;
}

int flush_buffer(BufferedFile *bf) {
    if (!bf || !bf->buffer.dirty) return 0;

    ssize_t written = write(bf->fd, bf->buffer.data, bf->buffer.size);
    if (written < 0) return -1;

    bf->buffer.size = 0;
    bf->buffer.position = 0;
    bf->buffer.dirty = 0;
    return 0;
}

ssize_t buffered_read(BufferedFile *bf, void *data, size_t count) {
    if (!bf || bf->mode != O_RDONLY) return -1;

    size_t total_read = 0;
    char *dest = (char*)data;

    while (total_read < count) {
        // If buffer is empty, fill it
        if (bf->buffer.position >= bf->buffer.size) {
            bf->buffer.size = read(bf->fd, bf->buffer.data, BUFFER_SIZE);
            if (bf->buffer.size <= 0) break;
            bf->buffer.position = 0;
        }

        // Copy from buffer to destination
        size_t available = bf->buffer.size - bf->buffer.position;
        size_t to_copy = (count - total_read) < available ? 
                        (count - total_read) : available;
        
        memcpy(dest + total_read, 
               bf->buffer.data + bf->buffer.position, 
               to_copy);
        
        bf->buffer.position += to_copy;
        total_read += to_copy;
    }

    return total_read;
}

ssize_t buffered_write(BufferedFile *bf, const void *data, size_t count) {
    if (!bf || bf->mode != O_WRONLY) return -1;

    size_t total_written = 0;
    const char *src = (const char*)data;

    while (total_written < count) {
        // If buffer is full, flush it
        if (bf->buffer.size >= BUFFER_SIZE) {
            if (flush_buffer(bf) < 0) return -1;
        }

        // Copy from source to buffer
        size_t available = BUFFER_SIZE - bf->buffer.size;
        size_t to_copy = (count - total_written) < available ? 
                        (count - total_written) : available;
        
        memcpy(bf->buffer.data + bf->buffer.size, 
               src + total_written, 
               to_copy);
        
        bf->buffer.size += to_copy;
        bf->buffer.dirty = 1;
        total_written += to_copy;
    }

    return total_written;
}

11.错误处理

全面的错误处理实现:

c 复制代码
#include <errno.h>
#include <string.h>

typedef enum FSError {
    FS_ERROR_NONE = 0,
    FS_ERROR_IO,
    FS_ERROR_NOMEM,
    FS_ERROR_NOSPACE,
    FS_ERROR_NOTFOUND,
    FS_ERROR_PERMISSION,
    FS_ERROR_INVALID
} FSError;

typedef struct FSErrorInfo {
    FSError code;
    char message[256];
    int system_errno;
} FSErrorInfo;

typedef struct FSContext {
    FSErrorInfo last_error;
} FSContext;

void fs_set_error(FSContext *ctx, FSError code, const char *msg) {
    if (!ctx) return;

    ctx->last_error.code = code;
    ctx->last_error.system_errno = errno;
    strncpy(ctx->last_error.message, msg, 255);
    ctx->last_error.message[255] = '\0';
}

const char* fs_error_string(FSContext *ctx) {
    if (!ctx) return "Invalid context";
    
    static char error_buffer[512];
    snprintf(error_buffer, sizeof(error_buffer),
             "Error: %s (code: %d, errno: %d - %s)",
             ctx->last_error.message,
             ctx->last_error.code,
             ctx->last_error.system_errno,
             strerror(ctx->last_error.system_errno));
    
    return error_buffer;
}

12.最佳实践

以下是一个简单文件系统接口的完整示例:

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>

#define FS_MAX_PATH 4096
#define FS_MAX_FILES 1000

typedef struct FSFile {
    int fd;
    char path[FS_MAX_PATH];
    mode_t mode;
    off_t size;
    time_t atime;
    time_t mtime;
    time_t ctime;
} FSFile;

typedef struct FileSystem {
    char root[FS_MAX_PATH];
    FSFile files[FS_MAX_FILES];
    int file_count;
    FSContext error_ctx;
} FileSystem;

FileSystem* fs_create(const char *root) {
    FileSystem *fs = malloc(sizeof(FileSystem));
    if (!fs) return NULL;

    memset(fs, 0, sizeof(FileSystem));
    strncpy(fs->root, root, FS_MAX_PATH - 1);

    // Create root directory if it doesn't exist
    if (mkdir(root, 0755) < 0 && errno != EEXIST) {
        fs_set_error(&fs->error_ctx, FS_ERROR_IO, "Failed to create root directory");
        free(fs);
        return NULL;
    }

    return fs;
}

int fs_create_file(FileSystem *fs, const char *path, mode_t mode) {
    if (!fs || !path) {
        fs_set_error(&fs->error_ctx, FS_ERROR_INVALID, "Invalid parameters");
        return -1;
    }

    char full_path[FS_MAX_PATH];
    snprintf(full_path, FS_MAX_PATH, "%s/%s", fs->root, path);

    int fd = open(full_path, O_CREAT | O_RDWR, mode);
    if (fd < 0) {
        fs_set_error(&fs->error_ctx, FS_ERROR_IO, "Failed to create file");
        return -1;
    }

    // Add to file table
    if (fs->file_count >= FS_MAX_FILES) {
        close(fd);
        fs_set_error(&fs->error_ctx, FS_ERROR_NOSPACE, "File table full");
        return -1;
    }

    FSFile *file = &fs->files[fs->file_count++];
    file->fd = fd;
    strncpy(file->path, path, FS_MAX_PATH - 1);
    file->mode = mode;
    
    struct stat st;
    if (fstat(fd, &st) == 0) {
        file->size = st.st_size;
        file->atime = st.st_atime;
        file->mtime = st.st_mtime;
        file->ctime = st.st_ctime;
    }

    return fs->file_count - 1;
}

// Example usage
int main() {
    FileSystem *fs = fs_create("/tmp/testfs");
    if (!fs) {
        fprintf(stderr, "Failed to create file system\n");
        return 1;
    }

    int file_index = fs_create_file(fs, "test.txt", 0644);
    if (file_index < 0) {
        fprintf(stderr, "Error: %s\n", fs_error_string(&fs->error_ctx));
        return 1;
    }

    printf("Created file at index: %d\n", file_index);
    return 0;
}

13.总结

文件系统基础构成了理解现代操作系统如何管理数据存储和检索的理论基础。在这里涉及的概念对于构建更复杂的文件系统实现,以及理解如日志记录和分布式文件系统等高级主题至关重要,后续的几天将对这些主题进行介绍。

14.参考资料和进一步阅读

  • "Operating System Concepts" by Silberschatz, Galvin, and Gagne
  • "The Design and Implementation of the 4.4BSD Operating System"
  • "Understanding the Linux Kernel" by Daniel P. Bovet and Marco Cesati
  • POSIX File System specifications
  • "Advanced Programming in the UNIX Environment" by W. Richard Stevens
相关推荐
..过云雨6 小时前
17-2.【Linux系统编程】线程同步详解 - 条件变量的理解及应用
linux·c++·人工智能·后端
南山乐只6 小时前
【Spring AI 开发指南】ChatClient 基础、原理与实战案例
人工智能·后端·spring ai
努力的小雨8 小时前
从“Agent 元年”到 AI IDE 元年——2025 我与 Vibe Coding 的那些事儿
后端·程序员
源码获取_wx:Fegn08958 小时前
基于springboot + vue小区人脸识别门禁系统
java·开发语言·vue.js·spring boot·后端·spring
wuxuanok8 小时前
Go——Swagger API文档访问500
开发语言·后端·golang
用户21411832636029 小时前
白嫖Google Antigravity!Claude Opus 4.5免费用,告别token焦虑
后端
爬山算法9 小时前
Hibernate(15)Hibernate中如何定义一个实体的主键?
java·后端·hibernate
用户268516121075610 小时前
常见的 Git 分支命名策略和实践
后端
程序员小假10 小时前
我们来说一下 MySQL 的慢查询日志
java·后端