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

 一.信号量与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操作是实现进程间的同步和互斥的核心工作部分。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python算法题集_最大子数组和

本文为Python算法题集之一的代码示例 题目53&#xff1a;最大子数组和 说明&#xff1a;给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。…

实战教程:使用Spring Boot和Vue.js开发社区团购管理系统

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

本地配置Joplin Server用于Joplin笔记同步并实现公网远程访问

文章目录 1. 安装Docker2. 自建Joplin服务器3. 搭建Joplin Sever4. 安装cpolar内网穿透5. 创建远程连接的固定公网地址 Joplin 是一个开源的笔记工具&#xff0c;拥有 Windows/macOS/Linux/iOS/Android/Terminal 版本的客户端。多端同步功能是笔记工具最重要的功能&#xff0c;…

关于JavaWeb的不使用模板创建方法

首先说一个特别重要的 , pom配置文件写完他不自动更新 就得这么办 , 不更新 idea就无法识别javaweb项目很烦

ASTORS国土安全奖:ManageEngine AD360荣获银奖

美国安全今日&#xff08;AST&#xff09;的年度“ASTORS”国土安全奖计划是一个备受瞩目的活动&#xff0c;致力于突显国土安全领域的创新与进步。这一奖项旨在表彰在保护国家免受安全威胁方面做出卓越贡献的个人和组织。该计划汇聚了执法、公共安全和行业领袖&#xff0c;不仅…

c4d线条放样模型的教程

c4d放样工具怎么使用&#xff1f;c4d中想要将线条放样成三维模型&#xff0c;该怎么放样呢&#xff1f;下面我们就来看看c4d线条放样模型的教程&#xff0c;需要的朋友可以参考下 c4d绘制的线条图形&#xff0c;想要放样成三维模型&#xff0c;该怎么放样呢&#xff1f;下面我…

谷歌上架防关联VPS开到和原来一样的IP造成关联?应该怎么选?

随着Google paly的发展&#xff0c;竞争越来越激烈&#xff0c;开发者们也面临的越来越多的挑战。其中&#xff0c;如何降低关联风险是开发者们重点关注的问题。 为了防止开发者账号的滥用或欺诈&#xff0c;谷歌会通过判断账号之间是否存在关联&#xff0c;并对违规账号进行处…

【Axure教程0基础入门】00Axure9汉化版下载、安装、汉化、注册+01制作线框图

写在前面&#xff1a;在哔哩哔哩上面找到的Axure自学教程0基础入门课程&#xff0c;播放量最高&#xff0c;5个多小时。课程主要分为4个部分&#xff0c;快速入门、动态面板、常用动效、项目设计。UP主账号【Song老师产品经理课堂】。做个有素质的白嫖er&#xff0c;一键三连必…

Qt QWidget Loading界面并覆盖在其他控件上面

目录 一、效果图二、Loading三、使用 一、效果图 界面中有一个Label&#xff0c;一个Button 点击Buttion&#xff0c;显示Loading的界面&#xff0c;并覆盖到Label和Button上面 二、Loading loadingwidget.h #ifndef LOADINGWIDGET_H #define LOADINGWIDGET_H#include <…

Python中类的相关术语(附带案例)

目录 1、面向对象 2、类 3、实例 4、初始化方法 5、魔法方法 6、字符串方法 7、self 8、数据、属性、操作、行为 9、父类、基类、超类 or 子类、派生类 10、多态 11、重载多态 and 重写多态 12、名称解释 1、面向对象 在Python中&#xff0c;面向对象编程&…

哪款洗地机好用?2024年洗地机推荐

家居清洁工作却是一项既重要又耗时的任务&#xff0c;尤其是地面的清扫工作&#xff0c;既要保证清洁&#xff0c;又要尽量减少对地板的磨损。吸拖一体的洗地机出现&#xff0c;让全职妈妈、忙碌的上班族以及健康状况欠佳的长者都能从繁重的家务活中解脱出来&#xff0c;享受自…

