12.5 Linux_进程间通信_信号灯

ops/2024/10/18 2:43:46/

概述

什么是信号灯:

信号灯也称为信号量,代表的是一类资源,其值表示系统中该资源的数量。

主要用途是实现进程、线程的同步。

什么是P/V操作:

P操作就是申请资源,V操作就是释放操作。

信号灯的种类:

Posix 有名信号灯:编译时需链接pthread库。有名信号灯文件存放在/dev/shm目录下

Posix 无名信号灯:只支持线程同步,编译时需链接pthread库

System V 信号灯

Posix 信号灯

Posix 信号灯的打开/关闭/删除函数有所不同,但PV操作是使用的同一组函数。具体函数如下

1、 打开/关闭/删除

1.1 有名信号灯

//打开
sem_t *sem_open(const char *name,int oflag);
sem_t *sem_open(const char *name,int oflag,mode_t mode,unsigned int value);
//关闭
int sem_close(sem_t *sem);
//删除
int sem_unlink(const char *name);

返回值:成功返回信号量指针,失败返回SEM_FAILED

name:信号灯的名字,即:文件名

oflag:打开方式,常用O_CREAT

mode:文件权限,常用0666

value:信号量值。二元信号灯值为1,普通信号灯表示资源数目。

1.2 无名信号灯

//创建
int sem_init(sem_t *sem, int pshared, unsigned int value);
//销毁
int sem_destroy(sem_t *sem);

sem:信号量指针

pshared:写0,代表不能在进程间共享。Linux中无名信号灯不能在进程间通信

value:信号量值。

2、P/V操作

2.1 P操作

int sem_wait(sem_t *sem);

当信号量为0时,进入阻塞,直到信号量不为0

当信号量不为0时,会将信号量的值-1

2.2 V操作

int sem_post(sem_t *sem);

该函数使用后会将信号量值+1

System V 信号灯

1、创建

int semget(key_t key, int nsems, int semflg);

返回值:成功返回信号灯id,失败返回-1

key:键值,由ftok生成

nsems:信号灯的个数

semflg:权限,通常写为IPC_CREAT|0666

2、控制信号灯

int semctl(int semid, int semnum, int cmd, ...);

semid:信号灯id

semnum:操作哪一个信号灯,序号从0开始

cmd:写入IPC_RMID,代表删除操作

           写入SETVAL,代表初始化信号灯的值,此时需要传入第四个参数,类型是共用体

union semun共用体:

union semun {int val;                //设置信号灯的初始值struct semid_ds *buf;unsigned short  *array;
} arg;

3、P/V操作

int semop(int semid, struct sembuf *sops, size_t nsops);

semid:信号灯id

sops:P/V操作

nsops:要操作的信号灯个数,通常写1

struct sembuf结构体:

struct sembuf {unsigned short sem_num;    //要操作的信号灯的编号short sem_op;              //1:V操作,-1:P操作short sem_flg;             //0:阻塞,IPC_NOWAIT不阻塞
};

示例代码

1、有名信号灯

见博文"12.2 Linux_进程间通信_共享内存"-"相关函数"-"实验代码"-"2、AB进程互传数据"

博文链接为:12.2 Linux_进程间通信_共享内存-CSDN博客

2、无名信号灯

无名信号灯只能用于线程间通信,下面是无名信号灯实现AB线程利用共享内存互传数据。

