linux进程间通信——信号量(通俗易懂,看这一篇就够了)

news/2024/9/30 11:31:53

信号量

概念

特点

  • 信号量实际是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储 进程间通信 数据。很多进程会访问同一资源,或者向共享内存写入一些东西,为防止争夺资源混乱。可以给一些进程上锁,让其排队等待

工作原理

  1. P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
  2. V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

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

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

步骤:

  1. 创建或者使用信号灯集
  2. 对某信号灯做PV操作(P减法,V加法)
  3. 删除信号灯集(非必须)

创建/获取信号量集

semphore get

功能

  • 创建一个新信号量或获取一个已有信号量

函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
  • key:唯一非零的整数值(和共享内存中的参数定义一致,key不变可以获取相同的信号灯集)
    • IPC_PRIVATE
    • ftok()
  • nsems:创建的信号量的个数(PV操作的钥匙个数)
  • semflg:一组标志
    • IPC_CREAT | 0666:当信号量不存在时创建一个新的信号量,并指定权限
    • IPC_CREAT | IPC_EXCL | 0666:创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。

返回值

  • 成功返回一个相应信号量集的标识号(非负整数)
  • 失败返回-1,并设置 errno

注意事项

  • nsems当前信号集中的信号量的个数,不是value值,semid是用于标识这一组信号量(即标识信号量集)

  • 示例:

    semget(key, 2, IPC_CREAT | 0666);
    
    • 创建一个信号量集,里面包含两个信号量,一个下标为0,另一个下标为1
    • 初始的value值都为0(没有通过semctl()修改),所以一旦先执行P操作,就会阻塞。

semget error: Invalid argument

  • 原因:semget之前创建的信号量集未删除(即nsms不一致)

    image-20240902214210359

    image-20240902214248861

  • 解决:使用命令ipcrm -s semid(semid为具体的信号量集标识符)删除系统中的信号量集,重新创建

使用命令查看信号量

ipcs -s # # interprocess communication status——进程间通信的状态 s——semaphore  
image-20240902095853401

PV操作

功能

  • 对某些信号量做 P(减法)和 V(加法)操作

函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>int semop(int semid, struct sembuf *sops, size_t nsops);
  • semid:信号量集标识号,semget()的返回值

  • sops:一个指向struct sembuf类型的指针

    struct sembuf
    {unsigned short int sem_num;	// 信号量组中对应的序号,0~sem_nums-1short int sem_op;	 		// 信号量值在一次操作中的改变量 +5(V操作) -5(P操作)short int sem_flg;			/* operation flag */
    };
    
    • sem_flg:0 阻塞 IPC_NOWAIT 不阻塞
  • nsops:操作的信号灯个数

返回值

  • 成功返回0
  • 失败返回-1,并设置 errno

示例1

struct sembuf ss1 = {0, -1, 0};
struct sembuf ss1 = {-1, -2, 0};
struct sembuf arr[2] = {ss1, ss2};
semop(semid, arr, 2); // semid为具体的信号灯标识号

示例2

struct sembuf ss = {0, -5, 0};
semop(semid, &ss, 1);

删除信号量集

功能

  • 对信号量集进行控制

函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>int semctl(int semid, int semnum, int cmd, ...);
  • semid:信号量集的标识号

  • semnum:信号量集中的某个信号量的下标

  • cmd

    • SETVAL:设置某个信号量的 value 值(这个值通过union semun中的val成员设置)

      • union semum结构体

        union semun {int              val;    /* 用于 SETVAL 的值 */struct semid_ds *buf;    /* 用于 IPC_STAT、IPC_SET 的缓冲区 */unsigned short  *array;  /* 用于 GETALL、SETALL 的数组 */struct seminfo  *__buf;  /* 用于 IPC_INFO(Linux 特定)的缓冲区 */
        };
        
      • 示例:

        union semun us;
        us.val = 5;
        semctl(semid, 0, SETVAL, us, NULL); // 并没有要求必须以NULL结尾
        
    • GETVAL:获取到 某个信号量的 value 值

      • 示例:

        semctl(semid, 0, GETVAL, NULL);
        
    • IPC_RMID:删除整个信号量集,不需要缺省参数,只需要三个参数即可

      • 示例:

        semctl(semid, 0, IPC_RMID, NULL);
        

