linux进程间通讯--信号量

news2025/3/15 15:16:33

1.认识信号量

        方便理解:信号量就是一个计数器。当它大于0能用,小于等于0,用不了,这个值自己给。

2.特点:

  1. 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
  2. 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。(P操作:拿锁。V操作:放回锁)
  3. 每次对信号量的 PV 操作不仅限于对信号量值加 1 或 减1 ,而且可以加加减任意正整数。
  4. 支持信号量组

3.有关api:

#include <sys/sem.h>
 
//1.创建或获取一个信号量组:若成功返回信号量级ID,失败返回-1
int semget(key_t key, int num_sems, int sem_flags);
 
//2.对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
int semop(int semid, struct sembuf semoparray[], size_t numops);
 
//3.控制信号量的相关信息
int semctl(int semid, int sem_num, int cmd, ...);

1.semget

功能:创建一个新的信号量或获取一个已经存在的信号量的键值。

返回值:成功返回信号量的标识码ID。失败返回-1;

参数:

key  为整型值,用户可以自己设定。有两种情况:

1.       键值是IPC_PRIVATE,该值通常为0,意思就是创建一个仅能被进程进程给我的信号量。

2.       键值不是IPC_PRIVATE,我们可以指定键值,例如1234;也可以一个ftok()函数来取得一个唯一的键值。

sems 表示初始化信号量的个数。比如我们要创建一个信号量,则该值为1.,创建2个就是2。

flags  :信号量的创建方式或权限。有IPC_CREAT,IPC_EXCL。

IPC_CREAT如果信号量不存在,则创建一个信号量,否则获取。

IPC_EXCL只有信号量不存在的时候,新的信号量才建立,否则就产生错误。

2.semop

//2.对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
int semop(int semid, struct sembuf semoparray[], size_t numops);

该函数的主要作用是执行对一个或多个信号量的原子操作,包括P操作(sem减1)和V操作(sem加1),以实现进程间的同步与互斥。

其中,参数semid为信号量集的标识符;参数sops指向进行操作的结构体数组的首地址;参数nsops指出将要进行操作的信号的个数。

需要注意的是,信号量的值只能通过PV操作来改变。当信号量的值大于0时,表示当前有可用资源,进程可以继续执行;若信号量的值小于等于0,则表示无可用资源,进程需要暂停等待。

另外,semop函数调用成功返回0,失败返回-1。因此在使用该函数时,需要进行错误检查以确保操作的正确性。

具体参数解释:

  _semid : 信号量的标识码。也就是semget()的返回值。

 _sembuf是一个指向结构体数组的指针。

1 struct sembuf 
2 {
3     short sem_num; // 信号量组中对应的序号,0~sem_nums-1
4     short sem_op;  // 信号量值在一次操作中的改变量
5     short sem_flg; // IPC_NOWAIT, SEM_UNDO
6 }

sembuf解释:

1.em_num:  操作信号在信号集中的编号。第一个信号的编号为0;

2.sem_op : 如果其值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;如果sem_op的值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用权;如果sem_op的值为0,则操作将暂时阻塞,直到信号的值变为0。

3.semflg

IPC_NOWAIT //对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息。

IPC_UNDO //程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值。这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。

_nsops:操作结构的数量,恒大于或等于1。

//3.控制信号量的相关信息
int semctl(int semid, int sem_num, int cmd, ...);

3.semctl

  1. semid:表示要操作的信号量集的标识符,通常由 semget() 函数返回。
  2. semnum:表示要操作的信号量的索引号,这里设置为 0,表示对第一个信号量进行操作。
  3. cmd    命令,表示要进行的操作

       下面列出的这些命令来源于百度!

        参数cmd中可以使用的命令如下:

  •         IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
  •         IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
  •         IPC_RMID将信号量集从内存中删除
  •         GETALL用于读取信号量集中的所有信号量的值。
  •         GETNCNT返回正在等待资源的进程数目。
  •         GETPID返回最后一个执行semop操作的进程的PID。
  •         GETVAL返回信号量集中的一个单个的信号量的值。
  •         GETZCNT返回这在等待完全空闲的资源的进程数目。
  •         SETALL设置信号量集中的所有的信号量的值。
  •         SETVAL设置信号量集中的一个单独的信号量的值。
    通常为标黄的命令

如果有第四个参数,它通常是一个union semum结构,定义如下:

union semun {
    int val;          // 用于SETVAL命令,表示要设置的信号量的值。
    struct semid_ds *buf; // 用于IPC_STAT、IPC_SET命令,指向一个semid_ds结构体的指针。
    unsigned short *array; // 用于GETALL、SETALL命令,指向一个无符号短整型数组的指针。
    struct seminfo *__buf; // 用于IPC_INFO命令,指向一个seminfo结构体的指针(Linux-specific)。
};

