第三十三章 System V共享内存与信号量综合
用信号量解决生产者、消费者问题

实现shmfifo

ip.h
#ifndef _IPC_H
#define _IPC_H
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while (0)
int sem_create(key_t key);
int sem_del(int semid);
int sem_open(key_t key);
int sem_setval(int semid, int val);
int sem_getval(int semid, int val);
int sem_p(int semid);
int sem_v(int semid);
#endif // !_IPC_H
ip.c
#include "ipc.h"
int sem_create(key_t key)
{
int semid;
semid = semget(key, 1, IPC_CREAT|IPC_EXCL| 0666);
if(semid == -1)
ERR_EXIT("semget");
return semid;
}
int sem_open(key_t key)
{
int semid;
semid = semget(key, 0, 0);
if(semid == -1)
ERR_EXIT("semget");
return semid;
}
int sem_setval(int semid, int val)
{
union semun su;
su.val = val;
int ret;
ret = semctl(semid, 0, SETVAL, su);
if(ret == -1)
ERR_EXIT("semctl");
return 0;
}
int sem_del(int semid)
{
int ret;
ret = semctl(semid, 0, IPC_RMID, 0);
if(ret == -1)
ERR_EXIT("semctl");
return 0;
}
int sem_getval(int semid, int val)
{
int ret;
ret = semctl(semid, 0, GETVAL, 0);
if(ret == -1)
ERR_EXIT("semctl getval");
return ret;
}
int sem_p(int semid)
{
struct sembuf sops = {0, -1, 0};
int ret;
ret = semop(semid, &sops, 1);
if(ret == -1)
ERR_EXIT("semop");
return 0;
}
int sem_v(int semid)
{
struct sembuf sops = {0, 1, 0};
int ret;
ret = semop(semid, &sops, 1);
if(ret == -1)
ERR_EXIT("semop");
return 0;
}
shmfifo.h
#ifndef SHM_FIFO_H
#define SHM_FIFO_H
#include "ipc.h"
typedef struct shmfifo shmfifo_t;
typedef struct shmhead shmhead_t;
struct shmhead
{
unsigned int blksize; //块大小
unsigned int blocks; //总块数
unsigned int rd_index; //读索引
unsigned int wr_index; //写索引
};
struct shmfifo
{
shmhead_t *p_shm; //共享内存头部指针
char *p_payload; //有效负载的起始地址
int shmid; //共享内存ID
int sem_mutex; //用来互斥用的信号量
int sem_full; //用来控制共享内存是否满的信号量
int sem_empty; //用来控制共享内存是否空的信号量
};
shmfifo_t* shmfifo_init(int key, int blksize, int blocks);
void shmfifo_put(shmfifo_t* fifo, const void* buf);
void shmfifo_get(shmfifo_t* fifo, void* buf);
void shmfifo_destory(shmfifo_t *fifo);
#endif // !SHM_FIFO_H
shmfifo.c
#include "shmfifo.h"
#include <assert.h>
#include <sys/ipc.h>
#include <sys/shm.h>
shmfifo_t* shmfifo_init(int key, int blksize, int blocks)
{
shmfifo_t *fifo = (shmfifo_t*)malloc(sizeof(shmfifo_t));
assert(fifo != NULL);
memset(fifo, 0, sizeof(shmfifo_t));
int shmid;
//假设共享内存已存在,直接打开
shmid = shmget(key, 0, 0);
int size = sizeof(shmhead_t) + blksize*blocks;
if(shmid == -1)
{
//打开失败,表示之前没有创建共享内存
fifo->shmid = shmget(key, size, IPC_CREAT|0666);
if(fifo->shmid == -1)
ERR_EXIT("shmget");
//把共享内存区对象映射到调用进程的地址空间
fifo->p_shm = (shmhead_t*)shmat(fifo->shmid, NULL, 0);
if(fifo->p_shm == (shmhead_t*)-1)
ERR_EXIT("shmat");
//初始化fifo这个结构
fifo->p_payload = (char *)(fifo->p_shm + 1);
fifo->p_shm->blksize = blksize;
fifo->p_shm->blocks = blocks;
fifo->p_shm->rd_index = 0;
fifo->p_shm->wr_index = 0;
//创建信号量
fifo->sem_mutex = sem_create(key);
fifo->sem_full = sem_create(key+1);
fifo->sem_empty = sem_create(key+2);
//设置信号量初始值
sem_setval(fifo->sem_mutex, 1);
sem_setval(fifo->sem_full, blocks);
sem_setval(fifo->sem_empty, 0);
}
else
{
//打开成功,共享内存和信号量已经存在
//共享内存和信号量使用的同一个key;共享内存之间或信号量之间不能用一个key
fifo->shmid = shmid;
fifo->p_shm = shmat(fifo->shmid, NULL, 0);
if(fifo->p_shm == (shmhead_t*)-1)
ERR_EXIT("shmat");
fifo->p_payload = (char *)fifo->p_shm+1;
fifo->sem_mutex = sem_open(key);
fifo->sem_full = sem_open(key+1);
fifo->sem_empty = sem_open(key+2);
}
return fifo;
}
void shmfifo_put(shmfifo_t* fifo, const void* buf)
{
//对信号量sem_full进行P操作;(sem_full = blocks)
//sem_full--
//若sem_full减1后仍大于或等于0,则进程继续执行
//若sem_full减1后小于0,则该进程被阻塞后放入等待该信号量的等待队列中,然后转进程调度
sem_p(fifo->sem_full);
// >=0
//进程对信号量sem_mutex进行P操作,其它写进程将阻塞
sem_p(fifo->sem_mutex);
//将buf拷贝到共享内存中
memcpy(fifo->p_payload + fifo->p_shm->blksize*fifo->p_shm->wr_index, buf, fifo->p_shm->blksize);
//更新写的下标
fifo->p_shm->wr_index = (fifo->p_shm->wr_index+1) % fifo->p_shm->blksize;
//进程对信号量sem_mutex进行V操作,其他写进程可以执行
sem_v(fifo->sem_mutex);
//对共享内存进行V操作;
//sem_empty ++
//若加1后结果大于0,则进程继续执行;
//若相加后结果小于或等于0,则从该信号的等待队列中释放一个等待进程,然后再返回原进程继续执行或转进程调度。
sem_v(fifo->sem_empty);
}
void shmfifo_get(shmfifo_t* fifo, void* buf)
{
//对信号量sem_empty进行P操作;(sem_full = 0)
//sem_empty--
//若sem_empty减1后仍大于或等于0,则进程继续执行
//若sem_empty减1后小于0,则该进程被阻塞后放入等待该信号量的等待队列中,然后转进程调度
sem_p(fifo->sem_empty);
// >=0
//进程对信号量sem_mutex进行P操作,其它读进程将阻塞
sem_p(fifo->sem_mutex);
//将共享内存拷贝到buf中
memcpy( buf, fifo->p_payload + fifo->p_shm->blksize*fifo->p_shm->rd_index, fifo->p_shm->blksize);
//更新读的下标
fifo->p_shm->rd_index = (fifo->p_shm->rd_index+1) % fifo->p_shm->blksize;
//进程对信号量sem_mutex进行V操作,其他写进程可以执行
sem_v(fifo->sem_mutex);
//对共享内存进行V操作;
//sem_full ++
//若加1后结果大于0,则进程继续执行;
//若相加后结果小于或等于0,则从该信号的等待队列中释放一个等待进程,然后再返回原进程继续执行或转进程调度。
sem_v(fifo->sem_full);
}
void shmfifo_destory(shmfifo_t *fifo)
{
sem_del(fifo->sem_mutex);
sem_del(fifo->sem_full);
sem_del(fifo->sem_empty);
shmdt(fifo->p_shm);
shmctl(fifo->shmid, IPC_RMID, NULL);
free(fifo);
}
shmfifo_send.c
#include "shmfifo.h"
typedef struct stu
{
int name[32];
int age;
}STU;
int main(void)
{
shmfifo_t *fifo = shmfifo_init(1234, sizeof(STU), 3);
STU s;
memset(&s, 9, sizeof(STU));
s.name[0] = 'A';
int i;
for(i=0; i<5; ++i)
{
s.age = 20 + i;
shmfifo_put(fifo,&s);
s.name[0] = s.name[0] + 1;
printf("send ok\n");
}
return 0;
}
shmfifo_recv.c
#include "shmfifo.h"
typedef struct stu
{
int name[32];
int age;
}STU;
int main(void)
{
shmfifo_t *fifo = shmfifo_init(1234, sizeof(STU), 3);
STU s;
memset(&s, 9, sizeof(STU));
int i;
for(i=0; i<5; ++i)
{
s.age = 20 + i;
shmfifo_get(fifo,&s);
printf("name = %s age = %d\n",s.name, s.age);
}
return 0;
}
shmfifo_free
#include "shmfifo.h"
typedef struct stu
{
int name[32];
int age;
}STU;
int main(void)
{
shmfifo_t *fifo = shmfifo_init(1234, sizeof(STU), 3);
shmfifo_destory(fifo);
return 0;
}
Makefile
.PHONY:clean all
CC=gcc
CFLAGS=-Wall -g
BIN = shmfifo_recv shmfifo_send shmfifo_free
OBJS1 = shmfifo_recv.o shmfifo.o ipc.o
OBJS2 = shmfifo_send.o shmfifo.o ipc.o
OBJS3 = shmfifo_free.o shmfifo.o ipc.o
all:$(BIN)
%.o:%.c
$(CC) $(CFLAGS) -c $< -o $@
shmfifo_recv: ${OBJS1}
$(CC) $(CFLAGS) $^ -o $@
shmfifo_send: ${OBJS2}
$(CC) $(CFLAGS) $^ -o $@
shmfifo_free: ${OBJS3}
$(CC) $(CFLAGS) $^ -o $@
clean:
rm -rf *.o $(BIN)
第三十三章 System V共享内存与信号量综合的更多相关文章
- system v共享内存与信号量综合
ipc.h #include <sys/types.h> #include <unistd.h> #include <sys/ipc.h> #include < ...
- 第二十九章 System V共享内存
共享内存数据结构 共享内存函数 shmget int shmget(key_t key, size_t size, int shmflg); 功能: 用于创建共享内存 参数: key : 这个共享内存 ...
- Linux进程通信之System V共享内存
前面已经介绍过了POSIX共享内存区,System V共享内存区在概念上类似POSIX共享内存区,POSIX共享内存区的使用是调用shm_open创建共享内存区后调用mmap进行内存区的映射,而Sys ...
- UNIX环境高级编程——System V 共享内存区
共享内存区域是被多个进程共享的一部分物理内存.如果多个进程都把该内存区域映射到自己的虚拟地址空间,则这些进程就都可以直接访问该共享内存区域,从而可以通过该区域进行通信.共享内存是进程间共享数据的一种最 ...
- Linux IPC实践(9) --System V共享内存
共享内存API #include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int ...
- System V共享内存介绍
(一)简单概念 共享内存作为一种进程间通信的方式,其相较于其他进程间通信方式而言最大的优点就是数据传输速率快.其内部实现的方式采用了Linux进程地址空间中的mmap文件映射区,将文件内容直接映射到各 ...
- 共享内存之——system V共享内存
System V 的IPC对象有共享内存.消息队列.信号灯(量). 注意:在IPC的通信模式下,不管是共享内存.消息队列还是信号灯,每个IPC的对象都有唯一的名字,称为"键(key)&quo ...
- System V共享内存
目录 1. 概述 2. System V共享内存API shmget shmat shmdt shmctl 3. 简单的程序 代码实现 common.h shmcreate.c shmrmid.c s ...
- 阐述linux IPC(五岁以下儿童):system V共享内存
[版权声明:尊重原创.转载请保留源:blog.csdn.net/shallnet 要么 .../gentleliu,文章学习交流,不用于商业用途] system V共享内存和posix ...
随机推荐
- 序列标注(BiLSTM-CRF/Lattice LSTM)
前言 在三大特征提取器中,我们已经接触了LSTM/CNN/Transormer三种特征提取器,这一节我们将介绍如何使用BiLSTM实现序列标注中的命名实体识别任务,以及Lattice-LSTM的模型原 ...
- linux下安装Elasticsearch
一.简单介绍: Elasticsearch提供了近乎实时的数据操作和搜索功能,es集群中所有节点可以一起提供索引和搜索功能,能够相互发现彼此和自动地加入到集群中 二.基础概念: 1.索引: 表征的文档 ...
- Docker 网易镜像仓库使用
Docker 网易镜像仓库使用: 网易账号注册: https://id.163yun.com/register?h=fc&referrer=https://console.163yun.com ...
- IDEA 学习笔记之 Spark/SBT项目开发
Spark/SBT项目开发: 下载Scala SDK 下载SBT 配置IDEA SBT:(如果不配置,就会重新下载SBT, 非常慢,因为以前我已经下过了,所以要配置为过去使用的SBT) 新建立SBT项 ...
- 低效sql语句执行缓慢引起的大量占用服务器的CPU问题处理 (优化心得)
1> 2> 3> 4> 5>删除不良的执行计划后执行时间仍然有150s,这实在是太慢了,继续查看原sql代码,发现父表的关联条件放在了子查询里,这是应该避免的 调整原sq ...
- python编程基础之三十五
系统的魔术方法:系统的魔术方法特别多,但是也都特别容易懂,简单的讲就是对系统的内置函数进行重写,你需要什么效果就重写成什么样, 比如说len()方法针对的对象本来没有自定义类的对象,但是当你重写了__ ...
- 【TencentOS tiny】深度源码分析(1)——task
任务的基本概念 从系统的角度看,任务是竞争系统资源的最小运行单元.TencentOS tiny是一个支持多任务的操作系统,任务可以使用或等待CPU.使用内存空间等系统资源,并独立于其它任务运行,理论上 ...
- GDB使用总结
1. GDB概述 GNU发布的调试器,可以查看程序如何运行或崩溃时的状态. 主要功能: 启动程序,可以按照自定义的要求运行程序. 可让被调试的程序在你所指定的断点处停住. 当程序被停住时,可以检查此时 ...
- Spring Boot 定时任务 @Scheduled
项目开发中经常需要执行一些定时任务,比如在每天凌晨,需要从 implala 数据库拉取产品功能活跃数据,分析处理后存入到 MySQL 数据库中.类似这样的需求还有许多,那么怎么去实现定时任务呢,有以下 ...
- java23种设计模式(二)抽象工厂模式
我们接着上一章的工厂方法模式继续学习一下抽象工厂模式. 抽象工厂模式:在工厂模式中,如果有多个产品,则就是抽象工厂模式. 例子: 有一个工厂开了两个子公司,专门用来生产电脑配件键盘和鼠标,一个是联想工 ...