共享内存和信号量可以配合起来一起使用。
什么是共享内存?:
共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。
共享内存中几个主要的api函数:
- shmget——创建/打开共享内存
- shmat——将共享内存与当前进程相关联
- shmdt——将当前进程与共享内存间脱离关联
- shmctl——操控共享内存
因为是共享一块内存,所以其中必然涉及到同步的问题。共享内存如何实现进程间的同步呢?也就是说不同的进程不能同时访问同一块内存》
这个时候就需要使用到信号量。
什么是信号量?
信号量 (Semaphore) 是一种用于进程间同步和互斥的机制,可以防止多个进程同时访问共享资源导致的数据不一致问题。
信号量主要有两种类型:
- 计数信号量 (Counting Semaphore):用于控制对资源的计数,可以允许多个进程同时访问有限数量的资源。
- 二进制信号量 (Binary Semaphore):类似于互斥锁,只能取0或1,通常用于实现互斥。
计数信号量主要用于解决生产者-消费者问题。而二进制信号量主要解决互斥的问题。
信号量的两个基本操作:
- P (Proberen) 操作:也称为 "wait" 或 "down" 操作,尝试获取一个信号量。如果信号量大于0,则将信号量减1;如果信号量为0或负数,进程将进入等待状态,直到其他进程释放资源。
- V (Verhogen) 操作:也称为 "signal" 或 "up" 操作,释放一个信号量,即将信号量加1。如果有进程在等待资源,则唤醒其中的一个进程。
信号量的实现步骤:
1.创建和初始化信号量
#include <semaphore.h>
sem_t semaphore; // 定义信号量
// 初始化信号量,0 表示在进程间共享,1 是信号量的初始值
sem_init(&semaphore, 0, 1);
2.使用P操作
// 等待信号量(P 操作)
sem_wait(&semaphore);
3.操作临界区数据
4.使用V操作
// 释放信号量(V 操作)
sem_post(&semaphore);
5.销毁信号量
sem_destroy(&semaphore);
两种信号量的解决的问题:
1.进程间互斥:
#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
sem_t semaphore;
void* write_to_file(void* arg) {
sem_wait(&semaphore); // P 操作,等待进入临界区
// 临界区,写入文件
printf("进程 %d 进入临界区,正在写入文件...\n", (int)arg);
// 模拟文件写操作
sleep(2);
printf("进程 %d 退出临界区\n", (int)arg);
sem_post(&semaphore); // V 操作,释放信号量
return NULL;
}
int main() {
pthread_t t1, t2;
sem_init(&semaphore, 0, 1); // 初始化信号量
pthread_create(&t1, NULL, write_to_file, (void*)1);
pthread_create(&t2, NULL, write_to_file, (void*)2);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
sem_destroy(&semaphore); // 销毁信号量
return 0;
}
2. 生产者-消费者问题:
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];
int count = 0;
sem_t empty_slots; // 表示空缓冲区的信号量
sem_t full_slots; // 表示满缓冲区的信号量
sem_t mutex; // 保护缓冲区访问的互斥信号量
void* producer(void* arg) {
for (int i = 0; i < 10; ++i) {
sem_wait(&empty_slots); // 等待有空位
sem_wait(&mutex); // 进入临界区
buffer[count++] = i;
printf("生产者生产了数据 %d\n", i);
sem_post(&mutex); // 离开临界区
sem_post(&full_slots); // 通知有新数据
}
return NULL;
}
void* consumer(void* arg) {
for (int i = 0; i < 10; ++i) {
sem_wait(&full_slots); // 等待有新数据
sem_wait(&mutex); // 进入临界区
int data = buffer[--count];
printf("消费者消费了数据 %d\n", data);
sem_post(&mutex); // 离开临界区
sem_post(&empty_slots); // 通知有空位
}
return NULL;
}
int main() {
pthread_t prod, cons;
sem_init(&empty_slots, 0, BUFFER_SIZE); // 初始为空位数
sem_init(&full_slots, 0, 0); // 初始为无满缓冲区
sem_init(&mutex, 0, 1); // 互斥信号量
pthread_create(&prod, NULL, producer, NULL);
pthread_create(&cons, NULL, consumer, NULL);
pthread_join(prod, NULL);
pthread_join(cons, NULL);
sem_destroy(&empty_slots);
sem_destroy(&full_slots);
sem_destroy(&mutex);
return 0;
}