#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>sem_t sem_mmap;
void* mmap_addr = NULL;
char buf[100] = {0};
int i=0;
void* Afun(void* arg){memcpy(mmap_addr,"A Start SIG",strlen("A Start SIG"));while(1){sem_wait(&sem_mmap);if(*(char*)mmap_addr == 'B'){//读出B线程写入的内容printf("A read:%s\n",(char*)mmap_addr+strlen("B"));//读取数据,不读取数据来源标号memset(mmap_addr,0,strlen(mmap_addr));//清空缓冲区//写入新数据memcpy(mmap_addr,"A",strlen("A"));//数据来源标号sprintf(buf,"A_Data:%d",i++);     //新数据memcpy(mmap_addr+strlen("A"),buf,strlen(buf));}sem_post(&sem_mmap);}
}
void* Bfun(void* arg){while(1){sem_wait(&sem_mmap);if(*(char*)mmap_addr == 'A'){//读出A线程写入的内容printf("B read:%s\n",(char*)mmap_addr+strlen("A"));memset(mmap_addr,0,strlen(mmap_addr));//清空缓冲区//写入新数据memcpy(mmap_addr,"B",strlen("B"));//数据来源标号sprintf(buf,"B get A data,B data is %d",i++);     //新数据memcpy(mmap_addr+strlen("B"),buf,strlen(buf));sleep(1);}sem_post(&sem_mmap);}
}#define FILE_PATH "./mmap"
int main(){pthread_t tid[2];int fd;//打开文件if((fd=open(FILE_PATH,O_RDWR)) < 0){perror("open");return -1;}//创建共享内存映射if((mmap_addr = mmap(NULL,4*1024,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED){perror("mmap");return -1;}memset(mmap_addr,lseek(fd,0,SEEK_END),strlen(mmap_addr));//清空缓冲区close(fd);//创建共享内存映射后可以关闭文件描述符//创建信号量sem_init(&sem_mmap,0,1);//创建线程pthread_create(&tid[0],NULL,Afun,NULL);pthread_create(&tid[1],NULL,Bfun,NULL);while(1);return 0;
}

3、System V信号灯

使用SystemV信号灯实现"有名信号灯"章节的同样功能

A.c代码如下:

#include <sys/sem.h>
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>#define FILE_PATH "./mmap"
union semun {int val;                //设置信号灯的初始值struct semid_ds *buf;unsigned short  *array;
} arg;
int main(){int fd;void* mmap_addr = NULL;int i=0;char buf[100] = {0};key_t key;int sem_mmap;struct sembuf semPV;//打开文件if((fd=open(FILE_PATH,O_RDWR)) < 0){perror("open");return -1;}//创建共享内存映射if((mmap_addr = mmap(NULL,4*1024,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED){perror("mmap");return -1;}memset(mmap_addr,0,lseek(fd,0,SEEK_END));//清空缓冲区close(fd);//创建共享内存映射后可以关闭文件描述符//创建信号量key = ftok(".",1);if((sem_mmap = semget(key,1,IPC_CREAT|0666)) == -1){perror("sem_get");return -1;}arg.val = 1;semctl(sem_mmap,0,SETVAL,arg);//进程间通信memcpy(mmap_addr,"A Start SIG",strlen("A Start SIG"));while(1){semPV.sem_num = 0;semPV.sem_op=-1;semPV.sem_flg=0;semop(sem_mmap,&semPV,1);if(*(char*)mmap_addr == 'B'){//读出B进程写入的内容printf("A read:%s\n",(char*)mmap_addr+strlen("B"));//读取数据,不读取数据来源标号memset(mmap_addr,0,strlen(mmap_addr));//清空缓冲区//写入新数据memcpy(mmap_addr,"A",strlen("A"));//数据来源标号sprintf(buf,"A_Data:%d",i++);     //新数据memcpy(mmap_addr+strlen("A"),buf,strlen(buf));}semPV.sem_num = 0;semPV.sem_op=1;semPV.sem_flg=0;semop(sem_mmap,&semPV,1);}return 0;
}

B.c的代码如下:

#include <sys/mman.h>
#include <sys/sem.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>#define FILE_PATH "./mmap"
union semun {int val;                //设置信号灯的初始值struct semid_ds *buf;unsigned short  *array;
} arg;
int main(){int fd;void* mmap_addr = NULL;int i=0;char buf[100] = {0};key_t key;int sem_mmap;struct sembuf semPV;//打开文件if((fd=open(FILE_PATH,O_RDWR)) < 0){perror("open");return -1;}//创建共享内存映射if((mmap_addr = mmap(NULL,4*1024,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED){perror("mmap");return -1;}memset(mmap_addr,0,lseek(fd,0,SEEK_END));//清空缓冲区close(fd);//创建共享内存映射后可以关闭文件描述符//创建信号量key = ftok(".",1);if((sem_mmap = semget(key,1,IPC_CREAT|0666)) == -1){perror("sem_get");return -1;}arg.val = 1;semctl(sem_mmap,0,SETVAL,arg);//进程间通信while(1){semPV.sem_num = 0;semPV.sem_op=-1;semPV.sem_flg=0;semop(sem_mmap,&semPV,1);if(*(char*)mmap_addr == 'A'){//读出A进程写入的内容printf("B read:%s\n",(char*)mmap_addr+strlen("A"));memset(mmap_addr,0,strlen(mmap_addr));//清空缓冲区//写入新数据memcpy(mmap_addr,"B",strlen("B"));//数据来源标号sprintf(buf,"B get A data,B data is %d",i++);     //新数据memcpy(mmap_addr+strlen("B"),buf,strlen(buf));sleep(1);}semPV.sem_num = 0;semPV.sem_op=1;semPV.sem_flg=0;semop(sem_mmap,&semPV,1);}return 0;
}


http://www.ppmy.cn/ops/126366.html

相关文章

了解Android中的事件分发机制

Android中的事件分发机制详解 在Android开发中&#xff0c;事件分发机制是处理用户输入事件&#xff08;如触摸、点击、滑动等&#xff09;的核心部分。深入理解这一机制对于开发者来说至关重要&#xff0c;它有助于我们更好地处理用户输入&#xff0c;提升应用的交互体验。以…

从MySQL到OceanBase离线数据迁移的实践

本文作者&#xff1a;玉璁&#xff0c;OceanBase 生态产品技术专家。工作十余年&#xff0c;一直在基础架构与中间件领域从事研发工作。现负责OceanBase离线导数产品工具的研发工作&#xff0c;致力于为 OceanBase 建设一套完善的生态工具体系。 背景介绍 在互联网与云数据库技…

【Kubernets】容器网络基础二:通讲CNI(Container Network Interface)容器网络接口实现方案

文章目录 背景知识Underlay网络Overlay网络一、基本概念二、工作原理三、实现方案四、应用场景 两者对比示意图 CNI实现有哪些&#xff1f;FlannelFlannel 的工作原理Flannel 的主要组件数据传输机制总结 Calico一、架构基础二、核心组件与功能三、路由与数据包转发四、安全策略…

jvm介绍

JVM&#xff0c;即Java虚拟机&#xff08;Java Virtual Machine&#xff09;&#xff0c;是运行Java程序的抽象平台。它是一个能够执行Java字节码的虚拟机实例&#xff0c;负责将Java字节码转换为特定平台上的机器码并执行。下面我将从几个方面对JVM进行详细介绍&#xff1a; 1…

串口空闲中断加DMA数据搬运

1 usart.c #include "stm32f10x.h" // Device header #include <stdio.h> #include <stdarg.h>//void Serial_Init(void) //{ // //使能GPIOA 以及串口1的时钟 // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_US…

C++ IO多路复用 poll模型

原文链接&#xff1a;C IO多路复用 poll模型 预备知识 poll模型前置需要了解的可以参考: IO控制:fcntl库:IO控制库 多线程:C Linux多线程同步通信-信号量 socket:C Linux多进程Socket通信 select模型:C IO多路复用 select模型 poll模型 特性 原理 poll是对select的改…

C#从零开始学习(Head First C#)

想要开发游戏&#xff0c;C#是unity用的编程语言,所以想系统的巩固和学习一下&#xff0c;在此记录自己的学习笔记&#xff0c;来和大家共同学习&#xff0c;同时也希望能够帮助一些想入门的同学&#xff0c;因此我会使用Head First C#这本书籍,从最开始的章节记录。给自己定个…

【环境搭建】远程服务器搭建ElasticSearch

参考&#xff1a; 非常详细的阿里云服务器安装ElasticSearch过程..._阿里云服务器使用elasticsearch-CSDN博客 服务器平台&#xff1a;AutoDL 注意&#xff1a; 1、切换为非root用户&#xff0c;su 新用户名&#xff0c;否则ES无法启动 2、安装过程中没有出现设置账号密码…