让MySQL和Redis数据保持一致的4种策略

1 前言 先阐明一下 MySQL 和 Redis 的关系&#xff1a;MySQL 是数据库&#xff0c;用来持久化数据&#xff0c;一定程度上保证数据的可靠性&#xff1b;Redis 是用来当缓存&#xff0c;用来提升数据访问的性能。 关于如何保证 MySQL 和 Redis 中的数据一致&#xff08;即缓存…

[服务器]ESXi 8安装centos7

文章目录 创建虚拟机创建虚拟机选择centos7选择存储选择镜像文件上传ios镜像文件 安装即将完成 启动虚拟机自动获取ip设置root密码安装成功 创建虚拟机 创建虚拟机 选择centos7 选择存储 选择镜像文件 上传ios镜像文件 如图显示上传进度&#xff0c;上传完毕之后&#xff0c;将…

CleanMyMac X.4.14.6中文版新功能介绍,mac系统垃圾清理

近些年伴随着苹果生态的蓬勃发展&#xff0c;越来越多的用户开始尝试接触Mac电脑。然而很多人上手Mac后会发现&#xff0c;它的使用逻辑与Windows存在很多不同&#xff0c;而且随着使用时间的增加&#xff0c;一些奇奇怪怪的文件也会占据有限的磁盘空间&#xff0c;进而影响使用…

AIGC项目——Meta:根据对话音频生成带动作和手势的3d逼真数字人

From Audio to Photoreal Embodiment: Synthesizing Humans in Conversations From Audio to Photoreal Embodiment:Synthesizing Humans in Conversations 从二元对话的音频中&#xff0c;我们生成相应的逼真的面部、身体和手势。 概括性:角色是由作者的声音驱动的(而不是模…

flink cdc,standalone模式下,任务运行一段时间taskmanager挂掉

在使用flink cdc&#xff0c;配置任务运行&#xff0c;过了几天后&#xff0c;任务无故取消&#xff0c;超时&#xff0c;导致taskmanager挂掉&#xff0c;相关异常如下&#xff1a; 异常1&#xff1a; did not react to cancelling signal interrupting; it is stuck for 30 s…

(1)SpringBoot学习——芋道源码

Spring Boot 的快速入门 一.、概述 使用 Spring Boot 可以很容易地创建出能直接运行的独立的、生产级别的基于 Spring 的应用。 二、快速入门 2.1 创建 Maven 项目 打开 IDEA&#xff0c;点击菜单 File -> New -> Project.来创建项目选择 Maven 类型&#xff0c;点击「…

Kotlin:用源码来深入理解 ‘StateFlow和SharedFlow的区别和联系‘

Kotlin&#xff1a;用源码来深入理解 ‘StateFlow和SharedFlow的区别和联系’ 在这篇文章中&#xff0c;我们将深入研究Kotlin中的StateFlow和SharedFlow&#xff0c;以及它们的相似之处和不同之处。我们将通过查看它们的源代码来理解它们的工作原理&#xff0c;这将帮助我们更…

大数据 - Spark系列《二》- 关于Spark在Idea中的一些常用配置

上一篇&#xff1a; 大数据 - Spark系列《一》- 从Hadoop到Spark&#xff1a;大数据计算引擎的演进-CSDN博客 目录 1. &#x1f959;Idea中配置Live Templates来快速生成代码片段 2. &#x1f959;Idea中配置文件模板自定义初始代码 3.&#x1f959;设置spark-submit提交程…

刨析数据结构(一)

&#x1f308;个人主页&#xff1a;小田爱学编程 &#x1f525; 系列专栏&#xff1a;数据结构————"带你无脑刨析" &#x1f3c6;&#x1f3c6;关注博主&#xff0c;随时获取更多关于数据结构的优质内容&#xff01;&#x1f3c6;&#x1f3c6; &#x1f600;欢迎…