深入浅出:信号灯与系统V信号灯的实现与应用

news2025/4/13 9:34:05

深入浅出:信号灯与系统V信号灯的实现与应用

信号灯(Semaphore)是一种同步机制,用于控制对共享资源的访问。在多线程或多进程环境下,信号灯能够帮助协调多个执行单元对共享资源的访问,确保数据一致性与程序的正确执行。本文将从基本概念、常用API函数、以及有名和无名信号灯的差异讲解信号灯的使用,适合新手理解并实践。

1. 信号灯概述

信号灯是一种用来控制对共享资源的访问的计数器,具有两个主要操作:

  • P操作(sem_wait:请求资源,如果信号灯的值大于0,则成功获取资源并将信号灯值减1。如果信号灯值为0,则会阻塞,直到信号灯的值大于0为止。
  • V操作(sem_post:释放资源,将信号灯值加1。如果有其他进程或线程正在等待该信号灯,则唤醒其中一个。

这些操作通常用于解决临界区问题,确保同一时刻只有一个线程或进程能够访问共享资源。

2. 有名与无名信号灯

2.1 有名信号灯

有名信号灯(Named Semaphore)是通过文件系统进行标识和访问的信号灯。它通常用于进程间通信,因为不同进程可以通过访问相同的文件路径来使用这个信号灯。

2.1.1 关键API函数
  • sem_open:打开或创建有名信号灯。

    sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
    
    • name:信号灯的名称(以 / 开头)。
    • oflag:操作标志,如 O_CREAT 表示创建,O_EXCL 表示如果信号灯已存在则返回错误。
    • mode:权限模式(如 0666)。
    • value:信号灯的初始值。
  • sem_close:关闭信号灯。

    int sem_close(sem_t *sem);
    
  • sem_unlink:删除有名信号灯。

    int sem_unlink(const char *name);
    
2.1.2 应用场景

有名信号灯适用于需要跨进程同步的场景。例如,多个进程需要访问一个共享的硬件设备或共享的文件资源时,使用有名信号灯可以确保这些进程不会同时访问,避免冲突。

2.2 无名信号灯

无名信号灯(Unnamed Semaphore)通常用于进程内或线程间同步,它们由进程中的内存地址来标识,且通常与进程或线程的生命周期绑定。

2.2.1 关键API函数
  • sem_init:初始化无名信号灯。

    int sem_init(sem_t *sem, int pshared, unsigned int value);
    
    • pshared:是否在进程间共享(0 表示线程间共享,非0表示进程间共享,通常用于多线程应用)。
    • value:信号灯的初始值。
  • sem_destroy:销毁无名信号灯。

    int sem_destroy(sem_t *sem);
    
2.2.2 应用场景

无名信号灯通常用于多线程程序中,用于保护共享资源或者在多个线程间实现同步。由于无名信号灯的生命周期与进程或线程绑定,因此它们无法在进程之间共享。

3. 有名信号灯 vs 无名信号灯

特性有名信号灯无名信号灯
作用范围进程间共享线程间共享,或进程间共享(需共享内存)
标识方式文件系统路径名内存地址
创建方式sem_opensem_init
生命周期与文件系统绑定与进程或线程绑定
适用场景进程间同步线程间同步,或进程间同步(需共享内存)

4. 信号灯操作详解

4.1 sem_wait(P 操作)

sem_wait 函数实现了信号灯的减操作(P 操作),它是阻塞的,直到信号灯的值大于0才会继续执行。

int sem_wait(sem_t *sem);
  • 如果信号灯的值大于0,sem_wait 会将其减1,表示成功获取资源。
  • 如果信号灯的值为0,当前进程或线程会阻塞,直到其他进程或线程释放资源(通过 sem_post)。

4.2 sem_post(V 操作)

sem_post 函数实现了信号灯的加操作(V 操作),它用于释放资源。

int sem_post(sem_t *sem);
  • 将信号灯的值加1,表示资源已经释放。
  • 如果有其他进程或线程正在等待该信号灯,sem_post 会唤醒其中一个。

5. 生产者消费者模型示例

生产者消费者问题是一个经典的同步问题,生产者和消费者共享一个有限大小的缓冲区,生产者不断生产数据并将其放入缓冲区,而消费者不断消费数据并从缓冲区中取出数据。这个过程需要使用信号灯来协调生产者与消费者的行为,避免出现“缓冲区满”或“缓冲区空”的情况。

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

#define BUFFER_SIZE 10

int buffer[BUFFER_SIZE]; // 共享缓冲区
int count = 0; // 缓冲区中的数据数量

sem_t empty; // 空闲缓冲区信号灯
sem_t full;  // 已占用缓冲区信号灯
sem_t mutex; // 互斥信号灯,保护共享资源

void* producer(void* arg) {
    for (int i = 0; i < 20; i++) {
        sem_wait(&empty); // P操作,等待空闲缓冲区
        sem_wait(&mutex); // P操作,获取锁

        // 生产数据
        buffer[count] = i;
        count++;
        printf("Produced: %d\n", i);

        sem_post(&mutex); // V操作,释放锁
        sem_post(&full);  // V操作,增加已占用缓冲区
    }
    return NULL;
}

void* consumer(void* arg) {
    for (int i = 0; i < 20; i++) {
        sem_wait(&full); // P操作,等待已占用缓冲区
        sem_wait(&mutex); // P操作,获取锁

        // 消费数据
        int item = buffer[count - 1];
        count--;
        printf("Consumed: %d\n", item);

        sem_post(&mutex); // V操作,释放锁
        sem_post(&empty); // V操作,增加空闲缓冲区
    }
    return NULL;
}

int main() {
    pthread_t prod_tid, cons_tid;

    // 初始化信号灯
    sem_init(&empty, 0, BUFFER_SIZE); // 初始值为缓冲区大小
    sem_init(&full, 0, 0); // 初始值为 0
    sem_init(&mutex, 0, 1); // 初始值为 1(互斥信号灯)

    // 创建生产者和消费者线程
    pthread_create(&prod_tid, NULL, producer, NULL);
    pthread_create(&cons_tid, NULL, consumer, NULL);

    // 等待线程结束
    pthread_join(prod_tid, NULL);
    pthread_join(cons_tid, NULL);

    // 销毁信号灯
    sem_destroy(&empty);
    sem_destroy(&full);
    sem_destroy(&mutex);

    return 0;
}

5.1 程序说明

  • empty 信号灯表示缓冲区中空闲的槽位,初始值为缓冲区大小。
  • full 信号灯表示缓冲区中已占用的槽位,初始值为0。
  • mutex 信号灯用于实现互斥锁,保护共享资源(缓冲区)不被同时访问。

生产者线程通过 sem_wait(&empty)sem_post(&full) 来协调缓冲区的生产过程,而消费者线程通过 sem_wait(&full)sem_post(&empty) 来协调消费过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2333725.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

定位改了IP属地没变怎么回事?一文解析

明明用虚拟定位软件将手机位置改到了“三亚”&#xff0c;为何某某应用评论区显示的IP属地还是“北京”&#xff1f;为什么切换了代理IP&#xff0c;平台却似乎“无视”这一变化&#xff1f; 在“IP属地显示”功能普及后&#xff0c;许多用户尝试通过技术手段隐藏真实位置&…

《深入理解生命周期与作用域:以C语言为例》

&#x1f680;个人主页&#xff1a;BabyZZの秘密日记 &#x1f4d6;收入专栏&#xff1a;C语言 &#x1f30d;文章目入 一、生命周期&#xff1a;变量的存在时间&#xff08;一&#xff09;生命周期的定义&#xff08;二&#xff09;C语言中的生命周期类型&#xff08;三&#…

一个插件,免费使用所有顶级大模型(Deepseek,Gpt,Grok,Gemini)

DeepSider是一款集成于浏览器侧边栏的AI对话工具&#xff0c;可免费使用所有顶级大模型 包括GPT-4o&#xff0c;Grok3,Claude 3.5 Sonnet,Claude 3.7,Gemini 2.0&#xff0c;Deepseek R1满血版等 以极简交互与超快的响应速度&#xff0c;完成AI搜索、实时问答、内容创作、翻译、…

智能车摄像头开源—9 动态权、模糊PID、速度决策、路径优化

目录 一、前言 二、动态权 1.概述 2.偏差值加动态权 三、模糊PID 四、速度决策 1.曲率计算 2.速度拟合 3.速度控制 五、路径 六、国赛视频 一、前言 在前中期通过识别直道、弯道等元素可进行加减速操作实现速度的控制&#xff0c;可进一步缩减一圈的运行速度&#xff…

《2025蓝桥杯C++B组:D:产值调整》

**作者的个人gitee**​​ 作者的算法讲解主页▶️ 每日一言&#xff1a;“泪眼问花花不语&#xff0c;乱红飞过秋千去&#x1f338;&#x1f338;” 题目 二.解题策略 本题比较简单&#xff0c;我的思路是写三个函数分别计算黄金白银铜一次新产值&#xff0c;通过k次循环即可获…

2025认证杯一阶段各题需要使用的模型或算法(冲刺阶段)

A题&#xff08;小行星轨迹预测&#xff09; 问题一&#xff1a;三角测量法、最小二乘法、空间几何算法、最优化方法 问题二&#xff1a;Gauss/Laplace轨道确定方法、差分校正法、数值积分算法&#xff08;如Runge-Kutta法&#xff09;、卡尔曼滤波器 B题&#xff08;谣言在…

①(PROFINET 转 EtherNet/IP)EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

型号 协议转换通信网关 PROFINET 转 EtherNet/IP MS-GW32 概述 MS-GW32 是 PROFINET 和 EtherNet/IP 协议转换网关&#xff0c;为用户提供两种不同通讯协议的 PLC 进行数据交互的解决方案&#xff0c;可以轻松容易将 EtherNet/IP 网络接入 PROFINET 网络中&#xff0c;方便…

国标GB28181视频平台EasyCVR如何搭建汽车修理厂远程视频网络监控方案

一、背景分析 近年我国汽车保有量持续攀升&#xff0c;与之相伴的汽车保养维修需求也逐渐提高。随着社会经济的发展&#xff0c;消费者对汽车维修服务质量的要求越来越高&#xff0c;这使得汽车维修店的安全防范与人员管理问题面临着巨大挑战。 多数汽车维修店分布分散&#…

PostIn安装及入门教程

PostIn是一款国产开源免费的接口管理工具&#xff0c;包含项目管理、接口调试、接口文档设计、接口数据MOCK等模块&#xff0c;支持常见的HTTP协议、websocket协议等&#xff0c;支持免登陆本地接口调试&#xff0c;本文将介绍如何快速安装配置及入门使用教程。 1、安装 私有…

spring cloud微服务API网关详解及各种解决方案详解

微服务API网关详解 1. 核心概念 定义&#xff1a;API网关作为微服务的统一入口&#xff0c;负责请求路由、认证、限流、监控等功能&#xff0c;简化客户端与后端服务的交互。核心功能&#xff1a; 路由与转发&#xff1a;将请求分发到对应服务。协议转换&#xff1a;HTTP/HTTP…

最新版PhpStorm超详细图文安装教程,带补丁包(2025最新版保姆级教程)

目录 前言 一、PhpStorm最新版下载 二、PhpStorm安装 三、PhpStorm补丁 四、运行PhpStorm 前言 PhpStorm 是 JetBrains 公司推出的 专业 PHP 集成开发环境&#xff08;IDE&#xff09;&#xff0c;专为提升 PHP 开发效率设计。其核心功能包括智能代码补全、实时语法错误检…

linux kernel arch 目录介绍

一&#xff1a;arch 目录 二&#xff1a;常用arch

ES6变量声明:let、var、const全面解析

一、引言 ECMAScript 6&#xff08;简称 ES6&#xff09;的发布为 JavaScript 带来了许多革命性的变化&#xff0c;其中变量声明方式的更新尤为重要。let、var和const成为开发者日常编码中频繁使用的关键字。 本文将深入解析这三种声明方式的核心特性、区别及最佳实践&#xff…

Linux 入门八:Linux 多进程

一、概述 1.1 什么是进程&#xff1f; 在 Linux 系统中&#xff0c;进程是程序的一次动态执行过程。程序是静态的可执行文件&#xff0c;而进程是程序运行时的实例&#xff0c;系统会为其分配内存、CPU 时间片等资源。例如&#xff0c;输入 ls 命令时&#xff0c;系统创建进程…

单调栈 —— 1.基本概念与核心算法

1. 基本概念 1.1 知识预备 在理解单调栈之前&#xff0c;我们需要先掌握两个基础概念&#xff1a;栈&#xff08;Stack&#xff09; 和 单调性&#xff08;Monotonicity&#xff09;。 什么是栈&#xff08;Stack&#xff09; 栈是一种**后进先出&#xff08;LIFO, Last-In…

工程师 - 场效应管分类

What Are the Different Types of FETs? Pulse Octopart Staff Jul 31, 2021 Field effect transistors (FETs) are today’s workhorses for digital logic, but they enjoy plenty of applications outside of digital integrated circuits, everything from motor driver…

Debezium报错处理系列之第128篇:增量快照报错java.lang.OutOfMemoryError: Java heap space

Debezium报错处理系列之第128篇:增量快照报错java.lang.OutOfMemoryError: Java heap space 一、完整报错二、错误原因三、解决方法Debezium从入门到精通系列之:研究Debezium技术遇到的各种错误解决方法汇总: Debezium从入门到精通系列之:百篇系列文章汇总之研究Debezium技…

AI——使用pandas

文章目录 1、pandas介绍2、为什么使用pandas3、pandas的数据结构1、Series2、DataFrame3、MultiIndex 4、pandas基本数据操作1、索引操作2、赋值操作3、排序4、算术运算5、逻辑运算6、逻辑运算函数7、统计函数8、累计统计函数9、自定义运算 5、pandas读取文件和存储1、csv文件2…

2025认证杯挑战赛B题【 谣言在社交网络上的传播 】原创论文讲解(含完整python代码)

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了认证杯数学中国数学建模网络挑战赛第一阶段B题目谣言在社交网络上的传播完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半…

用docker容器创建属于自己的一方小世界!容器中,盖周天之变,化吾为王~

用docker容器创建属于自己的一方小世界&#xff01;容器中&#xff0c;盖周天之变&#xff0c;化吾为王~ 分别查看用户id和组id。 命令&#xff1a; 1、id -u 2、id -g 创建并运行容器 docker run -d -p 31404:22 -v /home/liub:/home -v /data:/app/data --user 1004:1004 --…