MST

星途 面试题库

面试题:C语言内存映射错误处理机制深入

假设在一个多线程程序中使用C语言进行Linux内存映射,在映射过程中出现了`ENOMEM`错误(表示内存不足)。请描述这种情况下可能存在的竞争条件,并阐述如何设计一个合理的错误处理策略,确保程序的健壮性和数据一致性。
39.7万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

可能存在的竞争条件

  1. 内存分配竞争:多个线程同时尝试进行内存映射操作,系统的可用内存资源有限。当接近系统内存上限时,不同线程可能同时检测到还有足够内存用于映射,但在实际执行映射时却出现 ENOMEM 错误。例如,线程 A 和线程 B 都检测到剩余内存足够映射 100MB,然后同时发起映射请求,最终导致 ENOMEM
  2. 映射表竞争:Linux 内核维护内存映射表,多个线程同时进行映射操作可能导致对映射表的竞争。如果一个线程正在更新映射表,而另一个线程也尝试进行映射操作,可能会干扰映射表的一致性,进而导致内存映射失败并出现 ENOMEM 错误。

错误处理策略

  1. 全局锁机制
    • 使用互斥锁(pthread_mutex_t)对内存映射操作进行保护。在进行内存映射前,每个线程先获取互斥锁,操作完成后再释放。这样可以避免多个线程同时进行内存映射操作,减少竞争条件。
    • 示例代码:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

pthread_mutex_t mmap_mutex;
#define MAP_SIZE 1024 * 1024

void* map_memory() {
    pthread_mutex_lock(&mmap_mutex);
    int fd = open("testfile", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    if (fd == -1) {
        perror("open");
        pthread_mutex_unlock(&mmap_mutex);
        return NULL;
    }
    if (lseek(fd, MAP_SIZE - 1, SEEK_SET) == -1) {
        perror("lseek");
        close(fd);
        pthread_mutex_unlock(&mmap_mutex);
        return NULL;
    }
    if (write(fd, "", 1) != 1) {
        perror("write");
        close(fd);
        pthread_mutex_unlock(&mmap_mutex);
        return NULL;
    }
    void* map_start = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (map_start == MAP_FAILED) {
        perror("mmap");
        close(fd);
        pthread_mutex_unlock(&mmap_mutex);
        return NULL;
    }
    close(fd);
    pthread_mutex_unlock(&mmap_mutex);
    return map_start;
}

void* thread_function(void* arg) {
    void* mapped_mem = map_memory();
    if (mapped_mem) {
        // 使用映射内存
        munmap(mapped_mem, MAP_SIZE);
    }
    return NULL;
}

int main() {
    pthread_t thread1, thread2;
    pthread_mutex_init(&mmap_mutex, NULL);
    if (pthread_create(&thread1, NULL, thread_function, NULL) != 0) {
        perror("pthread_create");
        return 1;
    }
    if (pthread_create(&thread2, NULL, thread_function, NULL) != 0) {
        perror("pthread_create");
        return 1;
    }
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    pthread_mutex_destroy(&mmap_mutex);
    return 0;
}
  1. 错误重试
    • 当出现 ENOMEM 错误时,线程可以等待一段时间后重试内存映射操作。但要注意设置合理的重试次数和等待时间,避免无限循环重试占用过多资源。
    • 示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

#define MAP_SIZE 1024 * 1024
#define MAX_RETRIES 3
#define WAIT_TIME 1000000 // 1 秒

void* map_memory() {
    int retries = 0;
    while (retries < MAX_RETRIES) {
        int fd = open("testfile", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
        if (fd == -1) {
            perror("open");
            return NULL;
        }
        if (lseek(fd, MAP_SIZE - 1, SEEK_SET) == -1) {
            perror("lseek");
            close(fd);
            return NULL;
        }
        if (write(fd, "", 1) != 1) {
            perror("write");
            close(fd);
            return NULL;
        }
        void* map_start = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (map_start != MAP_FAILED) {
            close(fd);
            return map_start;
        }
        if (errno == ENOMEM) {
            close(fd);
            usleep(WAIT_TIME);
            retries++;
        } else {
            perror("mmap");
            close(fd);
            return NULL;
        }
    }
    perror("mmap after retries");
    return NULL;
}

int main() {
    void* mapped_mem = map_memory();
    if (mapped_mem) {
        // 使用映射内存
        munmap(mapped_mem, MAP_SIZE);
    }
    return 0;
}
  1. 资源预分配与监控
    • 在程序启动时,预先分配一定量的内存资源供多线程使用。可以通过系统调用(如 brkmmap 一次分配较大内存块)实现。
    • 同时,使用一个监控线程定期检查系统内存使用情况。如果发现内存不足,通知其他线程减少内存使用或暂停映射操作。
    • 示例代码(仅展示内存预分配部分):
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

#define PREALLOC_SIZE 1024 * 1024 * 10 // 10MB

int main() {
    void* prealloc_mem = mmap(0, PREALLOC_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (prealloc_mem == MAP_FAILED) {
        perror("mmap for preallocation");
        return 1;
    }
    // 后续多线程使用预分配内存,这里省略多线程代码
    if (munmap(prealloc_mem, PREALLOC_SIZE) == -1) {
        perror("munmap preallocation");
        return 1;
    }
    return 0;
}

通过以上策略,可以有效减少竞争条件,并在出现 ENOMEM 错误时保证程序的健壮性和数据一致性。