📅  最后修改于: 2023-12-03 15:39:52.786000             🧑  作者: Mango
在操作系统中,信号量是一种用于处理多道程序并发执行的进程同步机制。它主要用于控制进程对共享资源的访问,以保证多个进程能够在访问共享资源时不会发生冲突。
在 Linux 中,我们可以使用 semaphore.h
头文件中提供的函数来对信号量进行操作。信号量的基本操作包括三个函数:
int sem_init(sem_t *sem, int pshared, unsigned int value);
这个函数用于初始化一个信号量,其中参数 sem
是一个指向信号量的指针,pshared
是用于控制信号量的作用范围,value
是信号量的初值。
如果 pshared
的值为 0,则该信号量只能在当前进程中使用。否则,该信号量将在多个进程间共享。
int sem_wait(sem_t *sem);
这个函数用于将信号量的值减 1。如果减完之后信号量的值变为了负数,表示当前资源正在被占用,那么调用进程就会被阻塞。
int sem_post(sem_t *sem);
这个函数用于将信号量的值加 1。如果此时有进程被阻塞等待该资源,则此时该进程将被唤醒。
信号量的经典应用场景包括生产者-消费者问题以及读者-写者问题。
在该问题中,我们需要实现两个进程:一个生产者进程和一个消费者进程。它们需要访问同一个共享队列来进行数据传输。当队列为空时,消费者进程需要等待生产者进程生产数据;当队列已满时,生产者进程需要等待消费者进程消费数据。
下面是一个使用信号量解决生产者-消费者问题的代码片段:
#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
#define MAX_ITEM 10
int queue[MAX_ITEM];
int front, rear;
sem_t mutex, empty, full;
void init() {
sem_init(&mutex, 0, 1);
sem_init(&empty, 0, MAX_ITEM);
sem_init(&full, 0, 0);
front = rear = 0;
}
void put(int item) {
sem_wait(&empty);
sem_wait(&mutex);
queue[rear] = item;
rear = (rear + 1) % MAX_ITEM;
sem_post(&mutex);
sem_post(&full);
}
int get() {
int item;
sem_wait(&full);
sem_wait(&mutex);
item = queue[front];
front = (front + 1) % MAX_ITEM;
sem_post(&mutex);
sem_post(&empty);
return item;
}
void *producer(void *arg) {
int item;
while (1) {
item = rand();
printf("producing item %d\n", item);
put(item);
}
}
void *consumer(void *arg) {
int item;
while (1) {
item = get();
printf("consuming item %d\n", item);
}
}
int main() {
init();
pthread_t p1, p2, p3, c1, c2, c3;
pthread_create(&p1, NULL, producer, NULL);
pthread_create(&p2, NULL, producer, NULL);
pthread_create(&p3, NULL, producer, NULL);
pthread_create(&c1, NULL, consumer, NULL);
pthread_create(&c2, NULL, consumer, NULL);
pthread_create(&c3, NULL, consumer, NULL);
pthread_join(p1, NULL);
pthread_join(p2, NULL);
pthread_join(p3, NULL);
pthread_join(c1, NULL);
pthread_join(c2, NULL);
pthread_join(c3, NULL);
return 0;
}
在上面的代码中,我们使用了三个信号量:mutex
用于实现互斥访问共享队列,empty
用于表示队列中空余的位置数量,full
则用于表示队列中已经有的元素数量。
在该问题中,我们需要实现两个进程:一个读者进程和一个写者进程。它们需要访问同一个共享文件,其中写者进程将向文件中写入数据,读者进程则需要从文件中读取数据。
下面是一个使用信号量解决读者-写者问题的代码片段:
#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
#define MAX_READERS 10
int g_count = 0;
sem_t g_rmutex, g_wmutex, g_readTry, g_resource;
void init() {
sem_init(&g_rmutex, 0, 1);
sem_init(&g_wmutex, 0, 1);
sem_init(&g_readTry, 0, 1);
sem_init(&g_resource, 0, 1);
}
void *writer(void *arg) {
int id = *(int*)arg;
while (1) {
sem_wait(&g_wmutex);
g_count++;
if (g_count == 1)
sem_wait(&g_resource);
sem_post(&g_wmutex);
printf("writer %d is writing\n", id);
// write to file...
sem_wait(&g_wmutex);
g_count--;
if (g_count == 0)
sem_post(&g_resource);
sem_post(&g_wmutex);
}
}
void *reader(void *arg) {
int id = *(int*)arg;
while (1) {
sem_wait(&g_readTry);
sem_wait(&g_rmutex);
g_count++;
if (g_count == 1)
sem_wait(&g_resource);
sem_post(&g_rmutex);
sem_post(&g_readTry);
printf("reader %d is reading\n", id);
// read from file...
sem_wait(&g_rmutex);
g_count--;
if (g_count == 0)
sem_post(&g_resource);
sem_post(&g_rmutex);
}
}
int main() {
init();
pthread_t writers[3], readers[3];
int ids[3] = {1, 2, 3};
for (int i = 0; i < 3; i++) {
pthread_create(&writers[i], NULL, writer, &ids[i]);
pthread_create(&readers[i], NULL, reader, &ids[i]);
}
for (int i = 0; i < 3; i++) {
pthread_join(writers[i], NULL);
pthread_join(readers[i], NULL);
}
return 0;
}
在上面的代码中,我们使用了四个信号量:g_rmutex
和 g_wmutex
用于实现读者和写者之间的互斥访问;g_readTry
用于实现读者间的互斥访问,g_resource
则用于表示共享文件是否正在被写入。