前两个参数与前面一个函数中的一样,command通常是下面两个值中的其中一个
SETVAL:用来把信号量初始化为一个已知的值。这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。

IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。

4.代码例子

使用信号量实现父子进程先后进行。

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


/*
//1.创建或获取一个信号量组:若成功返回信号量级ID,失败返回-1
int semget(key_t key, int num_sems, int sem_flags);

//2.对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
int semop(int semid, struct sembuf semoparray[], size_t numops);

//3.控制信号量的相关信息
int semctl(int semid, int sem_num, int cmd, ...);
 */




// union for semctl to initialize
union semun {
	int val;          // 用于SETVAL命令,表示要设置的信号量的值。
	struct semid_ds *buf; // 用于IPC_STAT、IPC_SET命令,指向一个semid_ds结构体的指针。
	unsigned short *array; // 用于GETALL、SETALL命令,指向一个无符号短整型数组的指针。
	struct seminfo *__buf; // 用于IPC_INFO命令,指向一个seminfo结构体的指针(Linux-specific)。
};


//initialize sem to 0

int init_sem(int sem_id,int value){


	union semun tmp;
	tmp.val = value;

	if(semctl(sem_id,0,SETVAL,tmp)==-1){
		perror("init sem erro");

		return -1;

	}

	return 0;
}


//p operate
// if sem == 1,get message to sem =-1
//if sem == 0, process wait

int sem_p(int sem_id){


	struct sembuf pbuf;
	pbuf.sem_num  = 0;
	pbuf.sem_op   = -1;
	pbuf.sem_flg  = SEM_UNDO;

	if(semop(sem_id,&pbuf,1) == -1){

		perror("p operate error");


		return -1;
	}



	return 0;
}


//v operate
//sem >1 operate


int sem_v(int sem_id){


	struct sembuf vbuf;
	vbuf.sem_num  = 0;
	vbuf.sem_op   = 1;//v operate
	vbuf.sem_flg  = SEM_UNDO;

	if(semop(sem_id,&vbuf,1) == -1){

		perror("v operate error");


		return -1;
	}



	return 0;
}

//delete sem

int del_sem(int sem_id){

	union semun tmp;

	if(semctl(sem_id,0,IPC_RMID) == -1){

		perror("delete sem error");

		return -1;

	}



	return 0;
}

int main()
{
	int  sem_id;
	key_t key;
	pid_t pid;


	//get key num

	if((key = ftok(".",11))<0){

		perror("semget error");
		exit(-1);

	}


	//build sem only 1

	if(sem_id = semget(key,1,IPC_CREAT|0700)==-1){

		perror("semget error");

		exit(-1);

	}



	//initilize

	init_sem(sem_id,0);

	//father&child process

	if((pid = fork())== -1){

		perror("fork errror");
		exit(-1);

	}else if(pid == 0){   //child process

		sleep(1);
		printf("process from child pid :%d\n",getpid());

		sem_v(sem_id);//release resource &put tne lock


	}else {  //father process

		sem_p(sem_id);
		printf("father process pid:%d\n",getpid());

		sem_v(sem_id);
		del_sem(sem_id);


	}





	return 0;
}

突然意识到结构体和联合体是不同的,请看文章:

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

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

相关文章

GitHub仓库的README文件无法显示图片问题-非域名污染原因

之前上自己仓库就偶然发现图片不显示现象&#xff0c;当时以为是网络问题就没有留意这事。但是一直不显示就有问题了&#xff01;于是网上搜了一遭&#xff0c;看见大家遇到此现象的原因普遍归于DNS污染1而我的问题原来是MarkDown格式&#xff01; 在图片语法前不要加分区语法…

linux部署gitlab

1. 配置yum源&#xff1a; vim /etc/yum.repos.d/gitlab-ce.repo [gitlab-ce] nameGitlab CE Repository baseurlhttps://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el$releasever/ gpgcheck0 enabled1 2. 更新本地缓存 sudo yum install -y gitlab-ce 3. 安装相关依赖 yum …

RT-Thread学习笔记(三):线程管理

线程管理 线程管理相关概念什么是时间片轮转调度器锁线程运行机制线程的五种状态 动态和静态创建线程区别动态和静态创建线程优缺点RT-Thread动态线程管理函数动态创建线程动态删除线程 RT-Thread静态线程管理函数静态创建线程 线程其他操作线程启动线程延时获得当前执行的线程…

基于nodejs+vue小型企业银行账目管理系统

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

九阳真经之各大厂校招

大学计算机系的同学要怎么努力才能校招进大厂? 秋招的大公司非常多&#xff0c;也是非常好的&#xff0c;赶上了秋招&#xff0c;你基本工作就敲定了&#xff0c;在整个应届毕业生的人群中你就占据很大的优势了。 如何准备应届校招&#xff1f; 一、做好规划&#xff0c;把…

