Linux进阶-ipc信号量

news2025/1/24 14:48:44

目录

system-V IPC信号量

semget():创建或获取信号量

semop():PV操作

semctl():信号量集的一系列控制操作

sem.h文件

sem.c文件

main.c文件

Makefile文件

执行过程


system-V IPC信号量

本质上是一个计数器,用于协调多进程间对共享数据对象的读取,主要用来保护共享资源,使得该共享资源在一个时刻只有一个进程独享。

信号量只能进行两种操作:P(锁,申请资源)V(解锁,释放资源)

P操作:

        如果有可用的资源(信号量值大于0),则申请一个资源(信号量值减1,进入临界区代码);

        如果没有可用的资源(信号量值等于0),则阻塞,直到系统将资源分配给该进程(进入等待队列,一直等到资源轮到该进程)。

V操作:

        如果在该信号量的等待队列中有进程在等待资源,则唤醒一个阻塞的进程。

        如果在该信号量的等待队列中没有进程在等待资源,则释放一个资源(信号量值加1)。

在信号量进行PV操作时都为原子操作(因为它需要保护临界资源)。

        原子操作:单指令的操作称为原子的,单条指令的执行是不会被打断的。

semget():创建或获取信号量

semget()函数是创建或获取一个已经创建的信号量。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
/*
key:表示系统内的信号量。如果key已存在,nsems参数指定为0,semflg参数也指定为0。可以使用IPC_PRIVATE创建一个没有key的信号量
nsems:用于创建信号量时,表示可用的信号量数目。
semflg:指定标志位和mode。
    IPC_CREAT:创建新的信号量,即使信号量已经存在也不会出错。
    IPC_CREAT | IPC_EXCL:创建一个新的唯一的信号量,如果已经存在会返回报错
返回值:
    执行成功:信号量标识符
    执行失败:-1
*/

创建信号量时,受到一下系统信息的影响:

        SEMMNI:系统中信号量总数的最大值。

        SEMMSL:每个信号量中信号量元素个数的最大值。

        SEMMNS:系统中所有信号量中的信号量元素总数的最大值。

在Linux系统中,以下信息可通过命令ipcs -l查看。

semop():PV操作

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
/*
semid:system-v IPC信号量的标识符
sops:指向一个struct sembuf结构体数组的指针,该数组是一个信号量操作数组,如下文
nsops:表示sops参数数组的数量
*/
struct sembuf{
        unsigned short int sem_num;    // 信号量的序号:0~nsems-1
        short int sem_op;              // 对信号量的操作:>0、0、<0
        short int sem_flg;             // 操作标识:0、IPC_WAIT、SEM_UNDO
};

sem_num标识信号量的第几个信号量,0标识第一个,nsems-1标识最后一个。

sem_op标识对信号量所进行的操作类型。

        >0:表示进程对资源使用完毕,交回该资源,即对该信号量执行V操作,交回的资源数由sem_op决定,系统会把sem_op的值加到该信号量的信号量当前值semval上。如果sem_flg指定了SEM_UNDO(还原)标志,则从该进程的信号量调整值中减去sem_op。

        <0:表示进程希望使用资源,对该信号量执行P操作,当信号量当前值semval大于或等于-sem_op时,semval减去sem_op的绝对值,为该进程分配对应数目的资源。如果sem_flg指定了SEM_UNDO(还原)标志,则sem_op的绝对值加到该进程的此信号量调整值上。当semval小于-sem_op时,相应信号量的等待进程数量就加1,调用进程被阻塞,直到semval大于或等于-sem_op时,调用进程被唤醒,执行相应的P操作。

        0:表示进程要阻塞等待,直到信号量当前值semval变为0。

sem_flg表示信号量操作的属性标志。

        0:正常操作

        SEM_UNDO:维护进程对信号量的调整值,进程退出时会自动还原它对信号量的操作。

        IPC_WAIT:调用进程在信号量的值不满足条件的情况下不会被阻塞,直接返回-1,并将errno设置为EAGAIN。

