《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://xiahunao.cn/news/2805886.html

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

相关文章

删除遥感影像raster:另一个程序正在使用此文件,进程无法访问

问题&#xff1a; 在文件夹中删除处理过程得到的临时影像时&#xff0c;出现了上面的问题 os.remove(os.path.join(workspace2.replace(.tif, .cut.tif)))原因&#xff1a; 在文件夹中删除任何内容&#xff0c;比如文本、图片、影像时&#xff0c;都要先关闭这个对象 解决方…

【这个词(Sequence-to-Sequence)在深度学习中怎么解释,有什么作用?】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;深度学习笔记 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; Sequence-to-Sequence&#xff08;Seq2Seq&#xff09; Sequence-to-Sequence&#xff08;Seq2Seq…

加密函数f

1. 加密函数f 加密函数f(Ri-1,Ki)是DES中的核心算法,该函数包含选择运算E、异或运算、代替函数组S(S盒变换)、置换运算P,其流程如图所示。 (1)选择运算E与异或运算。选择运算E就是把Ri-1的32位扩展到48位,并与48位子秘钥Ki进行异或运算。具体扩展方式如图所示。 (2…

WordPres Bricks Builder 前台RCE漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

持续集成,持续交付和持续部署的概念,以及GitLab CI / CD的介绍

引言&#xff1a;上一期我们部署好了gitlab极狐网页版&#xff0c;今天我们介绍一下GitLabCI / CD 目录 一、为什么要 CI / CD 方法 1、持续集成 2、持续交付 3、持续部署 二、GitLab CI / CD简介 三、GitLab CI / CD 的工作原理 4、基本CI / CD工作流程 5、首次设置 …

视界未来:Sora领航AI视频模型的科技进步

随着人工智能技术的飞速发展&#xff0c;AI视频模型正逐渐成为视频内容创作、编辑和呈现的重要工具。在这个充满潜力的领域中&#xff0c;Sora作为一种领先的AI视频模型&#xff0c;正引领着科技的进步&#xff0c;并为我们带来了无限的想象空间。本文将探讨Sora在科技进步中的…

SpringBoot+PDF.js实现按需分片加载预览(包含可运行示例源码)

SpringBootPDF.js实现按需分片加载预览 前言分片加载的效果前端项目前端项目结构前端核心代码前端项目运行 后端项目后端项目结构后端核心代码后端项目运行 项目运行效果首次访问分片加载 项目源码 前言 本文的解决方案旨在解决大体积PDF在线浏览加载缓慢、影响用户体验的难题…

什么是IP地址,IP地址详解

在互联网的世界中&#xff0c;每一台连接的设备都需要一个独特的标识&#xff0c;这就是IP地址。IP地址&#xff0c;全称为“Internet Protocol Address”&#xff0c;即互联网协议地址&#xff0c;它是网络中进行数据传输的基础。下面&#xff0c;我们将对IP地址进行详细的解析…

幻兽帕鲁(Palworld 1.4.1)私有服务器搭建(docker版)

文章目录 说明客户端安装服务器部署1Panel安装和配置docker服务初始化设置设置开机自启动设置镜像加速 游戏服务端部署游戏服务端参数可视化配置 Palworld连接服务器问题总结 说明 服务器硬件要求&#xff1a;Linux系统/Window系统&#xff08;x86架构&#xff0c;armbian架构…

Vue+SpringBoot打造社区买菜系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、系统设计2.1 功能模块设计2.1.1 数据中心模块2.1.2 菜品分类模块2.1.3 菜品档案模块2.1.4 菜品订单模块2.1.5 菜品收藏模块2.1.6 收货地址模块 2.2 可行性分析2.3 用例分析2.4 实体类设计2.4.1 菜品分类模块2.4.2 菜品档案模块2.4.3…

在openEuler中通过KVM可视化安装华为FusionCompute的CNA主机

一、环境说明 在Windows物理主机上通过VMware WorkStation创建一个虚拟机&#xff08;4U4C、16GB内存&#xff0c;400GB磁盘&#xff0c;NAT网络连接&#xff09;&#xff0c;在虚拟机中安装openEuler 22.03 LTS系统&#xff0c;并将该虚拟机作为部署 FusionCompute的服务器&a…

maven3下载地址(含旧版本)

因为现有的3.8版本与IDEA不兼容&#xff0c;我需要下载3.6版本&#xff0c;但是官网的位置非常隐蔽&#xff0c;找了很多资料才看到。故记录一下。Index of /dist/maven/maven-3 选择需要的版本 选择binaries 选择zip文件下载就可以了

Modern C++ std::visit从实践到原理

前言 std::visit 是 C17 中引入的一个模板函数&#xff0c;它用于对给定的 variant、union 类型或任何其他兼容的类型执行一个访问者操作。这个函数为多种可能类型的值提供了一种统一的访问机制。使用 std::visit&#xff0c;你可以编写更通用和灵活的代码&#xff0c;而无需关…

静态时序分析:SDC约束命令set_input_transition详解

相关阅读 静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html DC工具在使用set_drive和set_driving_cell建模输入端口驱动能力时&#xff0c;会自动计算输入端口的转换时间&#xff0c;以及由于电阻或驱动单元带来的额外输入端口延迟。 set_input_tra…

Linux基础知识——命令行模式下命令的执行

文章目录 Linux基础知识——命令行模式下命令的执行开始执行Linux命令Linux基础命令的操作常用Linux命令行操作按键Linux输出错误信息查看 Linux系统在线帮助--help选项man命令info命令其他有用的文件文档百度搜索 文本编辑器&#xff1a;nanonano启动&#xff01; 正确关机方法…

Three.js加载PLY文件

这是官方的例子 three.js webgl - PLY 我在Vue3中使用&#xff0c;测试了好久始终不显示点云数据。在网上查询后发现ply文件要放置在public目录下才行 <el-row><el-button type"primary" class"el-btn" click"IniThree1">PLY</…

【ArcGIS微课1000例】0104:二位面状数据转三维多面体(建筑物按高度拉伸)

文章目录 一、加载数据二、添加高度字段三、三维拉伸显示四、生成三维体数据五、注意事项一、加载数据 打开ArcScene,加载配套实验数据(0104.rar中的二维建筑物矢量数据,订阅专栏,获取专栏所有文章阅读权限及配套数据),如下图所示: 二、添加高度字段 本实验将二维数据…

system_v

共享内存 创建共享内存 key_t ftok(const char *pathname, int proj_id);使用相同的pathname和proj_id可以得到相同的key_t int shmget(key_t key, size_t size, int shmflg);需要使用ftok获取key_t IPC_CREAT——不存在就创建&#xff0c;存在就获取 IPC_EXCL——一般和…

Android---PhotoViewer实现图片预览

PhotoViewer 图片查看器模仿微信朋友圈查看图片的效果。 在 Android 项目中引用 JitPack 库 AGP 8. 根目录的 settings.gradle dependencyResolutionManagement {...repositories {...maven { url https://jitpack.io }} } AGP 8. 根目录如果是 settings.gradle.kts 文件 d…

vue 中实现音视频播放进度条(满足常见开发需求)

由于开发需要&#xff0c;作者封装了一个音视频播放进度条的插件&#xff0c;支持 vue2 及 vue3 &#xff0c;有需要的朋友可联系作者&#xff0c;下面是对该款插件的介绍。 插件默认样式&#x1f447;&#xff08;插件提供了多个配置选项&#xff0c;可根据自身需求进行个性化…