Linux系统编程进阶---信号量(超详细!!新手小白也能看懂!!)

目录

一、信号量的基本概念

二、信号量的操作

三、信号量的代码示例

基础

进阶样例:环形队列--生产者消费者模型

四、信号量在进程同步中的应用


一、信号量的基本概念

信号量(Semaphore)是一种用于控制多个进程对共享资源进行访问的同步机制。它通常用于解决进程间的互斥和同步问题,确保资源在某一时刻只能被一个进程访问(互斥),或者多个进程能够按照某种顺序访问资源(同步)。

  1. 二元信号量
    • 二元信号量是最简单的信号量形式,其值只能为0或1。
    • 当信号量的值为1时,表示资源可用,进程可以访问该资源。
    • 当信号量的值为0时,表示资源已被占用,进程需要等待。
  2. 二元可重复信号量
    • 与二元信号量不同,二元可重复信号量允许多个进程同时访问资源,但其值仍然只能为0或1。
    • 这里的“可重复”指的是信号量可以被多次“申请”(即P操作)和“释放”(即V操作),但每次操作后信号量的值仍然保持在0或1之间。
    • 实际上,这种信号量更接近于一种标志位,用于表示资源是否可用,而不是限制资源的访问次数。
  3. 多元信号量
    • 多元信号量的值可以大于1,表示允许多个进程同时访问资源。
    • 当信号量的值大于0时,表示还有可用的资源,进程可以申请访问。
    • 每当一个进程访问资源时,信号量的值减1;当进程释放资源时,信号量的值加1。

二、信号量的操作

信号量的操作主要包括创建、初始化、等待(P操作)和发送(V操作)。

  1. 创建信号量
    • 在使用信号量之前,需要先创建它。
    • 在POSIX标准中,可以使用sem_init函数来初始化一个未命名的信号量,或者使用semget函数来创建一个命名的信号量集。
  2. 初始化信号量
    • 初始化信号量时,需要指定信号量的初始值。
    • 对于未命名的信号量,sem_init函数的第三个参数用于指定信号量的初始值。
    • 对于命名的信号量集,semget函数创建信号量集后,还需要使用semctl函数来初始化每个信号量的值。
  3. 等待信号量(P操作)
    • 等待信号量是指进程尝试获取对共享资源的访问权。
    • 如果信号量的值大于0,则进程成功获取资源,信号量的值减1。
    • 如果信号量的值为0,则进程进入等待状态,直到其他进程释放资源并增加信号量的值。
    • 在POSIX标准中,可以使用sem_wait函数来等待信号量。
  4. 发送信号量(V操作)
    • 发送信号量是指进程释放对共享资源的访问权。
    • 进程释放资源后,信号量的值加1。
    • 如果有等待该信号量的进程,则唤醒其中一个进程。
    • 在POSIX标准中,可以使用sem_post函数来发送信号量。

三、信号量的代码示例

基础

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>

sem_t semaphore;

void* thread_function(void* arg) 
{
    // 等待信号量
    sem_wait(&semaphore);
    
    // 访问共享资源
    printf("Thread %ld is accessing the resource.\n", (long)arg);
    
    // 模拟资源访问时间
    sleep(1);
    
    // 释放信号量
    sem_post(&semaphore);
    
    return NULL;
}

int main() {
    pthread_t threads[5];
    
    // 初始化信号量,初始值为1
    sem_init(&semaphore, 0, 1);
    
    // 创建多个线程
    for (long i = 0; i < 5; i++) 
    {
        pthread_create(&threads[i], NULL, thread_function, (void*)i);
    }
    
    // 等待所有线程完成
    for (int i = 0; i < 5; i++) 
    {
        pthread_join(threads[i], NULL);
    }
    
    // 销毁信号量
    sem_destroy(&semaphore);
    
    return 0;
}

在这个示例中,我们创建了一个初始值为1的信号量,并创建了5个线程。每个线程在访问共享资源之前都会等待信号量,访问完成后释放信号量。由于信号量的初始值为1,因此只有一个线程能够同时访问资源,其他线程需要等待。

进阶样例:环形队列--生产者消费者模型

//.hpp文件

#ifndef _RINGQUEUE_HPP_
#define _RINGQUEUE_HPP_
#include <iostream>
#include <semaphore.h>
#include <string>
#include <vector>
#include <pthread.h>
#include <unistd.h>

using DataType = int;
static int DEFAULT = 5;
template<typename T>
class RingQueue
{
private:
    void P(sem_t& sem)
    {
        sem_wait(&sem);
    }

    void V(sem_t& sem)
    {
        sem_post(&sem);
    }

public:
    RingQueue(int maxcap = DEFAULT)
        : _MaxCap(maxcap), _ringQueue(maxcap), _p_index(0), _c_index(0)
    {
        // 初始化信号量
        sem_init(&_Space_sem, 0, _MaxCap);
        sem_init(&_Data_sem, 0, 0);
    }
    void Push(const T &data)
    {
        // 先申请信号量预定空间资源
        P(_Space_sem);

        _ringQueue[_p_index] = data;
        _p_index = (++_p_index) % _MaxCap;

        V(_Data_sem);
    }
    void Pop(T *OutData)
    {
        // 先申请信号量预定data资源
        P(_Data_sem);

        *OutData = _ringQueue[_c_index];
        _c_index = (++_c_index) % _MaxCap;

        V(_Space_sem);
    }
    ~RingQueue()
    {
        // 销毁信号量
        sem_destroy(&_Space_sem);
        sem_destroy(&_Data_sem);
    }

private:
    std::vector<T> _ringQueue;
    int _MaxCap;
    int _p_index;     // 生产者下标
    int _c_index;     // 消费者下标
    sem_t _Space_sem; // 生产者信号量
    sem_t _Data_sem;  // 消费者信号量
};

#endif

四、信号量在进程同步中的应用

信号量在进程同步中有着广泛的应用,特别是在解决生产者-消费者问题和哲学家就餐问题时。

  1. 生产者-消费者问题
    • 生产者-消费者问题是一个经典的同步问题,其中生产者进程生产数据并将其放入缓冲区,消费者进程从缓冲区中取出数据并处理。
    • 为了确保生产者和消费者之间的正确同步,可以使用信号量来控制对缓冲区的访问。
    • 通常,会使用两个信号量:一个用于控制空缓冲区的数量(生产者等待),另一个用于控制满缓冲区的数量(消费者等待)。
  2. 哲学家就餐问题
    • 哲学家就餐问题是一个涉及多个进程(哲学家)和多个资源(叉子)的同步问题。
    • 每个哲学家需要两把叉子才能吃饭,而叉子数量有限且分布在桌子上。
    • 为了避免死锁和饥饿问题,可以使用信号量来控制对叉子的访问。
    • 通常,会为每把叉子分配一个信号量,并在哲学家尝试拿起或放下叉子时进行相应的等待和发送操作 done
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值