返回值

  • 成功,根据cmd返回一个非负值
    • 对于SETVALGETVALIPC_RMID返回0代表成功
  • 失败,返回-1,并设置errno

linux union semun报错storage size isn‘t known

  • 原因:内核中的union sem_union联合体被注释

  • 解决:自己实现

    #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
    /* union semun is defined by including <sys/sem.h> */
    #else
    /* according to X/OPEN we have to define it ourselves */
    union semun
    {int              val;   /* value for SETVAL */struct semid_ds* buf;   /* buffer for IPC_STAT, IPC_SET */unsigned short*  array; /* array for GETALL, SETALL *//* Linux specific part: */struct seminfo* __buf; /* buffer for IPC_INFO */
    };
    #endif
    

使用命令删除信号量集

ipcrm -s # interprocess communication remove	s——semphore

综合示例

semget_p

概述:

  1. 通过调用ftok函数,创建唯一键值
  2. 创建/返回信号量集(初始时value为0)
  3. 修改value值为5
  4. 执行p操作,需要value值为6,不够,故阻塞
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including <sys/sem.h> */
#else
/* according to X/OPEN we have to define it ourselves */
union semun
{int              val;   /* value for SETVAL */struct semid_ds* buf;   /* buffer for IPC_STAT, IPC_SET */unsigned short*  array; /* array for GETALL, SETALL *//* Linux specific part: */struct seminfo* __buf; /* buffer for IPC_INFO */
};
#endifint main(int argc, char* argv[])
{key_t          key;int            semid;struct sembuf* sops;int            ret;// 创建keyif (-1 == (key = ftok("../ftok.txt", 7))){perror("ftok error");exit(-1);}printf("key:%#x\n", key);// 创建信号量集,当前集合中有3个信号量,下标分别为012,初始value为0if (-1 == (semid = semget(key, 3, IPC_CREAT | 0666))){perror("semget error");exit(-1);}printf("semid:%d\n", semid);// V操作, 下标0,增加1个value值,0表示阻塞struct sembuf ss = {0, 1, 0};if (-1 == (ret = semop(semid, &ss, 1))){perror("semop error");exit(-1);}printf("I am back--v\n");// 删除整个信号灯集semctl(semid, 0, IPC_RMID, NULL);return 0;
}

semget_v

概述:

  1. 通过调用ftok函数,创建唯一键值
  2. 创建/返回信号量集(初始时value为0)
  3. 修改value值为5(二次修改,会立即生效,即使此时P操作还在阻塞)
    • 如果直接修改value值为6,此时P操作(需要value值为5)会立刻结束阻塞
  4. 执行V操作,释放一个value值,P操作结束阻塞
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including <sys/sem.h> */
#else
/* according to X/OPEN we have to define it ourselves */
union semun
{int              val;   /* value for SETVAL */struct semid_ds* buf;   /* buffer for IPC_STAT, IPC_SET */unsigned short*  array; /* array for GETALL, SETALL *//* Linux specific part: */struct seminfo* __buf; /* buffer for IPC_INFO */
};
#endifint main(int argc, char* argv[])
{key_t          key;int            semid;struct sembuf* sops;int            ret;union semun    us;// 创建keyif (-1 == (key = ftok("../ftok.txt", 7))){perror("ftok error");exit(-1);}printf("key:%#x\n", key);// 创建信号量集,当前集合中有3个信号量,下标分别为012,初始value为0if (-1 == (semid = semget(key, 3, IPC_CREAT | 0666))){perror("semget error");exit(-1);}printf("semid:%d\n", semid);// 修改信号量的value值(修改后,会立即剩下,即使p操作还在阻塞状态)us.val = 5;// 并没有要求必须以NULL结尾if (0 != semctl(semid, 0, SETVAL, us, NULL)){perror("semctl error");exit(-1);}// V操作, 下标0,增加1个value值,0表示阻塞struct sembuf ss = {0, 1, 0};if (-1 == (ret = semop(semid, &ss, 1))){perror("semop error");exit(-1);}printf("I am back--v\n");// 删除整个信号灯集semctl(semid, 0, IPC_RMID, NULL);return 0;
}

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

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

