MST

星途 面试题库

面试题:C语言内存映射性能优化 - 多进程访问

假设有一个大文件,需通过多个进程并发访问该文件的不同区域进行数据处理,使用C语言在Linux下利用内存映射技术实现此功能,并说明如何避免进程间的数据竞争以及优化性能的方法。
29.0万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

1. 使用内存映射技术实现多进程并发访问文件不同区域

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>

#define FILE_SIZE 1024 * 1024 // 假设文件大小为1MB
#define PROCESS_NUM 4

// 用于进程间同步的信号量
sem_t *semaphore;

void handle_error(const char *msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}

void process_task(int start, int length) {
    int fd = open("large_file", O_RDWR);
    if (fd == -1) {
        handle_error("open");
    }

    // 映射文件到内存
    char *file_map = (char *)mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (file_map == MAP_FAILED) {
        handle_error("mmap");
    }

    // 等待信号量,避免数据竞争
    if (sem_wait(semaphore) == -1) {
        handle_error("sem_wait");
    }

    // 处理文件特定区域的数据
    for (int i = start; i < start + length; i++) {
        file_map[i] = file_map[i] + 1; // 简单的数据处理示例
    }

    // 释放信号量
    if (sem_post(semaphore) == -1) {
        handle_error("sem_post");
    }

    // 解除映射
    if (munmap(file_map, FILE_SIZE) == -1) {
        handle_error("munmap");
    }
    close(fd);
}

int main() {
    // 创建大文件并初始化内容
    int fd = open("large_file", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
    if (fd == -1) {
        handle_error("open");
    }
    if (lseek(fd, FILE_SIZE - 1, SEEK_SET) == -1) {
        handle_error("lseek");
    }
    if (write(fd, "", 1) != 1) {
        handle_error("write");
    }
    close(fd);

    // 创建信号量用于同步
    semaphore = sem_open("/my_semaphore", O_CREAT, 0644, 1);
    if (semaphore == SEM_FAILED) {
        handle_error("sem_open");
    }

    // 创建子进程并发处理文件不同区域
    for (int i = 0; i < PROCESS_NUM; i++) {
        pid_t pid = fork();
        if (pid == -1) {
            handle_error("fork");
        } else if (pid == 0) {
            int start = i * (FILE_SIZE / PROCESS_NUM);
            int length = FILE_SIZE / PROCESS_NUM;
            process_task(start, length);
            exit(EXIT_SUCCESS);
        }
    }

    // 等待所有子进程结束
    for (int i = 0; i < PROCESS_NUM; i++) {
        wait(NULL);
    }

    // 删除信号量
    if (sem_close(semaphore) == -1) {
        handle_error("sem_close");
    }
    if (sem_unlink("/my_semaphore") == -1) {
        handle_error("sem_unlink");
    }

    return 0;
}

2. 避免进程间数据竞争的方法

  • 信号量(Semaphore):在上述代码中,使用semaphore来控制对共享内存区域的访问。每个进程在访问共享内存前,先通过sem_wait获取信号量,处理完数据后通过sem_post释放信号量。这样同一时间只有一个进程可以访问共享内存,避免了数据竞争。
  • 互斥锁(Mutex):互斥锁本质上是一种特殊的二值信号量(取值为0或1)。可以使用pthread_mutex_t类型的互斥锁,在进程间通过共享内存的方式来使用。在访问共享内存前加锁(pthread_mutex_lock),访问结束后解锁(pthread_mutex_unlock)。

3. 优化性能的方法

  • 减少锁的粒度:如果可能,将共享内存划分成更小的部分,每个进程只对自己需要的部分加锁。这样可以允许更多的进程同时访问不同部分的共享内存,提高并发性能。
  • 预读取:在处理数据前,提前将需要的数据从磁盘读取到内存中,减少I/O等待时间。可以使用posix_fadvise函数来向系统建议文件的访问模式,例如POSIX_FADV_SEQUENTIAL表示顺序访问,POSIX_FADV_RANDOM表示随机访问,帮助系统优化I/O。
  • 多线程处理:除了多进程,还可以结合多线程。在每个进程内部使用多线程来进一步提高并发度。但需要注意线程间同样存在数据竞争问题,需要使用线程同步机制(如互斥锁、条件变量等)。
  • 异步I/O:使用异步I/O函数(如aio_readaio_write),在进行I/O操作时不会阻塞主线程,从而提高程序的整体性能。