Linux下使用信号量实现PV操作

news2024/11/15 15:28:03

 一.信号量与PV操作概述

在多道程序系统中,由于资源共享与进程合作,使各进程之间可能产生两种形式的制约关系,一种是间接相互制约,例如,在仅有一台打印机的系统,同一时刻只能有一个进程分配到到打印机,其他进程必须阻塞;另一种是直接相互制约,例如进程A通过单缓冲去向进程B提供数据,当改缓冲区为空时,进程B不能获取所需的数据而阻塞,一旦进程A将数据送入缓冲区,进程B就被唤醒,反之,当缓冲区满时,进程A被阻塞,仅当进程B取走缓冲区的数据时,才唤醒进程A。

进程同步主要源于进程合作,是进程之间共同完成一项任务时直接发生相互作用的关系,为进程之间的直接制约关系。

进程互斥主要源于资源共享是进程之间的间接制约关系。在多道程序系统中,每次只允许一个进程访问的资源称为临界资源,进程互斥要求保证每次只有一个进程使用临界资源。在每个进程中访问临界资源的程序段称为临界区,进程进入临界区要满足一定的条件,以保证临界资源的安全使用和系统的正常运行。

PV操作是对信号量进行处理的操作过程,而且信号量只能由PV操作来改变。P操作是对信号量减去1,意味着请求系统分配一个单位资源,若系统无可用资源,则进程变为阻塞状态;V操作是对信号量加1,意味着释放一个资源,加1后若信号量小于等于0,则从就绪队列中唤醒一个进程,执行V操作的进程继续执行。

二.Linux系统下程序示例

 在Linux系统中,通常调用semget,semctl,semop这些API来实现。

首先,需要调用 semget函数创建信号量或获得在系统中已存在的信号量。不同进程通过使用同一个信号量键值来获得同一个信号量。其次,使用 semctl函数的SETVAL操作来初始化信号量,此时。当使用二维信号量时,通常将信号量初始化为1。接下来就是调用 semop函数进行信号量的PV操作。最后如果不需要信号量,则使用semctl函数的 IPC_RMID操作从系统中删除它。

在这里以Linux下的C程序与例子,简单验证下使用信号量实现PV操作的原理。

1.在Linux下创建一个名为pv_test的工程目录。

2.创建一个sem_pv.c的文件,写入以下代码。

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdarg.h>
#include <time.h>

#define SEM_P -1
#define SEM_V 1

#if !(defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED))
//如果系统中没有定义这个联合体,则需要额外定义一下
union semun {
    int val;                             
    struct semid_ds *buf;    
    unsigned short int *array; 
    struct seminfo *__buf;  
};
#endif

