MST

星途 面试题库

面试题:C语言文件输入输出的底层原理与高级应用

阐述C语言标准库文件输入输出函数(如`fopen`、`fread`、`fwrite`等)在操作系统层面的实现机制,包括缓冲区管理、系统调用等。并基于此原理,设计一个自定义的文件输入输出函数库,要求具备更灵活的缓冲区控制和异步I/O能力,简要说明设计思路和关键代码实现。
44.0万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

C语言标准库文件输入输出函数实现机制

  1. 缓冲区管理
    • C标准库的文件I/O函数(如fopenfreadfwrite等)通常使用缓冲区来减少系统调用的次数。分为全缓冲、行缓冲和无缓冲。
      • 全缓冲:当缓冲区被填满时,才会执行实际的I/O操作。对于磁盘文件通常是全缓冲,标准库会为每个打开的文件分配一个缓冲区(大小一般为4096字节或8192字节等)。例如fwrite函数向缓冲区写入数据,当缓冲区满时,通过系统调用将缓冲区数据写入文件。
      • 行缓冲:当遇到换行符\n或缓冲区满时,执行I/O操作。常用于终端设备,如stdout
      • 无缓冲:数据直接被写入或读出,不经过缓冲区,stderr通常是无缓冲的。
  2. 系统调用
    • 在Linux系统中,底层系统调用主要是openreadwriteclose等。例如fopen函数在内部调用open系统调用来打开文件,获取文件描述符。freadfwrite函数在缓冲区操作后,当需要真正读写数据时,会调用readwrite系统调用。fclose函数则调用close系统调用来关闭文件。

自定义文件输入输出函数库设计思路

  1. 更灵活的缓冲区控制
    • 允许用户在打开文件时指定缓冲区大小,而不是使用固定的默认值。
    • 提供函数来手动刷新缓冲区,而不仅仅依赖于缓冲区满或特定条件触发的自动刷新。
  2. 异步I/O能力
    • 使用操作系统提供的异步I/O机制,如Linux的aio_readaio_write函数。
    • 引入回调机制,当异步I/O操作完成时,调用用户指定的回调函数。

关键代码实现

  1. 自定义文件结构体
typedef struct {
    int fd; // 文件描述符
    char *buffer; // 缓冲区
    size_t buffer_size; // 缓冲区大小
    size_t buffer_pos; // 当前缓冲区位置
    int is_async; // 是否异步
    void (*callback)(void *); // 异步回调函数
    void *callback_arg; // 回调函数参数
} my_file;
  1. 自定义打开文件函数
my_file* my_fopen(const char *path, const char *mode, size_t buffer_size, int is_async, void (*callback)(void *), void *callback_arg) {
    int fd = open(path, mode == "r"? O_RDONLY : mode == "w"? O_WRONLY | O_CREAT | O_TRUNC : -1, 0666);
    if (fd == -1) {
        return NULL;
    }
    my_file *file = (my_file*)malloc(sizeof(my_file));
    file->fd = fd;
    file->buffer = (char*)malloc(buffer_size);
    file->buffer_size = buffer_size;
    file->buffer_pos = 0;
    file->is_async = is_async;
    file->callback = callback;
    file->callback_arg = callback_arg;
    return file;
}
  1. 自定义写入函数(同步部分)
size_t my_fwrite(const void *ptr, size_t size, size_t nmemb, my_file *file) {
    size_t total_size = size * nmemb;
    size_t remaining = total_size;
    while (remaining > 0) {
        size_t to_write = remaining > (file->buffer_size - file->buffer_pos)? (file->buffer_size - file->buffer_pos) : remaining;
        memcpy(file->buffer + file->buffer_pos, ptr, to_write);
        file->buffer_pos += to_write;
        ptr += to_write;
        remaining -= to_write;
        if (file->buffer_pos == file->buffer_size) {
            if (write(file->fd, file->buffer, file->buffer_size) == -1) {
                // 错误处理
                return 0;
            }
            file->buffer_pos = 0;
        }
    }
    return total_size;
}
  1. 自定义写入函数(异步部分)
void my_fwrite_async(const void *ptr, size_t size, size_t nmemb, my_file *file) {
    struct aiocb aiocbp;
    memset(&aiocbp, 0, sizeof(struct aiocb));
    aiocbp.aio_fildes = file->fd;
    aiocbp.aio_buf = (void *)ptr;
    aiocbp.aio_nbytes = size * nmemb;
    aiocbp.aio_offset = 0;
    aiocbp.aio_sigevent.sigev_notify = SIGEV_THREAD;
    aiocbp.aio_sigevent.sigev_notify_function = file->callback;
    aiocbp.aio_sigevent.sigev_value.sival_ptr = file->callback_arg;
    if (aio_write(&aiocbp) == -1) {
        // 错误处理
    }
}
  1. 自定义关闭文件函数
int my_fclose(my_file *file) {
    if (file->buffer_pos > 0) {
        if (write(file->fd, file->buffer, file->buffer_pos) == -1) {
            // 错误处理
            free(file->buffer);
            close(file->fd);
            free(file);
            return -1;
        }
    }
    free(file->buffer);
    close(file->fd);
    free(file);
    return 0;
}

以上代码仅为关键部分示例,实际使用中还需要完善错误处理等细节。