进程间通信----信号量

news/2025/1/13 2:40:54/

文章目录

  • 信号量
    • 1. 问题
    • 2. 什么是信号量
    • 3. 信号量的使用
    • 4. 信号量的控制
    • 6. 实例

信号量

1. 问题

  • 程序中,有时存在一种特殊代码,最多只允许一个进程执行该部分代码。这部分区域,称为“临界区”

  • 然而在多进程并发执行时,当一个进程进入临界区,我们可以使用信号量让别的进程不能进入。

2. 什么是信号量

信号量,是一种特殊的变量:只能对信号量执行P操作和V操作

  • P操作(要执行代码的时候): 如果信号量的值 == 0, 则挂起该进程,如果信号量的值 > 0, 则把该信号量减1
  • V操作(执行完代码的时候): 如果有进程因该信号量而被挂起,则恢复该进程运行,如果没有进程因该信号量而挂起,则把该信号量加1
  • 注意:P操作、V操作都是原子操作,即其在执行时,不会被中断。

注意:此指的“信号量”是指System V IPC的信号量,与线程所使用的信号量不同。该信号量,用于进程间通信。

文件锁,只能有一个对文件进行操作
当信号量 只为 0/1 的时候,有点向文件锁,只能有一个进程对“临界区”操作

我的简单理解:int num = 5;当我要进入“临界区”中的代码的时候,num --,当退出“临界区”的时候num++,

当我们要进入“临界区”的时候,num > 0,否则就阻塞

3. 信号量的使用

  1. 信号量的获取
  • 原型:int semget(key_t key, int nsems, int semflg);

  • 功能:获取一个已存在的、或创建一个新的信号量量,返回该信号量的标识符

  • 参数:

    • key, 键值,该键值对应一个唯一的信号量。类似于共享内存的键值。不同的进程可通过该键值和semget获取唯一的信号量。特殊键值:IPC_PRIVAT该信号量只允许创建者本身, 可用于父子进程间通信。
    • nsems, 需要的信号量数目,一般取1
    • sem_flags, 与共享内存的sem_flags类似。IPC_CREAT, 如果该信号量未存在,则创建该信号量如果该信号量已存在,也不发送错误。
  • 返回值: 成功,则返回一个正数,失败, 返回返回-1

  1. 信号量的操作
  • 原型:int semop(int semid, struct sembuf *sops, unsigned nsops);
  • 功能:改变信号量的值,即对信号量执行P操作、或V操作。
  • 参数:
    • semid, 信号量标识符, 即semget的返回值
    • sops, 是一个数组,元素类型为struct sembuf
 struct sembuf {short  sem_num;  //信号量组中的编号(即指定对哪个信号量操作)//semget实际是获取一组信号量//如果只获取了一个信号量,则该成员取0short  sem_op;   //   -1,  表示P操作//   1,  表示V操作short  sem_flg;  // SEM_UNDO : 如果进程在终止时,没有释放信号量// 如果不设置指定标志,应该设置为0则,自动释放该信号量                     }        
    • nsops, 表示第二个参数sops所表示的数组的大小,即表示有几个struct sembuf
  • 返回值: 失败, 返回-1,成功, 返回 0

4. 信号量的控制

  • 原型:int semctl(int semid, int sem_num, int cmd, …);

  • 功能:对信号量进行控制

  • 参数:

    • semid, 信号量标识符
    • sem_num, 信号量组中的编号,如果只有一个信号量,则取0
    • cmd, SETVAL 把信号量初始化为指定的值,具体的值由第4个参数确定
  • 注意:只能对信号量初始化一次,如果在各进程中,分别对该信号量进行初始化,则可能导致错误!

  • IPC_RMID 删除信号量

//参数4, 类型为:
union  semun {int val;      // SETVAL命令要设置的值struct semid_ds *buf;unsigned short *array;
}

注意:union semun类型要求自己定义有些Linux发行版在sys/sem.h中定义,有些发行版则没有定义。

可自定义如下:

#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)						   
#elseunion semun {int val;                             struct semid_ds *buf;    unsigned short int *array; struct seminfo *__buf;  
};
#endif   

6. 实例

实例1:不使用信号量,并发执行多个进程,观察对临界区的访问。
main1.c
#include <stdlib.h>
#include <stdio.h>int main(void) 
{int i;pid_t pd = fork();for (i=0; i<5; i++) {/* 模拟临界区----begin */printf("Process(%d) In\n", getpid());		sleep(1);printf("Process(%d) Out\n", getpid());/* 模拟临界区----end */ sleep(1);}return 0;
}

现象:父子进程可以同时执行 ”临界区“

