IO进程线程(十二)进程间通信 共享内存 信号灯集

2024-06-10 19:20

本文主要是介绍IO进程线程(十二)进程间通信 共享内存 信号灯集,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、共享内存 shared memory(shm)
    • (一)特点
    • (二) 相关API
      • 1. 创建共享内存
      • 2. 映射共享内存到当前的进程空间
      • 3. 取消地址映射
      • 4. 共享内存控制
    • (三)使用示例
    • (四) 属性
  • 二、信号灯集---控制进程间同步
    • (一)特点
    • (二) 相关API
      • 1. 创建一个信号灯集
      • 2. 信号灯集控制函数
      • 3. 信号灯集的操作函数
    • (三)封装函数

一、共享内存 shared memory(shm)

(一)特点

在内核中创建共享内存,让进程A和进程B都能够访问到,通过这段内存进行数据的传递。
共享内存是所有进程间通信方式中效率最高的(不需要来回进行数据的拷贝)
在这里插入图片描述

(二) 相关API

1. 创建共享内存

#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
功能:创建共享内存
参数:key:键值key 通过ftok获取IPC_PRIVATE:只能用于亲缘进程间的通信size:共享内存的大小  PAGE_SIZE(4k)的整数倍shmflg:共享的标志位IPC_CREAT|0666 或 IPC_CREAT|IPC_EXCL|0666
返回值:成功 共享内存编号失败 -1 重置错误码
  • 注:
  • 共享内存大小必须要4k的整数倍,因为一页是4k。如果申请时不要求4
    的整数倍,分配时也是分配4k的整数倍。
  • 内核空间越界会直接段错误
  • 同一个key值可以同时用于消息队列,共享内存,信号灯集

2. 映射共享内存到当前的进程空间

#include <sys/ipc.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享内存到当前的进程空间
参数:shmid:共享内存编号shmaddr:指定共享内存出现在进程内存地址的什么位置,直接指定为NULL,让系统自动分配shmflg:共享内存操作方式0    读写SHM_RDONLY    只读
返回值:成功 指向共享内存的地址失败 (void *)-1 重置错误码

3. 取消地址映射

#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:取消地址映射
参数:shmaddr:指向共享内存的指针
返回值:成功 0失败 -1 重置错误码

4. 共享内存控制

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:共享内存控制的函数
参数:shmid:共享内存编号cmd:操作的命令码IPC_STAT:获取IPC_SET:设置IPC_RMID:删除共享内存标记要销毁的段。实际上,只有在最后一个进程将其分离之后 (也就是说,关联结构shmid_ds的shm_nattch成员为零时), 段才会被销毁。调用者必须是段的所有者或创建者,或具有特权。buf参数被忽略。buf:共享内存属性结构体指针
返回值:成功 0失败 -1 重置错误码

(三)使用示例

read.c

#include <my_head.h>#define SHM_PAGE 1024*4
int main(int argc, char const *argv[])
{//获取键值key_t key = ftok("/home/linux/05work",'A');if(-1 == key)ERR_LOG("ftok error");//创建共享内存int shmid = shmget(key, SHM_PAGE,IPC_CREAT|0666);if(-1 == shmid)ERR_LOG("shmget error");//映射共享内存char *addr = (char *)shmat(shmid,NULL,SHM_RDONLY);while(1){getchar();//防止刷屏,回车一次打印一次  if(!strcmp(addr,"quit")){break;}    printf("%s",addr);}//解除映射if(-1 == shmdt(addr)){ERR_LOG("shmid error");}//第三个参数会被忽略if(-1 == shmctl(shmid,IPC_RMID,NULL)){if(EINVAL == errno){return 0;}ERR_LOG("shmctl error");}return 0;
}

write.c

#include <my_head.h>#define SHM_PAGE 1024*4
int main(int argc, char const *argv[])
{//获取键值key_t key = ftok("/home/linux/05work",'A');if(-1 == key)ERR_LOG("ftok error");//创建共享内存int shmid = shmget(key, SHM_PAGE,IPC_CREAT|0666);if(-1 == shmid)ERR_LOG("shmget error");//映射共享内存char *addr = (char *)shmat(shmid,NULL,0);while(1){printf("请输入要发送的内容:");scanf("%s",addr);if(!strcmp(addr,"quit")){break;}}//解除映射if(-1 == shmdt(addr)){ERR_LOG("shmid error");}//销毁if(-1 == shmctl(shmid,IPC_RMID,NULL)){if(EINVAL == errno){return 0;}ERR_LOG("shmctl error");}return 0;
}

(四) 属性

