《Linux C编程实战》笔记:信号量

信号量在操作系统的书里一般都有介绍,这里就只写书上说的了。

信号量是一个计数器,常用于处理进程或线程的同步问题,特别是对临界资源访问的同步。临界资源可以简单地理解为在某一时刻只能由一个进程或线程进行操作的资源,这里的资源可以是一段代码、一个变量或某种硬件资源。信号量的值大于或等于0时表示可供并发进程使用的资源实体数;小于0时代表正在等待使用临界资源的进程数。

与消息队列类似,Linux内核也为每个信号量集维护了一个semid_ds数据结构示例。该结构定义在头文件linux/sem.h中。

struct semid_ds {struct ipc_perm	sem_perm;		/* 对信号进行操作的许可权,和上一节的消息队列一样的 */__kernel_old_time_t sem_otime;		/* 对信号量进行操作的最后时间 */__kernel_old_time_t sem_ctime;		/* 对信号量进行修改的最后时间 */struct sem	*sem_base;		/* 指向第一个信号量 */struct sem_queue *sem_pending;		/* 等待处理的挂起操作 */struct sem_queue **sem_pending_last;	/* 最后一个正在挂起的操作 */struct sem_undo	*undo;			/* 撤销的请求 */unsigned short	sem_nsems;		/* 数组中的信号量个数 */
};

信号量的创建与使用

信号量集的创建与打开

信号量集和消息队列一样,创建了之后在进程间使用。Linux下使用semget创建或打开信号集,这个函数定义在头文件sys/sem.h中

int semget(key_t key, int nsems, int semflg);
  • key:一个键值,用于唯一标识信号量集。通常可以使用 ftok() 函数来生成该键值。
  • nsems:指定要创建或访问的信号量集中的信号量数量。如果只是打开信号量集,取0即可
  • semflg:这个参数和创建消息队列里的msgflg使用方式一样,通过IPC_CREATE,IPC_EXCL等标志来标志操作方式,具体看上一篇文章

semget() 函数成功时返回一个非负整数,表示信号量集的标识符(或称为信号量集描述符),失败返回-1

示例代码1

下面函数演示创建一个信号量集并对其中所有信号量进行初始化

int createsem(const char *pathname,int proj_id,int members,int init_val){key_t msgkey;int index,sid;union semun semopts;//这个结构体后面会讲if((msgkey=ftok(pathname,proj_id))==-1){perror("ftok error!\n");return -1;;}if((sid=semget(msgkey,members,IPC_CREAT|0666))==-1){perror("semget call failed.\n");return -1;}//后面是初始化操作semopts.val=init_val;for(index=0;index<members;index++){semctl(sid,index,SETVAL,semopts);}return sid;
}

信号量的操作

信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量。当它的值小于0时,其绝对值表示等待使用该资源的进程个数。信号量的值仅能由PV操作来改变。在Linux下,PV操作通过调用semop实现。该函数定义在头文件sys/sem.h中

int semop(int semid, struct sembuf *sops, size_t nsops);
  • semid:信号量集的标识符,通常是由 semget() 函数返回的值。
  • sops:一个指向结构数组的指针,每个结构体描述了对单个信号量的操作。sembuf 结构体包含以下字段:
    • sem_num:要操作的信号量在信号量集中的索引。
    • sem_op:操作类型,可以是正数表示增加信号量的值,负数表示减少信号量的值,0 表示等待信号量值变为 0。
    • sem_flg:标志位,用于指定操作的行为,比如操作的方式(阻塞或非阻塞)等。
  • nsops:操作的数量,即 sops 数组中元素的个数。
  • 成功返回0,失败返回-1

当然还是要看一下sembuf这个结构体具体的内容

struct sembuf
{unsigned short int sem_num;	/* 信号在信号量集中的索引 */short int sem_op;		/* 操作类型 */short int sem_flg;		/* 操作标志 */
};

sem_flg可以设置为IPC_NOWAIT,则调用进程直接返回。如果没设置,semop会阻塞进程直到资源可用。

示例代码2

下面是对一个信号量集中的某个信号进行操作的P、V函数

int sem_p(int semid,int index){//p是消耗资源struct sembuf buf={0,-1,IPC_NOWAIT};//所以第二个参数给的是负数,这里是-1if(index<0){perror("index of array cannot equal a minus value!\n");return -1;}buf.sem_num=index;if(semop(semid,&buf,1)==-1){//nsops是1,因为数组大小只有1perror("a wrong operation to semaphore ocurred!\n");return -1;}return 0;
}
int sem_v(int semid,int index){struct sembuf buf={0,1,IPC_NOWAIT};//v是释放资源,所以sem_op是正数1if(index<0){perror("index of array cannot equal a minus value!\n");return -1;}buf.sem_num=index;if(semop(semid,&buf,1)==-1){perror("a wrong operation to semaphore ocurred!\n");return -1;}return 0;
}

