MST

星途 面试题库

面试题:C语言共享内存的同步机制及优化

在使用C语言进行Linux共享内存编程时,可能会遇到多个进程同时访问共享内存导致数据不一致的问题。请详细阐述你所知道的同步机制(如信号量等),并说明如何在代码层面进行优化以提高共享内存访问的效率和稳定性。以生产者 - 消费者模型为例,用C语言实现该模型,使用共享内存作为数据缓冲区,并用信号量来处理同步问题。
39.7万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

同步机制

  1. 信号量:信号量是一个整型变量,它通过计数器来控制对共享资源的访问。例如,当信号量的值大于0时,表示有可用的资源,进程可以获取信号量(计数器减1)来访问共享资源;当信号量的值为0时,表示资源已被占用,进程需要等待。
  2. 互斥锁:互斥锁本质上是一种特殊的二元信号量(值只能为0或1)。它用于保证在同一时刻只有一个进程能够访问共享资源,通过加锁(将值设为0)和解锁(将值设为1)操作来实现。
  3. 条件变量:条件变量通常与互斥锁一起使用。它允许进程在某个条件满足时被唤醒。例如,在生产者 - 消费者模型中,消费者进程可以在共享缓冲区不为空的条件下被唤醒。

代码层面优化

  1. 减少信号量操作次数:避免在频繁执行的代码段中进行信号量的获取和释放操作,尽量将信号量操作放在关键的同步点。
  2. 合理设置缓冲区大小:根据实际需求设置共享内存缓冲区的大小,避免缓冲区过小导致频繁的读写操作,或过大造成内存浪费。
  3. 预读和预写:在生产者 - 消费者模型中,生产者可以提前将数据写入缓冲区的空闲位置,消费者可以提前读取缓冲区中的数据,提高整体效率。

生产者 - 消费者模型C语言实现

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

#define BUFFER_SIZE 5
#define PRODUCE_NUM 20

// 共享内存结构体
typedef struct {
    int buffer[BUFFER_SIZE];
    int in;
    int out;
} SharedData;

SharedData *shared_data;
sem_t *empty;
sem_t *full;
sem_t *mutex;

// 生产者线程函数
void *producer(void *arg) {
    int i, item;
    for (i = 0; i < PRODUCE_NUM; i++) {
        item = i;
        sem_wait(empty);  // 等待缓冲区有空闲位置
        sem_wait(mutex);  // 进入临界区
        shared_data->buffer[shared_data->in] = item;
        printf("Produced: %d\n", item);
        shared_data->in = (shared_data->in + 1) % BUFFER_SIZE;
        sem_post(mutex);  // 离开临界区
        sem_post(full);   // 通知缓冲区有新数据
    }
    return NULL;
}

// 消费者线程函数
void *consumer(void *arg) {
    int i, item;
    for (i = 0; i < PRODUCE_NUM; i++) {
        sem_wait(full);   // 等待缓冲区有数据
        sem_wait(mutex);  // 进入临界区
        item = shared_data->buffer[shared_data->out];
        printf("Consumed: %d\n", item);
        shared_data->out = (shared_data->out + 1) % BUFFER_SIZE;
        sem_post(mutex);  // 离开临界区
        sem_post(empty);  // 通知缓冲区有空闲位置
    }
    return NULL;
}

int main() {
    key_t key;
    int shmid;
    pthread_t producer_thread, consumer_thread;

    // 创建共享内存
    key = ftok(".", 'a');
    shmid = shmget(key, sizeof(SharedData), IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget");
        exit(1);
    }
    shared_data = (SharedData *)shmat(shmid, NULL, 0);
    if (shared_data == (void *)-1) {
        perror("shmat");
        exit(1);
    }
    shared_data->in = 0;
    shared_data->out = 0;

    // 创建信号量
    empty = sem_open("/empty", O_CREAT, 0666, BUFFER_SIZE);
    full = sem_open("/full", O_CREAT, 0666, 0);
    mutex = sem_open("/mutex", O_CREAT, 0666, 1);
    if (empty == SEM_FAILED || full == SEM_FAILED || mutex == SEM_FAILED) {
        perror("sem_open");
        exit(1);
    }

    // 创建生产者和消费者线程
    if (pthread_create(&producer_thread, NULL, producer, NULL) != 0) {
        perror("pthread_create producer");
        exit(1);
    }
    if (pthread_create(&consumer_thread, NULL, consumer, NULL) != 0) {
        perror("pthread_create consumer");
        exit(1);
    }

    // 等待线程结束
    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);

    // 销毁信号量
    sem_close(empty);
    sem_close(full);
    sem_close(mutex);
    sem_unlink("/empty");
    sem_unlink("/full");
    sem_unlink("/mutex");

    // 分离共享内存
    if (shmdt(shared_data) == -1) {
        perror("shmdt");
        exit(1);
    }
    // 删除共享内存
    if (shmctl(shmid, IPC_RMID, NULL) == -1) {
        perror("shmctl");
        exit(1);
    }

    return 0;
}