实例2:使用信号量,并发执行多个进程,观察对临界区的访问。main2.c (对main1.c改进)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <stdio.h>#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)						   
#elseunion semun {int val;                             struct semid_ds *buf;    unsigned short int *array; struct seminfo *__buf;  };
#endif     static sem_initial(int semid) 
{int ret;union semun semun;semun.val = 1;// 把信号量置为1ret = semctl(semid, 0, SETVAL, semun); // 设置信号量if (ret == -1) {fprintf(stderr, "semctl failed!\n");}return ret;
}static int  sem_p(int semid)
{int ret;struct sembuf sembuf;sembuf.sem_op = -1;sembuf.sem_num = 0;sembuf.sem_flg = SEM_UNDO;ret = semop(semid, &sembuf, 1);	if (ret == -1) {fprintf(stderr, "sem_p failed!\n");}return ret;
}static int  sem_v(int semid)
{int ret;struct sembuf sembuf;sembuf.sem_op = 1;sembuf.sem_num = 0;sembuf.sem_flg = SEM_UNDO;ret = semop(semid, &sembuf, 1);	if (ret == -1) {fprintf(stderr, "sem_v failed!\n");}return ret;
}int main(int argc, char* argv[]) 
{int i;int ret;int semid;/* 获取信号量 */semid = semget((key_t)1234, 1, 0666 | IPC_CREAT);if (semid == -1) {printf("semget failed!\n");exit(1);}/* 初始化信号量 */if (argc > 1) {ret = sem_initial(semid);if (ret == -1) {exit(1);}}for (i=0; i<5; i++) {// 先执行P操作if (sem_p(semid) == -1) {exit(1);}/* 模拟临界区----begin */printf("Process(%d) In\n", getpid());		sleep(1);printf("Process(%d) Out\n", getpid());/* 模拟临界区----end */ // 再执行V操作if (sem_v(semid) == -1) {exit(1);}sleep(1);}/* 删除信号量 */return 0;
}

两个终端都执行上面的程序,两个程序交替拿到信号量,则交替执行信号量之间的程序
信号量专治多进程

在这里插入图片描述

练习:

  • 使用信号量实现对文件操作的互斥访问。
  • 程序1,对test.txt写入学生记录信息10条
  • 程序2,对test.txt写入教师记录信息10条
  • 程序1和程序2并发执行

http://www.ppmy.cn/news/35541.html

相关文章

【云原生|Docker】06-dokcerfile详解

目录 前言 Dockerfile基础示例 Dockerfile简介 1. Dockerfile概念 2. Dokcer镜像分层理解 ​3. Doker build构建原理 Dockerfile参数解析 1. Dokcerfile组成 2. 指令说明 2.1 FROM引入基础镜像 2.2 LABEL 2.3 ENV 2.4 RUN 2.5 COPY 2.6 ADD 2…

【十二天学java】day09常用api介绍

1.API 1.1API概述 什么是API API (Application Programming Interface) &#xff1a;应用程序编程接口 java中的API 指的就是 JDK 中提供的各种功能的 Java类&#xff0c;这些类将底层的实现封装了起来&#xff0c;我们不需要关心这些类是如何实现的&#xff0c;只需要学习这…

软件测试面试题 —— 整理与解析(3)

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;&#x1f30e;【Austin_zhai】&#x1f30f; &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xf…

RPA机器人能做什么?自动化办公、简化工作流程……还有很多事情等着你挖掘

看到这篇文章&#xff0c;首先我要恭喜你&#xff0c;因为我会带领你进入一个全新的领域&#xff0c;这将极大地方便你的日常工作和生活。 你听说过RPA吗&#xff1f;也许你会认为这是一个非常陌生的名词&#xff0c;但它可能会改变你的工作方式&#xff0c;提高你的工作效率&a…

JAVA接收Byte字节数组,判断文件是否什么图片类型

在 Java 中&#xff0c;我们可以通过解析图片字节数据的前几个字节来获取图片的类型&#xff0c;通常我们称之为文件的“魔数”。对于常见的图片格式&#xff0c;其对应的魔数值是固定的。例如&#xff0c;JPEG 图片的前两个字节的十六进制表示是 “FF D8”&#xff0c;PNG 图片…

p84 CTF夺旗-PHP弱类型异或取反序列化RCE

数据来源 文章参考 本课重点&#xff1a; 案例1&#xff1a;PHP-相关总结知识点-后期复现案例2&#xff1a;PHP-弱类型对比绕过测试-常考点案例3&#xff1a;PHP-正则preg_match绕过-常考点案例4&#xff1a;PHP-命令执行RCE变异绕过-常考点案例5&#xff1a;PHP-反序列化考题…

Nginx安装部署

Nginx安装部署 nginx的安装与部署都需要一些模块的支持&#xff1b;因此需要提前安装一下Nginx的依赖模块 pcre/pcre-devel&#xff1a;这个依赖用于支持nginx的rewrite模块 pcre-config –version gcc/gcc-c&#xff1a;这个依赖主要用于源码编译 gcc -v&#xff1a;可以查看…

【数据结构篇C++实现】- 栈

文章目录&#x1f680;一、栈的原理精讲&#x1f680;二、栈的算法实现⛳栈的顺序存储结构&#x1f389;&#xff08;一&#xff09;顺序栈1.栈的结构体定义2.栈的初始化3.判断空栈4.判断栈满5.元素入栈6.元素出栈7.获取栈顶元素&#x1f389;&#xff08;二&#xff09;共享栈…