信号量集的控制

使用信号量时,往往需要对信号量集进行一些控制操作。比如删除信号量集、对内核维护的信号量集的数据结构semid_ds进行设置,获取信号量集中信号值等。通过semtcl控制函数可以完成这些操作,该函数定义在sys/sem.h,如下图所示:

int semctl(int semid, int semnum, int cmd, ...);
  • semid:信号量集的标识符。
  • semnum:信号量在集合中的索引。
  • cmd:要执行的控制操作。
  • ...:根据 cmd 参数指定的控制操作,可能需要附加参数。

最后的...说明函数的参数是可选的,它依赖于第三个参数cmd,它通过共用体变量semun选择要操作的参数,semun定义在linux/sem.h

union semun {int val;			/* 设置某个信号的值等于val for SETVAL */struct semid_ds *buf;	/* 存取semid_ds for IPC_STAT & IPC_SET */unsigned short *array;	/*  for GETALL & SETALL */struct seminfo *__buf;	/* 为控制IPC_INFO 提供的缓存 */void *__pad;
};

第二个参数cmd,通过宏来只是操作类型

  1. IPC_STAT:获取信号量集的当前状态,包括信号量集中每个信号量的当前值等信息。

  2. IPC_SET:设置信号量集的状态,可以用于设置信号量集中每个信号量的值。

  3. IPC_RMID:从系统中删除信号量集,释放其占用的资源。

  4. GETALL:获取信号量集中所有信号量的当前值。

  5. GETNCNT:获取在等待信号量值增加的进程数量。

  6. GETPID:获取上次执行 semop 函数的进程的进程ID。

  7. GETVAL:获取特定信号量的当前值。

  8. GETZCNT:获取在等待信号量值变为 0 的进程数量。

  9. SETALL:设置信号量集中所有信号量的值。

  10. SETVAL:设置特定信号量的值。

示例代码3

下面是获取和设置单个新信号的函数

int semval_op(int semid,int index,int cmd){if(index<0){printf("index cannot be minus!\n");return -1;}if(cmd==GETVAL||cmd==SETVAL)return semctl(semid,index,cmd,0);//0只有在SETVAL才有用,表示我们要把该信号量的值设置为0printf("function cannot support cmd:%d\n",cmd);return -1;
}

之前介绍的时候没有说setctl的返回值,因为也是根据cmd的不同而不同的。比如这个示例代码。如果cmd是GETVAL,那么函数的返回值就是对应信号量的值;如果cmd是SETVAL,那么函数的返回值就是用来标识操作是否成功,成功返回0,失败返回-1.

示例程序4

信号量一般用于处理访问临界资源的同步问题。下面也是两个程序,server和client。server创建一个信号量集,并对信号量循环减1,相当于分配资源。client执行时检查信号量,如果其值大于0表示有资源可用,继续执行,如果小于等于0代表资源已经分配完毕,进程client退出。

server:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h> 
union semun {int val;                // Value for SETVALstruct semid_ds *buf;   // Buffer for IPC_STAT, IPC_SETunsigned short *array;  // Array for GETALL, SETALLstruct seminfo *__buf;  // Buffer for IPC_INFO (Linux-specific)
};#define MAX_RESOURCE 5
int main(int argc,char **argv,char **environ){key_t key;int semid;struct sembuf sbuf={0,-1,IPC_NOWAIT};//-1代表使用资源union semun semopts;if((key=ftok(".",'s'))==-1){perror("ftok error!\n");exit(1);}if((semid=semget(key,1,IPC_CREAT|0666))==-1){//1表面信号量集只有一个信号量perror("semget error!\n");exit(1);}semopts.val=MAX_RESOURCE;if(semctl(semid,0,SETVAL,semopts)==-1){//把信号量集的那个信号量的值设置成MAX_RESOURCEperror("semctl error!\n");exit(1);}while (1){if(semop(semid,&sbuf,1)==-1){//程序循环减少信号量的值,也就是隔3秒使用一个资源perror("semop error!\n");exit(1);}sleep(3);}exit(0);
}

编译这个文件的问题很多,按照书上所说的应该加上linux/sem.h这个头文件,因为union semun就是在这个头文件定义的,但是它和 <sys/sem.h>一起包含的话会出现很多的重复定义,最后只能不包含linux/sem.h,自己去定义union semun了。

而如果只用linux/sem.h的话像semctl这些函数又没有定义,程序也用不了。

