MST

星途 面试题库

面试题:C语言共享内存的数据同步问题及解决方法

在多进程通过C语言访问共享内存时,可能会出现数据同步问题。请详细描述几种常见的数据同步机制(如信号量、互斥锁等)在C语言共享内存场景下的原理及使用方法,并说明它们各自的优缺点。
38.8万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

信号量

  1. 原理:信号量是一个整型变量,通过计数器来控制对共享资源的访问。当计数器的值大于0时,表示有可用资源,进程可以获取信号量(计数器减1)来访问共享资源;当计数器为0时,表示资源已被占用,进程需要等待。
  2. 使用方法(以POSIX信号量为例)
#include <semaphore.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

#define SHM_SIZE 1024

int main() {
    // 创建共享内存对象
    int shm_fd = shm_open("/shared_memory", O_CREAT | O_RDWR, 0666);
    ftruncate(shm_fd, SHM_SIZE);
    char *shm_ptr = (char *)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);

    // 创建信号量
    sem_t *sem = sem_open("/my_semaphore", O_CREAT, 0666, 1);

    // 子进程
    if (fork() == 0) {
        sem_wait(sem); // 获取信号量
        // 访问共享内存
        printf("Child process accessing shared memory: %s\n", shm_ptr);
        sem_post(sem); // 释放信号量
        _exit(0);
    } else {
        sem_wait(sem);
        // 访问共享内存
        sprintf(shm_ptr, "Hello from parent");
        sem_post(sem);
        wait(NULL);
    }

    sem_close(sem);
    sem_unlink("/my_semaphore");
    munmap(shm_ptr, SHM_SIZE);
    close(shm_fd);
    shm_unlink("/shared_memory");
    return 0;
}
  1. 优点:可以允许多个进程同时访问共享资源(通过设置信号量的初始值),适用于复杂的同步场景。
  2. 缺点:实现相对复杂,可能会出现死锁等问题,如果使用不当,调试困难。

互斥锁

  1. 原理:互斥锁是一种特殊的二元信号量(初始值为1),它的值只能是0或1。当一个进程获取了互斥锁(值变为0),其他进程就不能再获取,直到该进程释放互斥锁(值变为1)。这保证了同一时间只有一个进程能够访问共享资源。
  2. 使用方法(以POSIX互斥锁为例)
#include <pthread.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

#define SHM_SIZE 1024

int main() {
    // 创建共享内存对象
    int shm_fd = shm_open("/shared_memory", O_CREAT | O_RDWR, 0666);
    ftruncate(shm_fd, SHM_SIZE);
    char *shm_ptr = (char *)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);

    // 创建互斥锁
    pthread_mutex_t *mutex = (pthread_mutex_t *)mmap(0, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    pthread_mutex_init(mutex, NULL);

    // 子进程
    if (fork() == 0) {
        pthread_mutex_lock(mutex); // 加锁
        // 访问共享内存
        printf("Child process accessing shared memory: %s\n", shm_ptr);
        pthread_mutex_unlock(mutex); // 解锁
        _exit(0);
    } else {
        pthread_mutex_lock(mutex);
        // 访问共享内存
        sprintf(shm_ptr, "Hello from parent");
        pthread_mutex_unlock(mutex);
        wait(NULL);
    }

    pthread_mutex_destroy(mutex);
    munmap(mutex, sizeof(pthread_mutex_t));
    munmap(shm_ptr, SHM_SIZE);
    close(shm_fd);
    shm_unlink("/shared_memory");
    return 0;
}
  1. 优点:简单直观,易于理解和使用,能有效避免多个进程同时访问共享资源导致的数据不一致问题。
  2. 缺点:同一时间只允许一个进程访问共享资源,可能会导致其他进程等待时间过长,影响系统性能,尤其在并发度较高的场景下。

条件变量

  1. 原理:条件变量通常与互斥锁一起使用。它允许进程在某个条件满足时被唤醒。进程首先获取互斥锁,然后检查条件是否满足,如果不满足,进程将在条件变量上等待,并释放互斥锁。当另一个进程改变了条件并通知条件变量时,等待的进程被唤醒,重新获取互斥锁,然后检查条件。
  2. 使用方法(以POSIX条件变量为例)
#include <pthread.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

#define SHM_SIZE 1024

int main() {
    // 创建共享内存对象
    int shm_fd = shm_open("/shared_memory", O_CREAT | O_RDWR, 0666);
    ftruncate(shm_fd, SHM_SIZE);
    char *shm_ptr = (char *)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);

    // 创建互斥锁和条件变量
    pthread_mutex_t *mutex = (pthread_mutex_t *)mmap(0, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    pthread_cond_t *cond = (pthread_cond_t *)mmap(0, sizeof(pthread_cond_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    pthread_mutex_init(mutex, NULL);
    pthread_cond_init(cond, NULL);

    // 子进程
    if (fork() == 0) {
        pthread_mutex_lock(mutex);
        // 等待条件变量
        pthread_cond_wait(cond, mutex);
        // 访问共享内存
        printf("Child process accessing shared memory: %s\n", shm_ptr);
        pthread_mutex_unlock(mutex);
        _exit(0);
    } else {
        pthread_mutex_lock(mutex);
        // 设置共享内存
        sprintf(shm_ptr, "Hello from parent");
        // 通知条件变量
        pthread_cond_signal(cond);
        pthread_mutex_unlock(mutex);
        wait(NULL);
    }

    pthread_cond_destroy(cond);
    pthread_mutex_destroy(mutex);
    munmap(cond, sizeof(pthread_cond_t));
    munmap(mutex, sizeof(pthread_mutex_t));
    munmap(shm_ptr, SHM_SIZE);
    close(shm_fd);
    shm_unlink("/shared_memory");
    return 0;
}
  1. 优点:提供了更灵活的同步机制,适用于需要等待某个条件满足才能继续执行的场景。
  2. 缺点:使用相对复杂,需要与互斥锁配合使用,并且如果条件变量的通知和等待处理不当,可能会导致死锁或竞态条件。