信号量调整值:指定信号量针对某个特定进程的调整值。只有sembuf结构的sem_flg指定为SEM_UNDO后,信号量调整值才会随着sem_op而更新。

对某个进程,在指定了SEM_UNDO后,对信号量的当前值的修改都会反应到信号量调整值上,当该进程终止时,内核会根据信号量调整值重新恢复信号量之前的值,SEM_UNDO操作可以防止进程退出时没有释放信号量导致的死锁。

semctl():信号量集的一系列控制操作

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
/*
semid:system-v IPC信号量的标识符
semnum:表示信号量集的第几个信号量,0表示第一个,nsems-1表示最后一个
cmd:操作命令
    IPC_STAT:获取此信号量集合的 semid_ds 结构,存放在第四个参数的 buf 中。
    IPC_SET:通过第四个参数的 buf 来设定信号量集相关联的 semid_ds 中信号量集合权
限为 sem_perm 中的 uid, gid, mode。
    IPC_RMID:从系统中删除该信号量集合。
    GETVAL:返回第 semnum 个信号量的值。
    SETVAL:设置第 semnum 个信号量的值,该值由第四个参数中的 val 指定。
    GETPID:返回第 semnum 个信号量的 sempid,最后一个操作的 pid。
    GETNCNT:返回第 semnum 个信号量的 semncnt。等待 semval 变为大于当前值的线程
数。
    GETZCNT:返回第 semnum 个信号量的 semzcnt。等待 semval 变为 0 的线程数。
    GETALL:去信号量集合中所有信号量的值,将结果存放到的 array 所指向的数组。
    SETALL:按 arg.array 所指向的数组中的值,设置集合中所有信号量的值。
第四个参数:可选,参数类型为union semun。
    union semun{
        int val;                // setval的值
        struct semid_ds *buf;   // IPC_STAT、IPC_SET的buffer
        unsigned short *array;  // GETALL、SETALL的数组
        struct seminfo *__buf;  // IPC_INFO的buffer
    }
*/

sem.h文件

#ifndef __SEM_H_
#define __SEM_H_

int init_sem(int sem_id, int init_value);
int del_sem(int sem_id);
int sem_p(int sem_id);
int sem_v(int sem_id);

#endif

sem.c文件

#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>

#include "sem.h"

union semun{
        int val;
        struct semid_ds *buf;
};

int init_sem(int sem_id, int init_value)
{
        union semun sem_union;
        // setval的值
        sem_union.val = init_value;

        // 设置第一个信号量的值
        if(semctl(sem_id, 0, SETVAL, sem_union) == -1){
                perror("sem init");
                return -1;
        }

        return 0;
}

int del_sem(int sem_id)
{
        union semun sem_union;

        // 删除第一个信号量
        if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1){
                perror("sem del");
                return -1;
        }

        return 0;
}

int sem_p(int sem_id)
{
        struct sembuf sops;
        sops.sem_num = 0;               // 单个信号量的编号应该为0
        sops.sem_op = -1;               // P操作
        sops.sem_flg = SEM_UNDO;        // 若进程退出,系统将还原信号量

        if(semop(sem_id, &sops, 1) == -1){
                perror("P");
                return -1;
        }else{
                printf("P successful!\n");
        }

        return 0;
}

int sem_v(int sem_id)
{
        struct sembuf sops;
        sops.sem_num = 0;               // 单个信号量的编号应该为0
        sops.sem_op = 1;                // V操作
        sops.sem_flg = SEM_UNDO;        // 若进程退出,系统将还原信号量

        if(semop(sem_id, &sops, 1) == -1){
                perror("V");
                return -1;
        }else{
                printf("V successful!\n");
        }

        return 0;
}

main.c文件

#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>

#include "sem.h"

