day31

3.9 信号量集

1> 原理图

信号量集主要完成多个进程之间同步问题

2> 信号量集的API函数接口

1、创建用于生成消息队列的钥匙#include <sys/types.h>#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);功能:通过给定的文件路径和一个随机ID值创建出一个用于IPC通信的key值       ftok("/", 'k');参数1:文件路径,该文件的inode号占key值的2字节,该文件的设备号占key值的1字节参数2:一个给定的随机值,该值占key值的1字节返回值:成功返回创建出的key值,失败返回-1并置位错误码2、通过钥匙创建出一个消息队列对象#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>int msgget(key_t key, int msgflg);功能:通过给定的key值创建一个消息队列参数1:用于创建消息队列的key值,该值可以由ftok创建出来,也可以是 IPC_PRIVATE,表示进行亲缘进程间的通信参数2:创建标识位IPC_CREAT:表示本次操作要创建一个消息队列,如果该key值对应的消息队列已经存在,则直接打开该消息对象IPC_EXCL:表示本次确保要创建一个新的消息队列,如果该消息队列已经存在,则该函数报错,错误码为EEXIST创建文件的权限,也在该参数中,使用位或连接返回值:成功返回消息队列的id号,失败返回-1并置位错误码3、向消息队列中存放消息int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);功能:向消息队列中存放消息,要求当前进程对消息队列具有可写权限参数1:消息队列的id号参数2:是指向封装好的消息的起始地址,通常类型如下,但是需要用户自己定义struct msgbuf {long mtype;       /* message type, must be > 0 */char mtext[1];    /* message data */};参数3:表示参数2中,消息正文的大小,不包含消息类型的大小参数4:发送标识位,表示是否阻塞0:表示阻塞IPC_NOWAIT:表示非阻塞返回值:成功返回0,失败返回-1并置位错误码4、从消息队列中取消息ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);功能:从消息队列中取消息参数1:消息队列的id号参数2:存放消息的容器起始地址参数3:消息正文的大小参数4:取出消息的类型>0: 表示取出该类型的消息的第一个消息=0:不限制类型,直接取消息队列中的第一个<0: 取出消息队列中类型小于msgtyp绝对值的第一个例如:  50   10   2   10    6   6  8-7:    2  6  6---> 2参数5:是否阻塞0:表示阻塞IPC_NOWAIT:表示非阻塞返回值:成功返回成功读取的字节个数,失败返回-1并置位错误码5、控制消息队列#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>int msgctl(int msqid, int cmd, struct msqid_ds *buf);功能:完成对消息队列指定的cmd操作参数1:消息队列的id号参数2:操作指令IPC_STAT:获取消息队列的属性,此时参数3必须要给定,表示接收消息队列的属性struct msqid_ds {struct ipc_perm msg_perm;     /* 拥有者和权限 */time_t          msg_stime;    /* 最新一次向消息队列中发送数据的时间 */time_t          msg_rtime;    /* 最新一次消息队列接受数据的时间 */time_t          msg_ctime;    /* 最新一次操作消息队列的时间 */unsigned long   __msg_cbytes; /* 当前队列中已用的字节数 */msgqnum_t       msg_qnum;     /* 当前队列中消息的个数*/msglen_t        msg_qbytes;   /* 队列的最大容量,默认是16K */pid_t           msg_lspid;    /* 最后一次向消息队列中发送消息的进程id号 */pid_t           msg_lrpid;    /* 最后一次从消息队列中取消息的进程id号 */};对第一个成员的介绍struct ipc_perm {key_t          __key;       /* 键值 */uid_t          uid;         /* 当前拥有者的用户id号 */gid_t          gid;         /*当前拥有者的组id号 */uid_t          cuid;        /* 创建消息队列的进程的用户id */gid_t          cgid;        /* 创建消息队列进程的组id号 */unsigned short mode;        /* 操作权限 */unsigned short __seq;       /* 队列号 */};IPC_SET:设置消息队列的属性  IPC_RMID:删除消息队列,当参数2位该值时,参数3可以忽略,直接填NULL即可返回值:成功返回0,失败返回-1并置位错误码

4> 发送端实现