Python 网络爬虫

爬虫原理 计算机一次Request请求和服务器端的Response回应&#xff0c;即实现了网络连接。 爬虫需要做两件事&#xff1a;模拟计算机对服务器发起Request请求。 接受服务器的Response内容并解析、提取所需的信息。 多页面爬虫流程 ​​​​​​​多页面网页爬虫流程

Map中key和value值是否可以为null或空字符串?

Map中key和value值是否可以为null或空字符串? 答案&#xff1a; HashMap既支持分别为空/null&#xff0c;也支持key和value同时为空/nullHashtable不支持key和value存储null&#xff0c;但支持存空字符串** HashMap HashMap是中支持空键和空值的&#xff0c;不论存入null或…

python每日一练(9)

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

右值引用+移动语义

目录 右值引用 引入 介绍 左值 左值引用 左值引用的缺陷 引入 缺陷 解决 右值 纯右值 将亡值 右值引用 move函数 介绍 底层实现 参数 -- 通用引用类型 引用折叠 折叠规则: 返回值 remove_reference 移动 引入 介绍 移动构造函数 介绍 是否抛出异常…

SpringBoot+SpringSecurity项目的创建

首先创建一个springboot项目&#xff0c;注意springboot要求jdk版本在17以上 等项目构建完成之后&#xff0c;在pom文件中加入SpringSecurity的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-securi…

Unity之ShaderGraph如何实现边缘光效果

前言 游戏中最常用的一个效果就是边缘光了&#xff0c;既可以做物体的高亮效果&#xff0c;也可以给人物皮肤表面添加光泽。如下图所示&#xff1a; 实现原理 边缘光的原理就是Fresnel节点的底层逻辑。 Fresnel&#xff1a;菲涅尔效应是根据视角不同表面上反射率不同的效应…

MIT6.5830 Lab0-Go tutorial实验记录(三)

MIT6.5830 Lab0-Go tutorial实验记录&#xff08;三&#xff09; – WhiteNights Site 标签&#xff1a;Golang 在前面两次实验记录的铺垫&#xff0c;是时候完成第一项任务了。 实验步骤 补全handlers.go中缺失的代码 先来看第一个部分&#xff0c;从RidershipDB获取图像数据…

老师如何发布考试成绩?

成绩查询页面是什么&#xff1f;如何用各种代码、Excel来实现让学生自助查询成绩&#xff1f; 作为老师&#xff0c;发布考试成绩是教学过程中的一个重要环节。传统的做法是&#xff0c;老师手动计算每个学生的分数&#xff0c;然后将成绩单打印出来并逐个发放给学生。这种方式…

RISCV——RV32I指令集详述

一、指令编码 6种指令格式 40条基本整数指令 二、指令实现 load/store 参考链接&#xff1a;riscv load/store

【mysql】关于mysql的数据结构特点 索引特点

文章目录 二叉树红黑树 b treehash结构b tree索引存放特点myisamInnoDB 最左原则主键相关知识点缓存池淘汰机制 前言&#xff1a;翻自己博客 发现缺少mysql数据结构和索引相关内容 两年前整理的mysql知识点 一直存在于博主的笔记本里面 &#xff08;是的 纸质的那种笔记本 不是…

python:代码加密流水线

通过以下五步&#xff0c;将python代码加密&#xff0c;且只能在指定电脑上运行。 三层加密。只加密核心函数。将参数暴露在外。都可以实现。 文章目录 一、获取机器码二、加密机器码三、将加密机器码写入ism函数中四、加密代码&#xff08;可有可无&#xff09;1.使用[https:…

图像特征之SIFT

SIFT介绍 尺度不变特征转换即SIFT (Scale-invariant feature transform)是一种计算机视觉的算法。它用来侦测与描述影像中的局部性特征&#xff0c;它在空间尺度中寻找极值点&#xff0c;并提取出其位置、尺度、旋转不变量&#xff0c;此算法由 David Lowe在1999年所发表&#…

学习mapster的基本用法

正在学习的开源博客项目Blog .NET Core中采用mapster实现对象映射&#xff0c;个人理解对象映射框架主要用于不同类型间的数据转换&#xff0c;比起个人实现的定制化的类型对类型的转换代码&#xff0c;采用对象映射框架更便捷&#xff0c;同时也能支撑各式各样的对象映射场景。…

List集合拆分为多个List

list 拆分 目录概述需求&#xff1a; 设计思路实现思路分析1.list 拆分是2.用stream的parallel实现list的分批处理3.使用stream的parallel实现list的分批处理 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full bus…

【LeetCode】33. 搜索旋转排序数组

1 问题 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nums…