MST

星途 面试题库

面试题:C语言Linux内存映射虚拟内存跨进程共享及一致性

在C语言开发中,利用Linux内存映射实现虚拟内存跨进程共享。请详细描述如何保证多个进程对共享虚拟内存数据的一致性,讨论可能遇到的同步问题以及如何通过C语言结合Linux系统特性解决这些问题。
30.5万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

保证共享虚拟内存数据一致性的方法

  1. 信号量(Semaphore)
    • 原理:信号量是一个计数器,用于控制对共享资源的访问。在共享内存场景下,它可以控制多个进程对共享内存区域的访问顺序,防止数据竞争。
    • 使用方法(C语言示例)
#include <semaphore.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define SHM_SIZE 1024

int main() {
    int fd = shm_open("/shared_memory", O_CREAT | O_RDWR, 0666);
    ftruncate(fd, SHM_SIZE);
    char *shm_ptr = (char *)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    sem_t *sem = sem_open("/my_semaphore", O_CREAT, 0666, 1);

    // 进程1获取信号量
    sem_wait(sem);
    // 访问共享内存
    sprintf(shm_ptr, "Hello from process 1");
    // 释放信号量
    sem_post(sem);

    sem_close(sem);
    munmap(shm_ptr, SHM_SIZE);
    close(fd);
    shm_unlink("/shared_memory");
    sem_unlink("/my_semaphore");
    return 0;
}
  1. 互斥锁(Mutex)
    • 原理:互斥锁是一种特殊的二元信号量(值为0或1),用于保证在同一时刻只有一个进程能够访问共享资源。
    • 使用方法(C语言示例)
#include <pthread.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define SHM_SIZE 1024

// 定义共享数据结构
typedef struct {
    pthread_mutex_t mutex;
    char data[SHM_SIZE];
} SharedData;

int main() {
    int fd = shm_open("/shared_memory", O_CREAT | O_RDWR, 0666);
    ftruncate(fd, sizeof(SharedData));
    SharedData *shared = (SharedData *)mmap(0, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    // 初始化互斥锁
    pthread_mutex_init(&shared->mutex, NULL);

    // 进程1获取互斥锁
    pthread_mutex_lock(&shared->mutex);
    // 访问共享内存
    sprintf(shared->data, "Hello from process 1");
    // 释放互斥锁
    pthread_mutex_unlock(&shared->mutex);

    pthread_mutex_destroy(&shared->mutex);
    munmap(shared, sizeof(SharedData));
    close(fd);
    shm_unlink("/shared_memory");
    return 0;
}
  1. 读写锁(Read - Write Lock)
    • 原理:读写锁允许多个进程同时进行读操作,但只允许一个进程进行写操作。当有进程在写时,其他进程的读写操作都被阻塞。
    • 使用方法(C语言示例)
#include <pthread.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define SHM_SIZE 1024

// 定义共享数据结构
typedef struct {
    pthread_rwlock_t rwlock;
    char data[SHM_SIZE];
} SharedData;

int main() {
    int fd = shm_open("/shared_memory", O_CREAT | O_RDWR, 0666);
    ftruncate(fd, sizeof(SharedData));
    SharedData *shared = (SharedData *)mmap(0, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    // 初始化读写锁
    pthread_rwlock_init(&shared->rwlock, NULL);

    // 进程1获取写锁
    pthread_rwlock_wrlock(&shared->rwlock);
    // 写共享内存
    sprintf(shared->data, "Hello from process 1");
    // 释放写锁
    pthread_rwlock_unlock(&shared->rwlock);

    pthread_rwlock_destroy(&shared->rwlock);
    munmap(shared, sizeof(SharedData));
    close(fd);
    shm_unlink("/shared_memory");
    return 0;
}

可能遇到的同步问题

  1. 数据竞争(Data Race):多个进程同时对共享内存进行读写操作,导致数据不一致。例如,一个进程正在读取共享内存中的数据,而另一个进程同时对该数据进行修改,就可能导致读取到不完整或错误的数据。
  2. 死锁(Deadlock):当两个或多个进程互相等待对方释放资源时,就会发生死锁。比如,进程A持有资源1并等待资源2,而进程B持有资源2并等待资源1,此时两个进程都无法继续执行。
  3. 饥饿(Starvation):某个进程长时间无法获取到共享资源的访问权,导致其任务无法执行。例如,当有大量读操作频繁进行时,写操作可能因为读写锁的机制而长时间得不到执行。

通过C语言结合Linux系统特性解决同步问题

  1. 使用Linux系统调用:如semgetsemop等函数实现信号量机制;pthread_mutex_lockpthread_mutex_unlock等函数实现互斥锁;pthread_rwlock_wrlockpthread_rwlock_rdlock等函数实现读写锁。
  2. 利用文件锁:可以使用fcntl函数实现文件锁机制,通过对共享内存对应的文件加锁,来控制对共享内存的访问。例如:
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define SHM_SIZE 1024

int main() {
    int fd = shm_open("/shared_memory", O_CREAT | O_RDWR, 0666);
    ftruncate(fd, SHM_SIZE);
    char *shm_ptr = (char *)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    struct flock lock;
    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;

    // 获取文件锁
    fcntl(fd, F_SETLKW, &lock);
    // 访问共享内存
    sprintf(shm_ptr, "Hello from process 1");
    // 释放文件锁
    lock.l_type = F_UNLCK;
    fcntl(fd, F_SETLK, &lock);

    munmap(shm_ptr, SHM_SIZE);
    close(fd);
    shm_unlink("/shared_memory");
    return 0;
}
  1. 原子操作:对于简单的变量操作,可以使用原子操作函数(如__sync_fetch_and_add等)来避免数据竞争。原子操作是不可分割的操作,在执行过程中不会被其他进程打断。例如:
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

#define SHM_SIZE sizeof(int32_t)

int main() {
    int fd = shm_open("/shared_memory", O_CREAT | O_RDWR, 0666);
    ftruncate(fd, SHM_SIZE);
    int32_t *shared_value = (int32_t *)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    *shared_value = 0;

    // 原子增加操作
    __sync_fetch_and_add(shared_value, 1);

    munmap(shared_value, SHM_SIZE);
    close(fd);
    shm_unlink("/shared_memory");
    return 0;
}