struct shmid_ds{struct ipc_perm shm_perm;    //权限结构体size_t shm_segsz;             //共享内存大小,单位是字节 __time_t shm_atime;         //最后一次映射的时间 __pid_t shm_cpid;             //创建共享内存进程的pid __pid_t shm_lpid;             //最后一次操作共享内存进程的pid shmatt_t shm_nattch;         //共享内存映射的次数
};
struct ipc_perm{__key_t __key;                //ftok获取的key__uid_t uid;                 //用户的ID__gid_t gid;                 //组ID__uid_t cuid;                //创建共享内存的用户的ID__gid_t cgid;                //创建共享内存的组的IDunsigned short int mode;     //消息队列的权限
};

二、信号灯集—控制进程间同步

(一)特点

信号灯集:又叫做信号量数组,他是实现进程间同步的机制

在一个信号灯集中可以有很多个信号灯,这些信号灯之间工作相互互不干扰。
一般使用时使用的都是二值信号灯

(二) 相关API

1. 创建一个信号灯集

#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
功能:创建一个信号灯集
参数:key:键值IPC_PRIVATE/keynsems:信号灯集合中信号灯的个数semflag:创建的标志位IPC_CREAT|0666 或 IPC_CREAT|IPC_EXCL|0666
返回值:成功 semid失败 -1  重置错误码
  • 补充:使用IPC_PRIVATE创建的IPC对象, key值属性为0,和IPC对象的编号就没有了对应关系。这样毫无关系的进程,就不能通过key值来得到IPC对象的编号(因为这种方式创建的IPC对象的key值都是0)。因此,这种方式产生的IPC对象,和无名管道类似,不能用于毫无关系的进程间通信。但也不是一点用处都没有,仍然可以用于有亲缘关系的进程间通信。

2. 信号灯集控制函数

int semctl(int semid, int semnum, int cmd, ...);功能:信号灯集的控制函数参数:semid信号灯集的IDsenum:信号灯的编号 从0开始cmd:命令码SETVAL:设置信号灯的值 --->第四个参数val选项GETVAL:获取信号灯的值 --->不需要第四个参数IPC_STAT:获取信号灯集的属性--->第二参数被忽略,第四个参数buf选项IPC_SET :设置信号灯集的属性--->第二参数被忽略,第四个参数buf选项IPC_RMID:删除信号灯集 第二参数被忽略,第4个参数不用填写 @...:可变参union semun{int val; /* Value for SETVAL */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */};返回值:成功:GETVAL:成功返回信号灯的值其余的命令码成功返回0失败 -1  重置错误码
  • 注:*初始化操作在两个进程中必须只能进行一次,因为进程之间运行是没有顺序的,可以出现其中一个进程已经进行了信号灯集的相关操作后,另一个进程进行初始化而导致出错。

3. 信号灯集的操作函数