//获取时间戳
int get_timestamp(char *timestamp)
{
	time_t timep;
	struct tm *p, pp;
	time(&timep);
	localtime_r(&timep, &pp);
	p = &pp;
	return sprintf(timestamp, "%04d-%02d-%02d %02d:%02d:%02d", p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
	
}

/*
日志打印
*/
void LOG(const char *format,...)
{
        va_list argptr;
        char buffer[2048];
        va_start(argptr,format);
        vsprintf(buffer,format,argptr);
        va_end(argptr);

	char timestamp[64]="";
	get_timestamp(timestamp);
		
        printf("[%s][%d]%s", timestamp, getpid(), buffer);
}

int sem_init(int semid) 
{
	int ret;
	union semun semun;
	semun.val = 0;
	ret = semctl(semid, 0, SETVAL, semun);
	if (ret == -1) {
		LOG("semctl failed!\n");
	}
	
	return ret;
}

int semop_pv(int semid, int opt)
{
	int ret;
	struct sembuf sembuf;
	sembuf.sem_op = opt;
	sembuf.sem_num = 0;//初始化为0,表示暂时没资源
	sembuf.sem_flg = SEM_UNDO;
	ret = semop(semid, &sembuf, 1);	
	if (ret == -1) {
		LOG("sem_p failed!\n");
	}
	return ret;
}

/*
模拟P操作
*/
 int  semop_p(int semid)
{
	//p操作 对信号量进行减1
	LOG("semctl P\n");
	return semop_pv(semid, SEM_P);
}

/*
模拟V操作
*/
 int  semop_v(int semid)
{
	//v操作 对信号量进行加1
	LOG("semctl V\n");
	return semop_pv(semid, SEM_V);
}

/*
删除信号量
*/
void sem_del(int semid)
{
	union semun sem_un;
	if(semctl(semid, 0, IPC_RMID, sem_un)<0) {
		LOG("sem_del fail\n");
	}
}

 3.创建一个名为“main.c”的文件,写入以下代码:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <time.h>

#define TEST_FILE "./test"

int file_write()
{
	char timestamp[64]="";
	FILE *fp = fopen(TEST_FILE, "ab+");
	if(!fp) {
		LOG("[%s](%d)open %s failed!\n", __func__, __LINE__, TEST_FILE);
		return -1;
	}
	get_timestamp(timestamp);
//将进程号写入文件
	fprintf(fp, "[%s]Process (%d)write!!!\n", timestamp, getpid());
	fclose(fp);
	return 0;
}

int main(int argc, char* argv[]) 
{
	int i;
	int ret;
	int semid;
	int timeout = 5;//测试写文件操作5次
	int sleep_second = 1;

	/* 获取信号量  键值设置为6666, 
	1表示信号量的数量, 
	后面表示如果没有这个信号量就创建而且设置为都可以访问的权限*/
	semid = semget((key_t)6666, 1, 0666 | IPC_CREAT);
	/*如果没有创建成功就会返回-1 然后打印失败的信息*/
	LOG("semid=%d\n", semid);
	if (semid == -1) {
		LOG("semget failed!\n");
		exit(1);
	}

	/* 初始化信号量 */
	ret = sem_init(semid);
	if (ret == -1) {
		exit(1);
	}

	if(argc<2) {
		LOG("Input error!!!\n");
		exit(0);
	}
	int param = atoi(argv[1]);
	if(param==1) {
//参数为1的进程进行V操作
		if (semop_v(semid) == -1) {
			exit(1);
		}
		sleep(1);
	}
	else {
		unlink(TEST_FILE);
	}
	while(timeout--) {
//刚开始
		if (semop_p(semid) == -1) {
			exit(1);
		}
		
		/* 临界区操作*/
		LOG("begin write\n");
		file_write();
		sleep(sleep_second);
		LOG("write done!\n");
          
		if (semop_v(semid) == -1) {
			exit(1);
		}
	}

	/* 删除信号量 */
	if(param==1) {
		sem_del(semid);
	}
	return 0;
}

 4.创建一个Makefile文件,写入以下内容:

CPROG	= pv_test
BIN     = $(CPROG) 
CC= gcc
OBJS=main.o sem_pv.o

all: $(BIN) 
clean:
	rm -f $(OBJS) $(BIN)
$(BIN): $(OBJS)
	$(CC)  -o $(BIN) $(OBJS)   $(CFLAGS) $(LDFLAGS) $(CFLAGS_EXTRA) 

5.在当前目录下执行make clean;make编译生成一个pv_test可执行文件

6.测试。运行时,使用参数0和1来区分不同的进程。

 

查看写入的文件,可以看到两个进程交替写文件,如下所示:

总结:

信号量不仅是一种进程之间的通信机制,与此同时,信号量的使用,可以有效的解决进程间的同步与互斥问题。信号量的PV操作是实现进程间的同步和互斥的核心工作部分。

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

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

相关文章

Axure9知识点复盘

axure9 Axure有四种类型文件&#xff1a; 团队文件示例.rpteam 元件库文件示例.rplib 原型导出文件示例.html 原型文件示例.rp 将反复被调用都模板作成母版 鼠标和键盘的交互 形状交互 登录页面的实现 动态面版滚动条的使用 如何在Axure中动态加载图表&#xff08;Axh…

H5适配iOS顶部和底部安全区域

在移动端Web开发中&#xff0c;适配不同设备的屏幕是一个重要且挑战性的任务。对于iOS设备来说&#xff0c;这一任务尤为关键&#xff0c;因为自iPhone X起&#xff0c;苹果的设备引入了刘海屏、圆角等设计&#xff0c;这就要求开发者在Web页面中特别处理顶部和底部的安全区域。…

在RunnerGo测试平台中做WebSocket、Dubbo、TCP/IP接口测试

大家好&#xff0c;RunnerGo作为一款一站式测试平台不断为用户提供更好的使用体验&#xff0c;最近得知RunnerGo新增对&#xff0c;WebSocket、Dubbo、TCP/IP&#xff0c;三种协议API的测试支持&#xff0c;本篇文章跟大家分享一下使用方法。 WebSocket协议 WebSocket 是一种…

用 CanvasKit 实现超级丝滑的原神地图(已开源)!!!

首先给大家送上预览地址&#xff1a; 官网地址&#xff1a;https://webstatic.mihoyo.com/ys/app/interactive-map/index.html canvaskit地址&#xff1a;http://106.55.55.247/ky-genshin-map/ 为什么 canvaskit 有如此高的性能&#xff1f; 第一个问题&#xff0c;官方网页…

万户 ezOFFICE DocumentEditExcel.jsp SQL注入漏洞

0x01 产品简介 万户OA ezoffice是万户网络协同办公产品多年来一直将主要精力致力于中高端市场的一款OA协同办公软件产品,统一的基础管理平台,实现用户数据统一管理、权限统一分配、身份统一认证。统一规划门户网站群和协同办公平台,将外网信息维护、客户服务、互动交流和日…

day38_MySQL

今日内容 0 复习昨日 1 引言 2 数据库 3 数据库管理系统 4 MySQL 5 SQL语言 0 复习昨日 1 引言 1.1 现有的数据存储方式有哪些&#xff1f; Java程序存储数据&#xff08;变量、对象、数组、集合&#xff09;&#xff0c;数据保存在内存中&#xff0c;属于瞬时状态存储。文件&…

D4890——单通道 BTL 音频功率放大器电路,采用SOP8/MSOP8封装形式,无需输出耦合电容、缓冲网络或自举电容

D4890是一个AB类音频功率放大器专为移动电话MID和其他便携式通信设备。它能够从5Vn的电源以小于1%的失真(THDN) 传输1.1wts到8QBlo d。专为提供高品质的输出功率而设计&#xff0c;只需最少的外部元件。它不需要输出耦合电容或自举电容。和超低关断电流&#xff0c;D4890非常适…

DataCanvas会员中心正式上线,这些新春福利请接住!

重大消息&#xff1a;“九章云极DataCanvas智能研究院”服务号会员中心正式上线了 &#xff01;注册成为DataCanvas会员&#xff0c;接好这些新春福利&#xff01; 新岁将至&#xff0c;福启九章&#xff0c;作为集智库、服务、干货分享、互动交流于一体的用户综合服务平台&am…

对象原型和原型对象

在浏览器中显示的[[Prototype]]实际上就是__proto__,是对象原型&#xff0c;可以被实例访问。 prototype是构造函数的属性&#xff0c;__proto__是实例的属性。有点绕口。

浅析云性能监控的重要性及核心功能

随着企业日益依赖云计算服务&#xff0c;云性能监控变得至关重要。云性能监控是一种实时监测、分析和报告云基础设施及应用程序性能的方法。本文将深入探讨云性能监控的目的、重要性以及其核心功能&#xff0c;以帮助企业更好地理解和实施这一关键的运维实践。 一、云性能监控的…

AI算力专题:AI时代领先者,大装置+大模型推动AGI落地

今天分享的是AI算力系列深度研究报告&#xff1a;《AI算力专题&#xff1a;AI时代领先者&#xff0c;大装置大模型推动AGI落地》。 &#xff08;报告出品方&#xff1a;中银证券&#xff09; 报告共计&#xff1a;28页 四核驱动引领智慧科技新潮流 商汤是一家行业领先的人工…

C++ 数论相关题目 博弈论 Nim游戏

给定 n 堆石子&#xff0c;两位玩家轮流操作&#xff0c;每次操作可以从任意一堆石子中拿走任意数量的石子&#xff08;可以拿完&#xff0c;但不能不拿&#xff09;&#xff0c;最后无法进行操作的人视为失败。 问如果两人都采用最优策略&#xff0c;先手是否必胜。 输入格式…

惬意上手python —— python中的术语及案例解析

面向对象编程 面向对象编程&#xff08;Object-Oriented Programming&#xff0c;OOP&#xff09;是一种编程范式&#xff0c;它将数据和操作数据的方法封装在一起&#xff0c;以对象的形式表示。在Python中&#xff0c;一切皆为对象&#xff0c;因此Python是一种面向对象的语…

【开源】SpringBoot框架开发天然气工程运维系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统角色分类2.2 核心功能2.2.1 流程 12.2.2 流程 22.3 各角色功能2.3.1 系统管理员功能2.3.2 用户服务部功能2.3.3 分公司&#xff08;施工单位&#xff09;功能2.3.3.1 技术员角色功能2.3.3.2 材料员角色功能 2.3.4 安…

带自执行安装脚本的ROS包的生成

带自执行安装脚本的ROS包的生成 在打包和安装ROS包时, 会有一些固定的配置需要去人为实现, 比如网络配置, 设备树的管理等, 比较麻烦, 不如一次性解决掉, 所以查了相关文档 过程: # 使用bloom-generate rosdebian生成debian文件夹 bloom-generate rosdebian # 进入debian文件…

Unix/Linux上的五种IO模型

a.阻塞 blocking 调用者调用了某个函数&#xff0c;等待这个函数返回&#xff0c;期间什么也不做&#xff0c;不停的去检查这个函数有没有返回&#xff0c;必须等这个函数返回才能进行下一步动作。 注意&#xff1a;阻塞并不是函数的行为&#xff0c;而是跟文件描述符有关。通…

法律视角下的数据出境《2023年数据出境合规年鉴》

关注国际云安全联盟CSA公众号&#xff0c;回复关键词“数据安全”获取报告 在全球数字产业以及大数据和云计算技术快速发展的背景下&#xff0c;数据流动对世界经济的影响日益显著。由此带来的数据红利和数据安全之间的冲突&#xff0c;将对未来数字经济的发展方向产生深刻影响…

如何使用wireshark解析二进制文件

目录 目录 1.将已有的packet raw data按照下面格式写入文本文件中 a. Raw IP packet b. Ethernet packet 2.用wiershark导入hex文件 3.设置对应的packet类型 a. Raw IP packet b. Ethernet packet 1.将已有的packet raw data按照下面格式写入文本文件中 a. Raw IP pac…

C++——特殊类

特殊类 文章目录 特殊类一、请设计一个类&#xff0c;不能被拷贝二、请设计一个类&#xff0c;只能在堆上创建对象方案一&#xff1a;析构函数私有化方案二&#xff1a;构造函数私有化 三、请设计一个类&#xff0c;只能在栈上创建对象四、请设计一个类&#xff0c;不能被继承五…

交叉注意力融合2024创新方案汇总,附配套模块和代码

多模态学习和注意力机制是当前深度学习研究的热点领域之一&#xff0c;而交叉注意力融合作为这两个领域的结合点&#xff0c;具有很大的发展空间和创新机会。 作为多模态融合的一个重要组成部分&#xff0c;交叉注意力融合通过注意力机制在不同模块之间建立联系&#xff0c;促…