Linux系统编程——信号量
一、信号量的定义和原理
1、概念
- 原子操作:不可中断的一个或者一系列的操作,即一件事要么做要么不做。
- 临界资源:不同进程能够看到的一份公共资源,一次只能被一个进程使用。
- PV操作:由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行;
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1。
就是两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。
2、信号量的定义
为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。
3、信号量的原理
1. 测试控制该资源的信号量;
2. 若信号量的值为正,则进程可以使用该资源,进程的信号量值减1,表示一个资源被使用;
3. 若此信号量为0,则进程进入休眠,直到该信号量值大于0;
4. 当进程不再使用一个由一个信号控制的共享资源时,该信号量加1,如果有进程正在休眠等待该信号量,则该进程会被唤醒。
二、信号量的使用
1、一些数据结构的定义
(1)semid_ds
内核为每个信号量集合维护着一个结构体:
struct semid_ds{struct ipc_perm sem_perm;unsigned short sem_nsems;time_t sem_otime;time_t sem_ctime;...
}
(2)semun(必须定义该联合体)
union semun{int val;struct semid_ds *buf;unsigned short *array;
}
(3)sembuf(信号量操作数组)
struct sembuf{// 除非使用一组信号量,否则它为0,一般从0,1,...num_secs-1unsigned short sem_num; // 信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,// 即P(等待)操作,一个是+1,即V(发送信号)操作。short sem_op;// 通常为SEM_UNDO,使操作系统跟踪信号,// 并在进程没有释放该信号量而终止时,操作系统释放信号量。short sem_flg;
}
2、semget函数
#include <sys/sem.h>
int semget(key_t key, int num_sems, int sem_flags);
功能:创建一个新信号量集或取得一个已有信号量集。
参数:
key:整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget函数并提供一个键,再由系统生成一个相应的信号标识符(semget函数的返回值),只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作;
num_sems:指定需要的信号量数目,它的值几乎总是1;
sem_flags:一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。
返回值:成功返回一个相应信号标识符(非零),失败返回-1。
3、semop函数
#include <sys/sem.h>
int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
功能:它的作用是改变信号量的值。
参数:
sem_id:由semget返回的信号量标识符;
sem_opa:表示一个由sembuf结构表示的信号量操作数组;
num_sem_ops:规定该数组中操作的数量。
返回值:成功返回0,失败返回-1。
4、semctl函数
#include <sys/sem.h>
int semctl(int sem_id, int sem_num, int command, ...);
功能:直接控制信号量的信息。
参数:
sem_id:信号量标识符;
sem_num:信号量的值;
command:通常是下面两个值中的其中一个:
SETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置;
IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。
返回值:成功返回0,失败返回-1。
三、信号量的demo
// comm.h
#ifndef _MYSEM_H_
#define _MYSEM_H_#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h> // ftok
#include <sys/sem.h>
#include <sys/wait.h>
#include <string.h>#define PATHNAME "." // ftok 中生成key值 . 表示当前路径
#define PROJ_ID 56 // ftok 中配合PATHNAME 生成唯一key值union semun
{int val;struct semid_ds *buf;unsigned short *array;struct seminfo *_buf;
};int create_sems(int nums); // 创建含有nums个信号量的集合
int get_sems(); // 获取信号量
// 初始化semid对应的信号量集中编号为which的信号量值为value
int init_sems(int semid , int which, int value);
int destroy_sems(int semid); // 释放该信号量集
int P(int semid, int which); // 表示分配 信号量值-1
int V(int semid, int which); // 表示释放 信号量值+1#endif /* _MYSEM_H_ */
// comm.cpp
#include "comm.h"// 创建信号量和获取信号量公用函数
static int comm_sem(int nums , int semflag)
{// ftok 函数把一个已存在的路径名和一个整数标识转换成一个key_t值,// 即IPC关键字。key_t key = ftok(PATHNAME, PROJ_ID);if(key < 0){perror("ftok");return -1;}// 用来创建一个信号集,或者获取已存在的信号集。int semid = semget(key, nums, semflag); if( semid < 0){perror("semget");return -1;}return semid;
}// 初始化操作数组
static int comm_sem_op(int semid, int which, int op)
{struct sembuf _sembuf;_sembuf.sem_num = which;_sembuf.sem_op = op;_sembuf.sem_flg = 0; // IPC_NOWAIT SEM_UNDOreturn semop(semid, &_sembuf, 1);
}// 创建含有nums个信号量的集合
int create_sems(int nums)
{return comm_sem(nums, IPC_CREAT|IPC_EXCL|0666);
}// 初始化信号集
int init_sems(int semid , int which, int value)
{union semun _semun;_semun.val = value;int ret = semctl(semid, which, SETVAL,_semun);if(ret < 0){perror("inin_sem");return -1;}return 0;
}// 获取信号量
int get_sems()
{return comm_sem(0, IPC_CREAT);
}// 释放该信号量集
int destroy_sems(int semid)
{int ret = semctl(semid, 0, IPC_RMID, NULL);if(ret < 0){perror("rm_sem");return -1;}return 0;
}// P操作
int P(int semid, int which)
{return comm_sem_op(semid, which , -1);
}// V操作
int V(int semid, int which)
{return comm_sem_op(semid, which, 1);
}
// test.cpp
#include "comm.h"
#include <stdio.h>
#include <unistd.h>int main()
{int semid = create_sems(10); // 创建一个包含10个信号量的信号集init_sems(semid, 0, 1); // 初始化编号为 0 的信号量值为1pid_t id = fork(); // 创建子进程if (id < 0){perror("fork");return -1;}else if (0 == id) // 子进程{ int sem_id = get_sems();while(1){P(sem_id, 0); // 对该信号量集中的0号信号做P操作printf("你");fflush(stdout);sleep(1);printf("好");printf(":");fflush(stdout);sleep(1);V(sem_id, 0);}}else // 父进程{while(1){P(semid,0);printf("在");sleep(1);printf("吗");printf("?");fflush(stdout);V(semid, 0);}wait(NULL);}destroy_sems(semid);return 0;
}
四、信号量的总结
信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。我们经常通过信号来解决多个进程对同一资源的访问竞争的问题,使在任一时刻只能有一个执行线程访问代码的临界区域,也可以说它是协调进程间的对同一资源的访问权,也就是用于同步进程的。
【优点】:可以同步进程。
【缺点】:信号量有限。
相关文章:
Linux系统编程——信号量
一、信号量的定义和原理 1、概念 原子操作:不可中断的一个或者一系列的操作,即一件事要么做要么不做。临界资源:不同进程能够看到的一份公共资源,一次只能被一个进程使用。PV操作:由于信号量只能进行两种操作等待和发…...
Oracle索引问题汇总
一、oracle 数据库TIMESTAMP 时间字段,设置索引后,通过该字段进行排序,索引排序不生效问题 1. 记录下在工作中遇到的一次索引问题 问题描述: 数据库:oracle; 日志记录表中的一个创建时间(create…...
基于QT用工厂模式实现串口通信与网络通信激光器的控制
配置文件网络配置:IP+Port 串口配置:端口号+波特率 首先,我们需要创建一个配置文件 config.ini,内容如下: [SerialLaser] portName = COM1 baudRate = 9600[NetworkLaser] ipAddress = 192.168.1.1 port = 1234两类激光器的实现: #include <QCoreApplicat…...
【代码随想录Day58】图论Part09
dijkstra(堆优化版)精讲 题目链接/文章讲解:代码随想录 import java.util.*;class Edge {int to; // 邻接顶点int val; // 边的权重Edge(int to, int val) {this.to to;this.val val;} }class Pair<U, V> {public final U first; …...
_或者%关键字模糊匹配查出所有数据
1、问题 sql模糊匹配,如果页面输入_或者%,可以查出所有数据。 (1) SELECT * FROM test WHERE sfsc N and zdzwm like %%% (2) SELECT * FROM test WHERE sfsc N and zdzwm like %_% 2、解决方案 (1)mysql数据库 加转义字…...
【Python】转换得到图片的rgb565格式数据
使用方法:首先在代码同级目录创建input_images文件夹,然后将需要转换的图片放进去。 然后根据你的需要,修改代码最下面的crop_size、resize以及file_name。 最后点击运行,即可得到图片的rgb565格式数据 from PIL import Image, I…...
隨筆 20241024 Kafka中的ISR列表:分区副本的族谱
在分布式系统中,数据的一致性和可靠性至关重要。Apache Kafka作为一个强大的流处理平台,利用其分区和副本机制来确保这些特性。在Kafka中,ISR(In-Sync Replicas)列表是一个关键概念,它用来追踪与领导者副本…...

【python】爬虫
下载与批量下载 import requests #第三方库,没有下载的下载一下 pip install requests#爬虫下载图片 resrequests.get("url") print(res.content)#二进制字节流#写文件 with open("beauty.jpg","wb")as f:f.write(res.content)#批量…...

大语言模型数据类型与环境安装(llama3模型)
文章目录 前言一、代码获取一、环境安装二、大语言模型数据类型1、基本文本指令数据类型2、数学指令数据类型3、几何图形指令数据类型4、多模态指令数据类型5、翻译指令数据类型三、vscode配置四、相关知识内容1、理解softmax内容2、torch相关函数nn.Embedding函数torch.nn.fun…...
JS:列表操作
目录 1、列表截取2、列表数据包含3、列表筛选4、极值操作5、获取列表对象某一属性构建列表6、获取元素在列表中的下标7、列表去重 1、列表截取 列表截取:List.slice(start, end),左闭右开 var dataList [1,2,3,4,5,6] var resultList dataList.slice(0…...

ECharts 折线图 / 柱状图 ,通用配置标注示例
option {tooltip: { // 关于提示框(tooltip)的配置// 显示某一个去掉trigger: axis,显示一起显示 trigger: axistrigger: axis},legend: {top: bottom, // 显示标注位置// textStyle: {// color: "#000", // 设置图例文字颜…...
统计数据集的TXT、XML及JSON标注文件中各类别/每个标签的数量
在计算机视觉和深度学习领域,标注文件是模型训练的重要组成部分。无论是图像分类、目标检测还是图像分割,正确的标注能够显著提升模型的性能。在实际应用中,我们需要快速了解每个类别的样本数量,以便进行数据分析、平衡类别分布或…...
Facebook登录客户追踪:了解用户访问路径,优化客户体验
随着数字化转型的不断加速,精准的客户数据收集和用户行为追踪成为企业提升用户体验和优化业务流程的关键。Facebook登录作为一种便捷的第三方登录方式,已经被广泛应用于各类网站和应用中。它不仅简化了用户的注册与登录流程,还帮助企业获得用…...

NUUO摄像头 debugging_center_utils 远程命令执行漏洞复现
0x01 产品描述: NUUO摄像头是由中国台湾NUUO公司生产的一款网络视频录像机(Network Video Recorder,简称NVR),广泛应用于零售、交通、教育、政府和银行等多个领域。它能够同时管理多个IP摄像头,…...
Nginx 的讲解和案例示范
一、基础理解 1.1 Nginx 是什么? Nginx是一个高性能的 Web 服务器和反向代理服务器,同时也可以作为邮件代理服务器。Nginx 以其高并发处理能力、低内存消耗和丰富的功能受到广泛欢迎。 主要功能: 静态资源服务:高效地提供 HTM…...
微信小程序元素水平居中或垂直居中
最近在做一个微信小程序的项目,其中涉及到css样式实现将<navigator>标签内的图片和文本元素垂直排列,并水平居中。在尝试实现的过程中,将元素在标签内的所有排列情况都顺带实现了。上代码: index.wxml <navigator url&…...

ClickHouse 神助攻:纽约城市公共交通管理(MTA)数据应用挑战赛
本文字数:13198;估计阅读时间:33 分钟 作者:The PME Team 本文在公众号【ClickHouseInc】首发 我们一向对开放数据挑战充满热情,所以当发现 MTA(城市交通管理局)在其官网发起了这样的挑战时&…...

ELK + Filebeat + Spring Boot:日志分析入门与实践(二)
目录 一、环境 1.1 ELKF环境 1.2 版本 1.3 流程 二、Filebeat安装 2.1 安装 2.2 新增配置采集日志 三、logstash 配置 3.1 配置输出日志到es 3.2 Grok 日志格式解析 3.2 启动 logstash 3.3 启动项目查看索引 一、环境 1.1 ELKF环境 springboot项目:w…...

使用 Docker Compose 将数据版 LobeChat 服务端部署
LobeChat 是一个基于 TypeScript 的开源聊天机器人项目,支持本地部署和接入多个大语言模型。本文介绍如何使用 Docker Compose 将 LobeChat 服务端及其数据库部署到生产环境,让您拥有一个私有化的、可定制的 AI 聊天助手。 一、部署前准备 服务器&…...
python如何完成金融领域的数据分析,思路以及常见的做法是什么?
引言 在现代金融领域,数据分析已成为决策支持的重要工具。随着金融市场的复杂性和数据量的激增,传统的分析方法已无法满足需求。 Python作为一种强大的编程语言,凭借其丰富的库和工具,成为金融数据分析的首选语言之一。 本文将探讨如何利用Python进行金融数据分析,包括…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...