int semop(int semid, struct sembuf *sops, size_t nsops);功能:信号灯集中信号灯的操作函数参数:semid:信号灯集的编号sops:操作方式struct sembuf{unsigned short sem_num; //信号灯的编号short sem_op; //操作方式(PV)-1:P操作,申请资源 1:V操作,释放资源short sem_flg; //操作的标志位 0:阻塞 IPC_NOWAIT:非阻塞方式操作}nsops:本次操作信号灯的个数返回值:成功 0失败 -1  重置错误码
  • 注:同时对多个信号灯进行操作时,可以定义一个结构体数组

(三)封装函数

原生函数直接使用会比较繁琐,因此会做二次封装。

初始化(解决可能重复初始化的问题):

int sem_init_pack(key_t key, int nsem){int semid = 0;//不存在创建,存在退出返回semidif(-1 == (semid = semget(key,nsem,IPC_CREAT|IPC_EXCL|0666))){if(EEXIST == errno){//已存在导致的错误if((-1 == (semid = semget(key,nsem,IPC_CREAT|0666)))){return -1;}return semid;}else{//不是已存在导致的错误,说明出错return -1;}}//初始化,第一个置1,其余置0if(semctl(semid,0,SETVAL,1)){return -1;}for(int i = 1; i < nsem; i++){if(semctl(semid,i,SETVAL,0)){return -1;}}return semid;
}

单信号灯PV操作

int sem_p_pack(int semid,int semnum){struct sembuf sembuff={.sem_num=semnum,.sem_op=-1,.sem_flg=0};if(-1 == semop(semid,&sembuff,1)){return errno;}return 0;
}int sem_v_pack(int semid,int semnum){struct sembuf sembuff={.sem_num=semnum,.sem_op=1,.sem_flg=0};if(-1 == semop(semid,&sembuff,1)){return errno;}return 0;
}

多信号灯PV操作
传入一个int型数组指针,数组成员就是要进行p操作的信号灯编号

int sem_p_set_pack(int semid, int *semnum, int num){struct sembuf sembuff[num];for(int i=0; i<num; i++){sembuff[i].sem_flg=1;sembuff[i].sem_op=-1;sembuff[i].sem_num=semnum[i];if(-1 == semop(semid,sembuff+i,1)){return errno;}}return 0;
}int sem_v_set_pack(int semid, int *semnum, int num){if(NULL==semnum) return -1;struct sembuf sembuff[num];for(int i=0; i<num; i++){sembuff[i].sem_flg=1;sembuff[i].sem_op=1;sembuff[i].sem_num=semnum[i];if(-1 == semop(semid,sembuff+i,1)){return errno;}}return 0;
}

销毁

int sem_destroy_pack(int semid){if(-1 == semctl(semid,0,IPC_RMID)){//如果出现这个错误说明是已经销毁过了,无视这个错误if(EINVAL == errno){return 0;}//否则就是出错了,返回错误信息printf("semop error:%s",strerror(errno));return -1;}return 0;
}

这篇关于IO进程线程(十二)进程间通信 共享内存 信号灯集的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1049041

相关文章

Java线程面试题(50)

不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题。Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员的欢迎。大多数待遇丰厚的Java开发职位都要求开发者精通多线程技术并且有丰富的Java程序开发、调试、优化经验,所以线程相关的问题在面试中经常会被提到。 在典型的Java面试中, 面试官会从线程的基本概念问起, 如:为什么你需要使用线程,

Java——IO流(一)-(5/8):IO流概述、字节流-FileInputStream 每次读取一个字节

IO流概述 介绍 输入输出流,用于读写数据。 I指Input,称为输入流:负责把数据读到内存中去。 O指Output,称为输出流:负责写数据出去。 IO流的应用场景 文件内容的读写永久保存应用数据复制粘贴对话通信等等 怎么学IO流 理清楚IO六点分类和体系循序渐进、深入学习每个IO流的作用和用法 IO流的分类 IO流总体来看就有四大类: 字节输入流:以内存

线程池ThreadPoolExecutor类源码分析

Java并发编程:线程池的使用   在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:   如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。   那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?

线程Lock

线程Lock   在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问。本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock。   也许有朋友会问,既然都可以通过synchronized来实现同步访问了,那么为什么还需要提供Lock?这个问题将在下面进行阐述。本文先从s

线程封装,互斥

文章目录 线程封装线程互斥加锁、解锁认识接口解决问题理解锁 线程封装 C/C++代码混编引起的问题 此处pthread_create函数要求传入参数为void * func(void * )类型,按理来说ThreadRoutine满足,但是 这是在内类完成封装,所以ThreadRoutine函数实际是两个参数,第一个参数Thread* this不显示 解决方法: 第

Linux-笔记 线程同步机制

目录 前言 实现 信号量(Semaphore) 计数型信号量 二值信号量  信号量的原语操作 无名信号量的操作函数 例子 互斥锁(mutex) 互斥锁的操作函数 例子 自旋锁 (Spinlock) 自旋锁与互斥锁的区别 自旋锁的操作函数 例子 前言         线程同步是为了对共享资源的访问进行保护,确保数据的一致性,由于进程中会有多个线程的存在,

jmeter之Thread Group(线程组)

Thread Group(线程组) 1.线程组,或者可以叫用户组,进行性能测试时的用户资源池。 2.是任何一个测试计划执行的开始点。 3.上一篇提到的“控制器”和“HTTP请求”(采集器)必须在线程组内;监听器等其他组件,可以直接放在测试计划下。 线程组设置参数的意义 我们以下图为例,进行详细说明。见下图:  区域1(在取样器错误后要执行的动作) 这个区域的主要作用很明显,在线程内

如何在Android中实现多线程与线程池?

目录 一、Android介绍二、什么是多线程三、什么是线程池四、如何在Android中实现多线程与线程池 一、Android介绍 Android是一种基于Linux内核的开源操作系统,由Google公司领导开发。它最初于2007年发布,旨在为移动设备提供一种统一、可扩展的操作系统。Android系统以其高度的可定制性和丰富的应用生态而受到广泛欢迎,如今已经成为全球最流行的

Android进程保活全攻略(中)

在上一篇博客Android进程保活全攻略(上)中介绍了进程保活的背景和一些方法的思路和实现方式,本篇博客我将承接上篇博客,继续进行介绍。 9) 1像素悬浮层 **思路:**1像素悬浮层是传说的QQ黑科技,监控手机锁屏解锁事件,在屏幕锁屏时启动1个像素的 Activity,在用户解锁时将 Activity 销毁掉。注意该 Activity 需设计成用户无感知。通过该方案,可以使进程的优先级在屏幕

Android进程保活全攻略(上)

对于每个公司的APP来说,当然都希望自己APP的进程尽量的不被杀死,于是乎,就有了一些列进程保活的方法出现,网上也有很多关于这类的文章,但网上很多资料往往只告诉了思路,并未将实现代码展示,本次我的博客将分为上下两篇,阐述关于进程保活的所有方法,以及实现的方式,若有错漏之处,大家可以在博客进行留言。 ** 1.进程保活-背景知识 ** (1)什么时候系统会去杀死进程? Android系统会