MST

星途 面试题库

面试题:C语言中Linux共享内存同步机制之信号量

在Linux环境下使用C语言进行共享内存编程时,信号量常被用于同步机制。请描述如何使用信号量来实现对共享内存的互斥访问,并给出简单的代码示例,说明信号量的初始化、获取和释放操作。
36.3万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

1. 信号量实现共享内存互斥访问原理

在共享内存编程中,信号量是一种计数器,用于控制对共享资源(这里即共享内存)的访问。通过设置信号量的初始值为1,进程在访问共享内存前获取信号量(将计数器减1),访问结束后释放信号量(将计数器加1)。如果信号量的值为0,说明已有其他进程在访问共享内存,此时试图获取信号量的进程会被阻塞,直到信号量的值变为1。

2. 代码示例

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

// 定义信号量操作函数
union semun {
    int val;                /* Value for SETVAL */
    struct semid_ds *buf;   /* Buffer for IPC_STAT, IPC_SET */
    unsigned short *array;  /* Array for GETALL, SETALL */
    struct seminfo *__buf;  /* Buffer for IPC_INFO (Linux-specific) */
};

// 初始化信号量
int init_semaphore(int semid, int value) {
    union semun sem_union;
    sem_union.val = value;
    if (semctl(semid, 0, SETVAL, sem_union) == -1) {
        perror("semctl SETVAL");
        return -1;
    }
    return 0;
}

// 获取信号量
int acquire_semaphore(int semid) {
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = -1;
    sem_b.sem_flg = SEM_UNDO;
    if (semop(semid, &sem_b, 1) == -1) {
        perror("semop acquire");
        return -1;
    }
    return 0;
}

// 释放信号量
int release_semaphore(int semid) {
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = 1;
    sem_b.sem_flg = SEM_UNDO;
    if (semop(semid, &sem_b, 1) == -1) {
        perror("semop release");
        return -1;
    }
    return 0;
}

int main() {
    key_t key;
    int semid, shmid;
    int *shared_memory;

    // 生成唯一键值
    if ((key = ftok(".", 'a')) == -1) {
        perror("ftok");
        return 1;
    }

    // 创建信号量
    if ((semid = semget(key, 1, IPC_CREAT | 0666)) == -1) {
        perror("semget");
        return 1;
    }

    // 初始化信号量为1
    if (init_semaphore(semid, 1) == -1) {
        return 1;
    }

    // 创建共享内存
    if ((shmid = shmget(key, sizeof(int), IPC_CREAT | 0666)) == -1) {
        perror("shmget");
        return 1;
    }

    // 连接共享内存到当前进程地址空间
    shared_memory = (int *)shmat(shmid, NULL, 0);
    if (shared_memory == (void *)-1) {
        perror("shmat");
        return 1;
    }

    // 获取信号量
    if (acquire_semaphore(semid) == -1) {
        return 1;
    }

    // 访问共享内存
    (*shared_memory)++;
    printf("Process %d incremented shared memory value: %d\n", getpid(), *shared_memory);

    // 释放信号量
    if (release_semaphore(semid) == -1) {
        return 1;
    }

    // 分离共享内存
    if (shmdt(shared_memory) == -1) {
        perror("shmdt");
        return 1;
    }

    // 删除共享内存和信号量
    if (shmctl(shmid, IPC_RMID, NULL) == -1) {
        perror("shmctl IPC_RMID");
        return 1;
    }
    if (semctl(semid, 0, IPC_RMID) == -1) {
        perror("semctl IPC_RMID");
        return 1;
    }

    return 0;
}

3. 代码说明

  • 信号量初始化init_semaphore 函数使用 semctl 函数并设置 SETVAL 命令来初始化信号量的值为传入的 value(这里为1)。
  • 信号量获取acquire_semaphore 函数使用 semop 函数,通过设置 sem_op 为 -1 来获取信号量,即尝试减少信号量的值。如果信号量的值为0,进程将被阻塞。
  • 信号量释放release_semaphore 函数使用 semop 函数,通过设置 sem_op 为 1 来释放信号量,即增加信号量的值,以便其他进程可以获取。

main 函数中,先创建信号量并初始化,然后获取信号量,访问共享内存,之后释放信号量,最后清理共享内存和信号量资源。