相关文章

什么是非空集合A上的等价关系?

定义:解释 既然是等价关系,那么 \(A\) 中的元素 \(a\) 一定和自己等价 如果 \(a\) 和 \(b\) 等价,那么 \(b\) 和 \(a\) 也一定等价 如果 \(a,b\) 等价, \(b,c\) 等价,那么 \(a,c\) 一定等价

NetSarang Xshell(SSH客户端软件) v7.0.0169 中文绿色版

概述 NetSarang Xshell破解版是一款免费SSH客户端软件的Linux远程监控工具.Xshell中文版,轻松管理远程主机服务器,会话管理器,支持多选项卡管理主机.Xftp 7最新版以及Xshell 7最新版支持远程协议Telnet,Rlogin,SSH/SSH PKCS#11,SFTP,Serial,具有Unicode编码支持,动态端口转发,…

中望CAD 专业 v2024 解锁版下载与安装教程 (CAD三维制图)

安装步骤 ZWCAD2024-SP1.3-x64-Chs-Setup+Crack:ZWCAD2024-SP1.3-x64-Chs-Setup+Crack.zip 1、下载解压后点击如下进行安装2、选择安装目录,最好不要安装在系统盘(C盘)3、安装中...耐心等待2-3分钟4、安装完先不要运行它,点击完成即可5、回到我们解压的文件夹里面,访问cr…

SCRAPY入门学习(待完善)

Scrapy介绍 Scrapy 是用 Python 实现的一个为了爬取网站数据、提取结构性数据而编写的应用框架。 Scrapy 常应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。 通常我们可以很简单的通过 Scrapy 框架实现一个爬虫,抓取指定网站的内容或图片。Scrapy的运作流程 Sc…

从需求到交付:Leangoo领歌助力敏捷项目成功

​ 在敏捷项目管理中,需求管理是决定项目成功的关键环节。准确捕捉和高效管理需求,不仅能避免项目偏航,还能确保最终交付的产品与客户预期高度契合。Leangoo领歌敏捷工具,正是为此而生,助力团队轻松实现需求管理的每一步。​ 在敏捷项目管理中,需求管理是决定项目成功的关…

GNN-RAG:以RAG形式将 llm 的语言能力与 gnns 推理进行联合

Paper: https://arxiv.org/abs/2405.20139 知识图(KGs)以三元组(头、关系、尾)的形式表示人类精心制作的事实知识,它们 共同构成了一个图。大型语言模型(Large Language Models, llm)是 QA 任务中最 先进的模型,因为它们具有理解自然语言的卓越能力。另一方面,图神经网络 (G…

经验之谈

如果你想初三自学高中化学或者对化学理解十分不深刻,这篇或许能帮到你。前言 先叠个甲,这个文章全是主观内容,并且说的东西并不适合所有人,请结合自身实际情况仔细鉴别。 写这个文章的原因是我给很多人讲自己怎么学的化学。虽然我自己化学也不算特别好。 但是讲的时候因为我…

机器学习之——决策树信息增益比计算[附加计算程序]

0 前言本文主要介绍决策树信息增益比的计算,并给出例子帮助读者理解。 读者需要具备:信息熵、条件熵、信息增益 相关知识。 本文使用数据集:游玩数据集 1.1节。1 信息增益比计算公式2 信息增益比计算 2.1 gR(play,outlook)的计算 根据信息增益(跳转)相关知识,得出:特征out…