int main(void)
{
        pid_t pid;
        int sem_id;

        sem_id = semget((key_t)0x1111, 1, 0666 | IPC_CREAT);
        init_sem(sem_id, 0);

        pid = fork();
        if(pid == -1){
                perror("fork");
        }else if(pid == 0){
                // 子进程
                sleep(3);
                printf("the child process pid:%d\n", getpid());
                sem_v(sem_id);
        }else{
                // 父进程
                sem_p(sem_id);
                printf("the father process pid:%d\n", getpid());
                sem_v(sem_id);
                del_sem(sem_id);
        }

        exit(0);
}

Makefile文件

照旧

执行过程

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

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

相关文章

JDK21的虚拟线程是什么?和平台线程什么关系?

虚拟线程&#xff08;Virtual Thread&#xff09;是 JDK 而不是 OS 实现的轻量级线程(Lightweight Process&#xff0c;LWP&#xff09;&#xff0c;由 JVM 调度。许多虚拟线程共享同一个操作系统线程&#xff0c;虚拟线程的数量可以远大于操作系统线程的数量。 在引入虚拟线程…

智慧公厕管理系统:让公共厕所管理变得更高效。

公共厕所的管理是城市环境卫生的重要组成部分&#xff0c;而传统的公厕管理方式已经无法满足日益增长的需求。为了提高公厕的管理效率&#xff0c;降低运营成本&#xff0c;智慧公厕管理系统应运而生。本文将以智慧公厕领先厂家广州中期科技有限公司&#xff0c;大量精品案例现…

java运维部署操作手册

关闭防火墙 不管是windows还是linux&#xff0c;首先必须关闭防火墙。 # linux添加8080端口 firewall-cmd --zonepublic --add-port8080/tcp --permanent#删除80端口 firewall-cmd --zonepublic --remove-port8080/tcp --permanent# 刷新使端口立即生效 firewall-cmd --reloa…

成集云 | 1药城对接英克ERP | 解决方案

方案介绍 1药城是中国一家知名的互联网医药零售平台&#xff0c;提供在线药品购买及健康服务。1药城致力于为用户提供方便、安全、优质的购药体验&#xff0c;同时与各大医疗机构合作&#xff0c;为用户提供在线挂号、预约就诊等服务。 英克ERP系统是由英克公司开发的企业资源…

uniapp(uncloud) 使用生态开发接口详情3(新增产品分类,产品列表,新闻列表)

我的想法是有产品分类,产品列表,新闻咨询,新闻列表 项目中, uniCloud > database 目录下新建 sy_product_nav.schema.json // 代码如下 {"bsonType": "object","required": ["classname"],"permission": {"read&…

瓷器文玩经营商城小程序的作用是什么

瓷器文玩商品受到不少人喜欢&#xff0c;无论是高价值物品还是低价饰品&#xff0c;都有较高需求&#xff0c;然而随着线下流量匮乏及线上互联网发展&#xff0c;传统瓷器文玩品牌店也面临着一些难题。 私域是近几年的热词&#xff0c;也有不少品牌基于私域取得了成功&#xf…

uniapp:使用subNVue原生子窗体在map上层添加自定义组件

我们想要在地图上层添加自定义组件&#xff0c;比如一个数据提示框&#xff0c;点一下会展开&#xff0c;再点一下收起&#xff0c;在h5段显示正常&#xff0c;但是到app端真机测试发现组件显示不出来&#xff0c;这是因为map是内置原生组件&#xff0c;层级最高&#xff0c;自…

流程挖掘助力企业数字化转型:CBPM 2023圆桌论坛圆满召开

9月23日至24日&#xff0c;由山东科技大学、山东大学、中国石油大学和中国海洋大学联合承办&#xff0c;RPA中国、山东理工大学、青岛大学协办的第十三届中国业务过程管理大会&#xff08;CBPM 2023&#xff09;成功举办&#xff01; 本次会议共有100余位来自国内外高校和科研…

《低代码指南》——AI低代码维格云架构视图怎么用?

架构视图是一个展示信息层级关系的视图,轻轻拖拽卡片,就能搭建精巧缜密的企业组织架构视图、实现信息结构化。 利用好架构视图,可以很好地解决以下场景: 展示企业/团队的组织关系 可视化管理产品开发架构 统筹全员 OKR 完成情况 架构视图的基础知识​ 架构视图分为以下几个…