#include<myhead.h>
//要发送的消息类型
struct msgbuf 
{long mtype;       /* message type, must be > 0 */char mtext[1024];    /* message data */
};
#define SIZE sizeof(struct msgbuf)-sizeof(long)int main(int argc, const char *argv[])
{//1、创建key值,用于生产消息队列key_t key = ftok("/", 'k');if(key == -1){perror("ftok error");return -1;}printf("key = %#x\n", key);//2、通过key值创建一个消息队列int msqid = msgget(key, IPC_CREAT|0664);if(msqid == -1){perror("msgget error");return -1;}printf("msqid = %d\n", msqid);        //id号//向消息队列中存放消息struct msgbuf buf;while(1){printf("请输入消息类型:");scanf("%ld", &buf.mtype);getchar();                    //吸收回车printf("请输入消息正文:");fgets(buf.mtext, SIZE, stdin);        //从终端获取数据buf.mtext[strlen(buf.mtext)-1] = 0;   //将换行换成 '\0'//将消息发送到消息队列中msgsnd(msqid, &buf, SIZE, 0);//参数1:消息队列id号//参数2:消息的起始地址//参数3:消息正文大小//参数4:阻塞形式发送数据printf("发送成功\n");if(strcmp(buf.mtext, "quit") == 0){break;}}return 0;
}

5> 接收端实现

#include<myhead.h>
//要发送的消息类型
struct msgbuf 
{long mtype;       /* message type, must be > 0 */char mtext[1024];    /* message data */
};
#define SIZE sizeof(struct msgbuf)-sizeof(long)int main(int argc, const char *argv[])
{//1、创建key值,用于生产消息队列key_t key = ftok("/", 'k');if(key == -1){perror("ftok error");return -1;}printf("key = %#x\n", key);//2、通过key值创建一个消息队列int msqid = msgget(key, IPC_CREAT|0664);if(msqid == -1){perror("msgget error");return -1;}printf("msqid = %d\n", msqid);        //id号//从消息队列中读取消息struct msgbuf buf;while(1){msgrcv(msqid, &buf, SIZE, 0, 0);//参数1:消息队列id号//参数2:数据容器起始地址//参数3:数据的正文大小//参数4:消息类型,0表示任意类型//参数5:表示阻塞读取消息printf("收到消息为:%s\n", buf.mtext);if(strcmp(buf.mtext, "quit") == 0){break;}}//删除消息队列if(msgctl(msqid, IPC_RMID, NULL) ==-1){perror("msgctl error");return -1;}return 0;
}

6> 消息队列的属性

#include<myhead.h>int main(int argc, const char *argv[])
{//1、创建key值,用于生产消息队列key_t key = ftok("/", 'k');if(key == -1){perror("ftok error");return -1;}printf("key = %#x\n", key);//2、通过key值创建一个消息队列int msqid = msgget(key, IPC_CREAT|0664);if(msqid == -1){perror("msgget error");return -1;}printf("msqid = %d\n", msqid);        //id号//获取该消息队列的属性struct msqid_ds ds;          //用于存放消息队列属性的变量if(msgctl(msqid, IPC_STAT, &ds) == -1){perror("msgctl error");return -1;}//程序执行至此,该消息队列中的相关信息就被存入到ds结构体中了printf("键:%#x, msqid:%d, 权限:%#o, 已用字节:%ld, 消息:%ld\n", \ds.msg_perm.__key, msqid, ds.msg_perm.mode, ds.__msg_cbytes,\ds.msg_qnum);return 0;
}

3.10 将信号量集函数二次封装

1> sem.h

#ifndef SEM_H
#define SEM_H
#include <myhead.h>union semun
{int val;               /* 参数3为 SETVAL使用该成员 */struct semid_ds *buf;  /* 参数3位 IPC_STAT, IPC_SET使用该成员 */unsigned short *array; /* 参数3位 GETALL, SETALL 使用该成员*/struct seminfo *__buf; /* 参数3为 IPC_INFO
使用该成员*/
};// 创建信号灯集并初始化
// 返回值:信号灯集id
// 参数:信号灯集中灯的个数
int sem_create(int semcont);// 执行申请某个信号灯的资源操作(P操作)
// 返回值:成功返回0,失败返回-1
// 参数1:信号灯集id
// 参数2:要操作的信号灯编号
int P(int semid, int semnum);// 执行释放某个信号灯的资源操作(V操作)
// 返回值:成功返回0,失败返回-1
// 参数1:信号灯集id
// 参数2:要操作的信号灯编号
int V(int semid, int semnum);// 删除信号灯集
// 参数:信号灯id号
int sem_del(int semid);#endif

