MST

星途 面试题库

面试题:C语言Linux信号量PV操作在复杂场景中的应用

假设有一个多进程的C语言程序,进程A负责生产数据并放入共享缓冲区,进程B和进程C负责从共享缓冲区消费数据。请使用Linux信号量的PV操作来协调这三个进程的同步,以确保数据的正确生产和消费,同时要避免出现死锁情况。阐述设计思路并给出核心代码片段。
23.1万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 共享缓冲区:使用共享内存来实现共享缓冲区,以让多个进程可以访问。
  2. 信号量
    • 创建三个信号量:一个用于表示共享缓冲区中的空闲槽位(empty),初始值为缓冲区大小;一个用于表示共享缓冲区中的已占用槽位(full),初始值为0;另一个用于实现互斥访问共享缓冲区(mutex),初始值为1。
    • 进程A(生产者):先获取empty信号量(表示有空闲槽位才能生产),然后获取mutex信号量(互斥访问共享缓冲区),生产数据放入缓冲区,再释放mutex信号量,最后释放full信号量(表示缓冲区有数据了)。
    • 进程B和进程C(消费者):先获取full信号量(表示有数据才能消费),然后获取mutex信号量(互斥访问共享缓冲区),从缓冲区消费数据,再释放mutex信号量,最后释放empty信号量(表示缓冲区有空闲槽位了)。
  3. 避免死锁:按照固定的顺序获取信号量(先empty/full,再mutex),并且在获取信号量失败时进行合理处理(例如等待或重试)。

核心代码片段

#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>

#define BUFFER_SIZE 5

// 定义信号量操作函数
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

void P(int semid, int semnum) {
    struct sembuf sops = {semnum, -1, 0};
    semop(semid, &sops, 1);
}

void V(int semid, int semnum) {
    struct sembuf sops = {semnum, 1, 0};
    semop(semid, &sops, 1);
}

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

    // 创建共享内存
    key = ftok(".", 'a');
    shmid = shmget(key, sizeof(int) * BUFFER_SIZE, IPC_CREAT | 0666);
    shared_buffer = (int *)shmat(shmid, NULL, 0);

    // 创建信号量
    key = ftok(".", 'b');
    semid = semget(key, 3, IPC_CREAT | 0666);
    union semun arg;

    // 初始化信号量
    arg.val = BUFFER_SIZE;
    semctl(semid, 0, SETVAL, arg); // empty
    arg.val = 0;
    semctl(semid, 1, SETVAL, arg); // full
    arg.val = 1;
    semctl(semid, 2, SETVAL, arg); // mutex

    // 创建进程A
    if (fork() == 0) {
        for (int i = 0; i < 10; i++) {
            P(semid, 0); // 获取empty信号量
            P(semid, 2); // 获取mutex信号量
            shared_buffer[i % BUFFER_SIZE] = i;
            printf("Producer A produced %d\n", i);
            V(semid, 2); // 释放mutex信号量
            V(semid, 1); // 释放full信号量
        }
        exit(0);
    }

    // 创建进程B
    if (fork() == 0) {
        for (int i = 0; i < 5; i++) {
            P(semid, 1); // 获取full信号量
            P(semid, 2); // 获取mutex信号量
            int data = shared_buffer[i % BUFFER_SIZE];
            printf("Consumer B consumed %d\n", data);
            V(semid, 2); // 释放mutex信号量
            V(semid, 0); // 释放empty信号量
        }
        exit(0);
    }

    // 创建进程C
    if (fork() == 0) {
        for (int i = 0; i < 5; i++) {
            P(semid, 1); // 获取full信号量
            P(semid, 2); // 获取mutex信号量
            int data = shared_buffer[(i + 5) % BUFFER_SIZE];
            printf("Consumer C consumed %d\n", data);
            V(semid, 2); // 释放mutex信号量
            V(semid, 0); // 释放empty信号量
        }
        exit(0);
    }

    // 等待子进程结束
    wait(NULL);
    wait(NULL);
    wait(NULL);

    // 清理共享内存和信号量
    shmdt(shared_buffer);
    shmctl(shmid, IPC_RMID, NULL);
    semctl(semid, 0, IPC_RMID, 0);

    return 0;
}