深度学习入门(一)之感知机

文章目录 前言什么是感知机简单的逻辑电路与门与非门和或门 感知机的实现简单的实现导入权重和偏置 感知机的局限性异或门线性和非线性 多层感知机已有门电路的组合异或门的实现 代码合集 前言 感知机是由美国学者1957年提出来&#xff0c;作为神经网络的起源算法。因此学习感…

基于jsp+ssm+springboot的高校校园点餐系统【毕业论文+源码】

摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…

非技术背景项目经理如何发展?

非技术背景的项目经理在现代企业中扮演着重要的角色&#xff0c;他们负责协调和管理项目的各个方面&#xff0c;确保项目按时、按预算和按质量要求完成。对于没有技术背景的项目经理来说&#xff0c;他们需要通过一些特定的方法和策略来发展自己的职业生涯。 首先&#xff0c;…

七大排序的顶级理解(近万字详解)

目录 1. 排序的概念及引用 2.常见的排序算法 2.1直接插入排序 2.2希尔排序( 缩小增量排序 ) 2.3选择排序 2.4堆排序 2.5冒泡排序 2.6快速排序 2.6.1Hoare版 2.6.2挖坑法 2.6.3前后指针 2.7快速排序优化 2.7.1递归优化 2.7.2非递归实现快速排序 2.8归并排序 …

k8s-19 资源限制与监控

资源限制 上传镜像 limitrange 创建pod 自动添加限制 limitrage 是给所有的pod 都加range 自定义限制的pod也需要在limitrange定义的区间内 资源限制 创建的ResourceQuota对象将在default名字空间中添力头下限制:每个容器必须设置内存请求(memory request)&#xff0c;内存限…

苹果手机视频误删怎么恢复?这3个方法分享给大家!

在手机相册中&#xff0c;除了照片&#xff0c;其余占据空间最多的便是视频了。视频和照片一样&#xff0c;都是我们用来记录生活的一种方式。在这个盛行短视频的时代&#xff0c;越来越多小伙伴喜欢在各个平台分享自己的生活。 对于他们或者是一些职业博主来说&#xff0c;视…

模拟IIC通讯协议(stm32)(硬件iic后面在补)

一、IIC基础知识总结。 &#xff08;1&#xff09;IIC通讯需要两条线就可以&#xff0c;SCL、SDA。 &#xff08;2&#xff09;IIC的数据传输的速率&#xff0c;不同的ic是不同的&#xff0c;根据电平维持的延时函数的时间来确定IIC数据传输的速率. &#xff08;3&#xff09;I…

16603/16604系列噪声源

仪器仪表苏州新利通 16603/16604系列噪声源 16603/16604 series noise sources 噪声源是一种能产生随机连续频谱信号的装置 国产思仪噪声源 01 产品综述 噪声源是一种能产生随机连续频谱信号的装置&#xff0c;良好的噪声源应在规定的频带内具有稳定的输出噪声功率和均匀的功…

QT10_16

X-mind #include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {this->resize(536,412);this->setWindowTitle(" 坤坤之家");this->setWindowIcon(QIcon("D:/Ubuntushare/preview.gif"));this->setWindowFlag…

工业机器视觉系统构成及功能

工业机器视觉系统构成及功能 工业机器视觉系统由光源、光学传感器、图像采集设备、图像处理设备、机器视觉软件、辅助传感器、控制单元和执行机构等组件构成。 光源提供光线以辅助图像获取。 光学传感器将外部场景转换为电信号。 图像采集设备将信号转换为图像数据&#xf…

F. Minimum Maximum Distance Codeforces Round 903 (Div. 3)

Problem - F - Codeforces 题目大意&#xff1a;有一棵n个点的树&#xff0c;其中有k个标记点&#xff0c;令点i到所有标记点的最远距离为fi&#xff0c;问所有点中fi的最小值是多少 1<k<n<2e5 思路&#xff1a;我们首先考虑取得最小值的点在哪&#xff0c;我们假设…