2> sem.c

#include"sem.h"
//定义设置某个信号灯的值的函数
int init_semnum(int semid, int semnum)
{int val = 0;printf("请输入第%d号灯的初始值:", semnum+1);scanf("%d", &val);getchar();//准备共用体变量union semun buf;buf.val = val;     //要传递的数据//调用semctl函数完成对信号灯的值的设置if(semctl(semid, semnum, SETVAL, buf)==-1){perror("semctl error");return -1;}return 0;
}//创建信号灯集并初始化
int sem_create(int semcont)
{//1、创建key值key_t key = ftok("/", 't');if(key == -1){perror("ftok error");return -1;}//2、创建信号量集int semid = semget(key, semcont, IPC_CREAT|IPC_EXCL|0664);if(semid == -1){//对错误码进行判断if(errno == EEXIST){//说明消息队列已经存在,直接打开即可semid = semget(key, semcont, IPC_CREAT|0664);return semid;}perror("semget error");return -1;}//3、对信号灯进行初始化for(int i=0; i<semcont; i++){init_semnum(semid, i);}//4、返回创建的信号灯集idreturn semid;}//P操作:申请资源
int P(int semid, int semnum)
{//定义操作结构体变量struct sembuf buf;buf.sem_num = semnum;   //要操作的信号灯buf.sem_op = -1;        //表示申请资源,如果semnum灯的资源为0,则阻塞buf.sem_flg = 0;          //表示如果没有资源,则阻塞//调用函数进行申请资源if(semop(semid, &buf, 1) ==-1){perror("P error");return -1;}//成功返回0return 0;
}//V操作:释放资源
int V(int semid, int semnum)
{//定义操作结构体变量struct sembuf buf;buf.sem_num = semnum;   //要操作的信号灯buf.sem_op = 1;        //表示申请资源,如果semnum灯的资源为0,则阻塞buf.sem_flg = 0;          //表示如果没有资源,则阻塞//调用函数进行申请资源if(semop(semid, &buf, 1) ==-1){perror("V error");return -1;}//成功返回0return 0;
}//删除信号灯集
int sem_del(int semid)
{//调用semctl删除信号灯集if(semctl(semid, 1, IPC_RMID) ==-1){perror("delete error");return -1;}printf("信号灯集删除成功\n");return 0;
}

3.11 使用信号灯集完成共享内存的进程同步

1> 发送端流程

#include<myhead.h>
#include<sys/user.h>
#include"sem.h"int main(int argc, const char *argv[])
{//11、创建一个信号灯集并初始化int semid = sem_create(2);//1、创建key值用于创建共享内存段key_t key = ftok("/", 't');if(key == -1){perror("ftok error");return -1;}printf("key = %d\n", key);//2、创建一个共享内存的对象int shmid = shmget(key, PAGE_SIZE, IPC_CREAT|0664);if(shmid == -1){perror("shmget error");return -1;}printf("shmid = %d\n", shmid);//3、将共享内存段映射到程序中来char *addr = (char *)shmat(shmid, NULL, 0);//参数1:共享内存的id号//参数2:系统自动映射对齐页//参数3:表示对共享内存的操作权限为读写权限printf("addr = %p\n", addr);         //输出映射的地址//向共享内存中写入数据while(1){printf("请输入>>>");//22、申请资源P(semid, 0);fgets(addr, PAGE_SIZE, stdin);addr[strlen(addr)-1] = 0;//33、释放资源V(semid, 1);printf("发送成功\n");if(strcmp(addr,"quit") == 0){break;}}//取消映射关系if(shmdt(addr) ==-1){perror("shmdt error");return -1;}return 0;
}

2> 接收端流程

#include<myhead.h>
#include<sys/user.h>
#include"sem.h"int main(int argc, const char *argv[])
{//11、创建信号灯集int semid = sem_create(2);//1、创建key值用于创建共享内存段key_t key = ftok("/", 't');if(key == -1){perror("ftok error");return -1;}printf("key = %d\n", key);//2、创建一个共享内存的对象int shmid = shmget(key, PAGE_SIZE, IPC_CREAT|0664);if(shmid == -1){perror("shmget error");return -1;}printf("shmid = %d\n", shmid);//3、将共享内存段映射到程序中来char *addr = (char *)shmat(shmid, NULL, 0);//参数1:共享内存的id号//参数2:系统自动映射对齐页//参数3:表示对共享内存的操作权限为读写权限printf("addr = %p\n", addr);         //输出映射的地址//读出共享内存中的数据while(1){//22、申请资源P(semid, 1);printf("消息为:%s\n", addr);if(strcmp(addr,"quit") == 0){break;}//33、释放资源V(semid, 0);}if(shmdt(addr) == -1){perror("shmdt error");return -1;}//删除共享内存if(shmctl(shmid, IPC_RMID, NULL) == -1){perror("shmctl error");return -1;}//44、删除信号灯集sem_del(semid);return 0;
}

练习:使用信号灯集实现,三个进程,分别输出A、B、C,让这三个进程同步输出:ABCABCABC...

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

你也觉得FOTA升级难吗?这份详细教程让你自信升级!

前言&#xff1a; 我经常在各个讨论群里看到有合宙Air780EP的用户说&#xff1a; FOTA远程升级有点难呀~一步错后面就得重新来了&#xff0c;有没有大佬给个教程啊&#xff1f; 用户提需求了&#xff0c;那我们肯定要满足啊&#xff0c;就连夜赶了一篇 在整理这篇文章之前&…

掌握 LINQ:通过示例解释 C# 中强大的 LINQ的集运算

文章目录 集运算符原理实战示例1. Union2. Intersect3. Except4. ExceptWith5. Concat6. Distinct 注意事项总结 在C#中&#xff0c;LINQ&#xff08;Language Integrated Query&#xff09;提供了丰富的集合操作功能&#xff0c;使得对集合数据进行查询、过滤、排序等操作变得…

删除有序数组中的重复项(LeetCode)

题目 给你一个 升序排列 的数组 &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 中唯一元素的个数。 考虑 的唯一元素的数量为 &#xff0c;你需要做以下事情确…

CVE-2023-1313

开启靶场 url访问/install来运行安装 http://eci-2ze0wqx38em0qticuhug.cloudeci1.ichunqiu.com/install/ 得知其用户和密码为admin 登录 查找文件上传位置 上传一句话木马文件 <?php echo phpinfo();eval($_POST[flw]);?> 下载查看上传木马路径 复制路径 /storag…

代理IP如何助力品牌保护?

品牌是企业非常重要的无形资产&#xff0c;代表着一个公司、一个产品或服务的价值、信誉和形象。在竞争激烈的市场中&#xff0c;一个强有力的品牌可以帮助公司吸引更多的客户、提高销售、提高客户满意度和忠诚度&#xff0c;还可以帮助公司建立和维护其声誉、增强其企业形象&a…

单词拆分——LeetCode

139.单词拆分 题目 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。 注意&#xff1a;不要求字典中出现的单词全部都使用&#xff0c;并且字典中的单词可以重复使用 示例 1&#xff1a; 输入: s &qu…

数据结构实验:树和二叉树(附c++源码:实现树有关算法)

目录 一、实验目的 二、问题分析及数据结构设计 三、算法设计&#xff08;伪代码表示&#xff09; 1. 输入字符序列 创建二叉链表 2. 递归前序遍历 3. 递归中序遍历 4. 递归后序遍历 5. 非递归前序遍历 6. 非递归中序遍历 7. 非递归后序遍历 8. 层次遍历 9. 求二叉…

【AI】关于AI和手机

2011 年至2015 年期间&#xff0c;全球智能手机出货量年增长率均超过两位数&#xff0c;显示出强劲的市场需 求和快速扩张趋势。然而&#xff0c;自2016 年起&#xff0c;全球智能手机用户数量趋于饱和&#xff0c;换机周期也逐 渐变长&#xff0c;市场进入存量替换阶段&#x…

Qt/C++最新地图组件发布/历时半年重构/同时支持各种地图内核/包括百度高德腾讯天地图

一、前言说明 最近花了半年时间&#xff0c;专门重构了整个地图组件&#xff0c;之前写的比较粗糙&#xff0c;有点为了完成功能而做的&#xff0c;没有考虑太多拓展性和易用性。这套地图自检这几年大量的实际项目和用户使用下来&#xff0c;反馈了不少很好的建议和意见&#…

PXE 批量安装Linux系统

目录 一、 实验环境准备 1、一台红帽版本7的主机 2、开启主机图形 3、配置网络可用 4、关闭VMware dhcp 功能 ​编辑​编辑 5、配置好本地仓库&#xff0c;方便后续下载 二、配置kickstart自动安装脚本的工具 1、 安装图形化生成kickstart自动安装脚本的工具 2、启动图…

2.MySQL库的操作

创建数据库 创建数据库的代码&#xff1a; CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [,create_specification] ...];​create_specification:[DEFAULT] CHARACTER SET charset_name[DEFAULT] COLLATE collation_name 说明&#xff1a; 大写的表示关键…

【隐私保护】无证书签名方案(CLS)

一、CLS方案提出的背景 无证书签名方案&#xff08;Certificateless Signature Scheme, CLS&#xff09;是一种旨在结合公钥基础设施&#xff08;PKI&#xff09;和基于身份的加密&#xff08;IBE&#xff09;的优点&#xff0c;同时避免它们缺点的加密技术。 CLS方案的主要目标…

【网络安全渗透测试零基础入门必知必会】之什么是文件包含漏洞分类(非常详细)零基础入门到精通,收藏这一篇就够了

一、前言 这是大白给粉丝盆友们整理的网络安全渗透测试入门阶段文件包含渗透与防御第1篇。 本文主要讲解什么是文件包含漏洞、本地文件包含漏洞 喜欢的朋友们&#xff0c;记得给大白点赞支持和收藏一下&#xff0c;关注我&#xff0c;学习黑客技术。 一、什么是文件包含漏洞…

【HarmonyOS NEXT星河版开发学习】小型测试案例07-弹性布局小练习

个人主页→VON 收录专栏→鸿蒙开发小型案例总结​​​​​ 基础语法部分会发布于github 和 gitee上面&#xff08;暂未发布&#xff09; 前言 在鸿蒙&#xff08;HarmonyOS&#xff09;开发中&#xff0c;Flex布局是一种非常有用的布局方式&#xff0c;它允许开发者创建灵活且响…

Spring Boot实战:拦截器

一.拦截器快速入门 1.1了解拦截器 什么是拦截器&#xff1a; 概念 &#xff1a;拦截器是Spring框架提供的核⼼功能之⼀, 主要⽤来拦截⽤⼾的请求, 在指定⽅法前后, 根据业务需要执⾏预先设定的代码。 也就是说, 允许开发⼈员提前预定义⼀些逻辑, 在⽤⼾的请求响应前后执⾏. 也…

ThinkPHP6与金仓数据库(Kingbase)集成:模型查询的解决方案

摘要&#xff1a; ThinkPHP6是一款流行的PHP框架&#xff0c;支持多种数据库。然而&#xff0c;对于金仓数据库&#xff08;Kingbase&#xff09;这种相对小众的数据库系统&#xff0c;开发者在使用ThinkPHP6进行模型查询时可能会遇到一些兼容性问题。本文将提供一种解决方案&a…

仿推特社区源码修复版,含pc端和H5端,可以封装成app

简介&#xff1a; 新鲜出炉的仿推特社区源码修复版&#xff0c;含pc端和H5端&#xff0c;可以封装成app。这玩意绝对可以算是精品代码了。 手机h5端可以封装成软件也不错的。 推特的风格还是不错的&#xff0c;不然世界首富马斯克也不会花费440亿美金收购它了。 阅览&#…

nginx 405错误是什么意思

405错误&#xff1a;方法不被允许 当Web服务器收到一个它不支持的HTTP请求方法时&#xff0c;就会返回405错误。 原因 405错误通常是由于客户端发出了不兼容或不支持的HTTP请求方法。例如&#xff0c;客户端可能请求一个只能通过GET方法访问的资源&#xff0c;但使用了POST方…

图片转文字怎么操作?教你几招图片转文字小妙招

在日常的工作学习中&#xff0c;我们每天可能会接触到大量的图片资料&#xff0c;无论是会议纪要、书籍扫描页、还是网络上的有用信息截图&#xff0c;如果能快速将这些图片中的文字提取出来&#xff0c;无疑将极大提升我们的工作效率。下面给大家分享几种能够将图片转换成文字…

简单中间件模型

中间件是软件开发过程中架构的一个通用概念&#xff0c;其目的在于为运行的主程序提供一个供外部自定义拓展的能力。比如&#xff1a;wen服务的controller层中间件针对request请求处理的前后进行通用的扩展处理、redux中间件针对store数据获取前后的扩展处理。。。   本文简单…