所以感觉要么是书上有错误,要么是书太老了,很多东西都改了导致照书上敲有问题

client:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/sem.h>
//把sys/ipc.h删了是因为我发现sem.h里就已经包含了它
#include <unistd.h> 
union semun {int val;                // Value for SETVALstruct semid_ds *buf;   // Buffer for IPC_STAT, IPC_SETunsigned short *array;  // Array for GETALL, SETALLstruct seminfo *__buf;  // Buffer for IPC_INFO (Linux-specific)
};
int main(int argc,char **argv,char **environ){key_t key;int semid,semval;union semun semopts;if((key=ftok(".",'s'))==-1){perror("ftok error!\n");exit(1);}if((semid=semget(key,1,IPC_CREAT|0666))==-1){perror("semget error!\n");exit(1);}while (1){if((semval=semctl(semid,0,GETVAL))==-1){//去获取信号量的值perror("semctl error!\n");exit(1);}if(semval>0){//大于0说明还有资源可用printf("Still %d resources can be used\n",semval);}else{printf("No more resources can be used!\n");break;}sleep(3);}exit(0);
}

运行结果:

稍微解释一下 ,先执行server,再执行client,资源不是从5开始应该是因为client不是第一时间执行,漏了两个。而server在信号量为0后就自动退出也是因为struct sembuf sbuf={0,-1,IPC_NOWAIT};设置了IPC_NOWAIT,这样在semop(semid,&sbuf,1)==-1这个判断中不会阻塞而是直接返回-1从而退出程序。

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

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

相关文章

【零基础入门】什么样的人适合学网络安全?该怎么学?

有很多想要转行网络安全或者选择网络安全专业的人在进行决定之前一定会有的问题&#xff1a; 什么样的人适合学习网络安全&#xff1f;我适不适合学习网络安全&#xff1f; 到底什么样的人适合学习网络安全呢&#xff1f;&#xff08;需要什么前提条件呢&#xff09; 开门见山…

【Appium UI自动化】pytest运行常见错误解决办法

通过Appium工具录制代码在pycharm上运行报错&#xff1a; 错误一&#xff1a; 1.提示 setup() 方法运行 error failed 解决办法&#xff1a;未创建 init __ 方法&#xff0c;创建一个空的__init.py文件就解决了。 原因&#xff1a; 错误二&#xff1a; 2.运行代码&#xff…

Linux之ACL权限管理

文章目录 1.ACL权限介绍二、操作步骤1. 添加测试目录、用户、组&#xff0c;并将用户添加到组2. 修改目录的所有者和所属组3. 设定权限4. 为临时用户分配权限5. 验证acl权限6. 控制组的acl权限 1.ACL权限介绍 每个项目成员有一个自己的项目目录&#xff0c;对自己的目录有完全…

构建生物医学知识图谱from zero to hero (4):通过Neo4j构建知识图谱

图数据库是一种专门用于存储图形数据的 NoSQL 数据库。与传统的关系型数据库和其他 NoSQL 数据库不同,图数据库利用图形数据模型来存储和管理数据。图形数据模型由节点和边组成,节点代表实体,边代表实体之间的关系。例如,在社交网络中,用户可以表示为节点,朋友关系可以表…

xff注入 [CISCN2019 华东南赛区]Web111

打开题目 看见smarty 想到模板注入 又看见ip 想到xff注入 一般情况下输入{$smarty.version}就可以看到返回的smarty的版本号。该题目的Smarty版本是3.1.30 在Smarty3的官方手册里有以下描述: Smarty已经废弃{php}标签&#xff0c;强烈建议不要使用。在Smarty 3.1&#xff…

C# OpenVINO 百度PaddleSeg实时人像抠图PP-MattingV2

目录 效果 项目 代码 下载 C# OpenVINO 百度PaddleSeg实时人像抠图PP-MattingV2 效果 项目 代码 using OpenCvSharp; using Sdcb.OpenVINO; using System; using System.Diagnostics; using System.Drawing; using System.Security.Cryptography; using System.Text; us…

SparkSQL学习03-数据读取与存储

文章目录 1 数据的加载1.1 方式一&#xff1a;spark.read.format1.1.1读取json数据1.1.2 读取jdbc数据 1.2 方式二&#xff1a;spark.read.xxx1.2.1 读取json数据1.2.2 读取csv数据1.2.3 读取txt数据1.2.4 读取parquet数据1.2.5 读取orc数据1.2.6 读取jdbc数据 2 数据的保存2.1…

SmartX 携手 openGauss 社区发布联合方案评测与性能最佳实践

近日&#xff0c;北京志凌海纳科技有限公司&#xff08;以下简称 “SmartX”&#xff09;携手 openGauss 社区完成了 openGauss 数据库基于 SmartX 超融合平台&#xff08;SMTX OS&#xff09;和 SmartX 分布式存储平台&#xff08;SMTX ZBS&#xff09;的性能测试和调优。 结果…

MySQL数据库基础(十三):关系型数据库三范式介绍

文章目录 关系型数据库三范式介绍 一、什么是三范式 二、数据冗余 三、范式的划分 四、一范式 五、二范式 六、三范式 七、总结 关系型数据库三范式介绍 一、什么是三范式 设计关系数据库时&#xff0c;遵从不同的规范要求&#xff0c;设计出合理的关系型数据库&…

紫光同创初使用

芯片PGC2KG-6LPG144 1、安装好软件接&#xff0c;加载license,有两个&#xff0c;与电脑MAC地址绑定的 2、正常使用后&#xff0c;新建个工程&#xff0c;配置管脚Tools→UCE 3、程序中有些信号被软件认为是时钟信号&#xff0c;会报错&#xff08;时钟输入I0约束在非专用时钟…

【LeetCode刷题笔记】242.有效的字母异位词

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法知识专栏&#xff1a;算法分析&#x1f525; 给大家跳段街舞感谢…

Spring基础之AOP和代理模式

文章目录 理解AOPAOP的实现原理 AOP代理模式静态代理动态代理1-JDK动态代理2-CGLIB动态代理 总结 理解AOP OOP - - Object Oriented Programming 面向对象编程 AOP - - Aspect Oriented Programming 面向切面编程 AOP是Spring提供的关键特性之一。AOP即面向切面编程&#xff0…

Jenkins邮件通知配置(7)

1、安装插件&#xff1a; Email Extension&#xff0c;Email Extension Template&#xff0c;这两个插件可以帮助我们进行邮件的编写发送以及格式化 2、配置jenkins中链接腾讯企业邮箱 先配置发送服务&#xff0c;然后在具体工程中设置接收者 基础信息&#xff1a; POP3/S…

SWIFT:自我认知微调

文档:https://github.com/modelscope/swift/blob/main/docs/source/LLM/%E8%87%AA%E6%88%91%E8%AE%A4%E7%9F%A5%E5%BE%AE%E8%B0%83%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5.md ​​​​​​代码: Swift是如何把自我认知数据集融合到训练集中呢? 1:相关的3个参数

企业级大数据安全架构(十一)Kerberos接入dophinscheduler

作者&#xff1a;楼高 建议将dophinscheduler集成到Ambari安装部署&#xff0c;在Ambari上面开启kerberos 1.安装准备 编译 从GitHub获取dolphinscheduler-1.3.9源码 git clone https://github.com/apache/dolphinscheduler.git -b 1.3.9-releasehttps://github.com/apache/…

多输入回归预测|GWO-CNN-LSTM|灰狼算法优化的卷积-长短期神经网络回归预测(Matlab)

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、算法介绍&#xff1a; 灰狼优化算法&#xff1a; 卷积神经网络-长短期记忆网络&#xff1a; 四、完整程序下载&#xff1a; 一、程序及算法内容…

ChatGPT/GPT4科研应用与AI绘图及论文写作

2023年随着OpenAI开发者大会的召开&#xff0c;最重磅更新当属GPTs&#xff0c;多模态API&#xff0c;未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网和个人电脑的问世。360创始人周鸿祎认为未来各行各业如果不能搭上这班车…

重看LeakCanary

LeakCanary是我很久之前看的东西了&#xff0c;我当时侯对它的印象就是它可以用来检测内存泄漏&#xff0c;具体原理就是将弱引用对象延迟个5s然后看是否被回收,如果没有被回收,那么就说明发生了内存泄漏,其他的也没有仔细地看 现在就详细地梳理一遍这个流程&#xff1a; 1.L…

微服务篇之分布式事务

一、Seata架构 Seata事务管理中有三个重要的角色&#xff1a; TC (Transaction Coordinator) - 事务协调者&#xff1a;维护全局和分支事务的状态&#xff0c;协调全局事务提交或回滚。 TM (Transaction Manager) - 事务管理器&#xff1a;定义全局事务的范围、开始全局事务、…

docker学习总结

docker 1.初识Docker1.1.什么是Docker1.1.1.应用部署的环境问题1.1.2.Docker解决依赖兼容问题1.1.3.Docker解决操作系统环境差异1.1.4.小结 1.2.Docker和虚拟机的区别1.3.Docker架构1.3.1.镜像和容器1.3.2.DockerHub1.3.3.Docker架构1.3.4.小结 1.4.安装Docker 2.Docker的基本操…