MST

星途 面试题库

面试题:C语言共享内存访问控制中的同步机制

在多个进程通过C语言访问Linux共享内存时,可能会出现数据竞争问题。请详细阐述至少两种常用的同步机制(如信号量、互斥锁等),并以代码示例说明如何在共享内存访问场景中使用信号量来保证数据一致性。
33.1万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

常用同步机制

  1. 互斥锁(Mutex)
    • 互斥锁是一种特殊的二元信号量,取值只有0和1。它的作用是保证在同一时刻只有一个进程能够访问共享资源。当一个进程获取到互斥锁(将其值设为0),其他进程就不能再获取,直到该进程释放互斥锁(将其值设为1)。
    • 优点:简单高效,适用于大多数需要保护共享资源的场景。
    • 缺点:如果一个进程在持有互斥锁时发生异常终止,可能会导致其他进程永远无法获取锁,即死锁情况。
  2. 信号量(Semaphore)
    • 信号量可以是一个任意的非负整数。它通过计数器来控制对共享资源的访问。当一个进程想要访问共享资源时,它需要先获取信号量(将计数器减1),如果计数器的值为0,则该进程被阻塞,直到其他进程释放信号量(将计数器加1)。
    • 优点:适用于需要控制同时访问共享资源的进程数量的场景,比互斥锁更灵活。
    • 缺点:使用相对复杂,需要正确地初始化和操作信号量的值,否则容易出现死锁或资源泄漏。
  3. 条件变量(Condition Variable)
    • 条件变量通常与互斥锁一起使用。它允许进程在某个条件满足时被唤醒。例如,当共享内存中的数据达到某个特定状态时,等待在条件变量上的进程可以被唤醒。
    • 优点:能够实现更复杂的同步逻辑,避免不必要的忙等待。
    • 缺点:需要与互斥锁配合使用,编程复杂度较高,容易出现死锁。

使用信号量保证共享内存数据一致性的代码示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <semaphore.h>
#include <fcntl.h>
#include <unistd.h>

#define SHM_SIZE 1024

int main() {
    key_t key = ftok(".", 'a');
    if (key == -1) {
        perror("ftok");
        return 1;
    }

    int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget");
        return 1;
    }

    char *shmaddr = (char *)shmat(shmid, NULL, 0);
    if (shmaddr == (void *)-1) {
        perror("shmat");
        return 1;
    }

    sem_t *sem_id = sem_open("/mysem", O_CREAT, 0666, 1);
    if (sem_id == SEM_FAILED) {
        perror("sem_open");
        return 1;
    }

    if (fork() == 0) {
        // 子进程
        sem_wait(sem_id);
        // 访问共享内存
        sprintf(shmaddr, "Hello from child");
        printf("Child process wrote to shared memory: %s\n", shmaddr);
        sem_post(sem_id);
    } else {
        // 父进程
        sem_wait(sem_id);
        // 访问共享内存
        sprintf(shmaddr, "Hello from parent");
        printf("Parent process wrote to shared memory: %s\n", shmaddr);
        sem_post(sem_id);
        wait(NULL);
    }

    sem_close(sem_id);
    sem_unlink("/mysem");
    shmdt(shmaddr);
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}

在上述代码中:

  1. ftok 用于生成一个唯一的键值,用于共享内存和信号量的标识。
  2. shmget 创建共享内存段。
  3. shmat 将共享内存段连接到进程的地址空间。
  4. sem_open 创建并初始化一个信号量,初始值为1,表示允许一个进程访问共享资源。
  5. 在父子进程中,通过 sem_wait 获取信号量,访问共享内存,然后通过 sem_post 释放信号量。
  6. 最后,关闭并删除信号量,分离共享内存段并删除共享内存。