50.1 共享内存
50.1.1 共享内存的概念
- 共享内存区域是被多个进程共享的一部分物理内存
- 多个进程都可把该共享内存映射到自己的虚拟内存空间。所有用户空间的进程若要操作共享内存,都要将其映射到自己虚拟内存空间中,通过映射的虚拟内存空间地址去操作共享内存,从而达到进程间的数据通信
- 共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容
- 本身不提供同步机制,可通过信号量进行同步
- 提升数据处理效率,一种效率最高的 IPC 机制
50.1.2 共享内存的映射
50.1.3 共享内存的属性
50.1.4 共享内存使用步骤
- 使用 shmget 函数创建共享内存
- 使用 shmat 函数映射共享内存,将这段创建的共享内存映射到具体的进程虚拟内存空间
50.1.5 创建共享内存
- 函数参数
- key:用户指定的共享内存键值
- size:共享内存大小
- shmflg:IPC_CREAT、IPC_EXCL 等权限组合
- 返回值:
- 如果成功,返回内核*享内存的标识 ID。如果失败,返回 -1
- errno:
- EINVAL:无效的内存段大小
- EEXIST:内存段已经存在,无法创建
- EIDRM:内存段已经被删除
- ENOENT:内存段不存在
- EACCES:权限不够
- ENOMEM:没有足够的内存来创建内存段
50.1.6 共享内存控制函数
- 函数参数:
- shmid:共享内存 ID
- cmd:
- IPC_STAT:获取共享内存段属性
- IPC_SET:设置共享内存段属性
- IPC_RMID:删除共享内存段
- SHM_LOCK:锁定共享内存段页面(页面映射到物理内存,不和外存进行换入换出操作)
- SHM_UNLOCK:解除共享内存段页面的锁定
- buf:共享内存属性指针
- 返回值:成功返回 0,出错返回 -1
50.1.7 共享内存的映射和解除映射
- 函数参数:
- shmid:共享内存 ID
- shmaddr:映射到进程虚拟内存空间的地址,建议设置为 0,由操作系统分配
- shmflg:若 shmaddr 设置为 0,则 shmflag 也设置为 0
- SHM_RND
- SHMLBA:地址为 2 的乘方
- SHM_RDONLY:只读方式链接
- 返回值:
- shmat:成功,则返回共享内存映射到进程虚拟内存空间的地址;失败,则返回 -1
- shmdt:如果失败,则返回 -1
- errno:
- EINVAL:无效的 IPC ID 值或者无效的地址
- ENOMEM:没有足够的内存
- EACCES:存取权限不够
- 函数说明:
- 子进程不继承父进程创建的共享内存,大家是共享的。子进程继承父进程映射的地址
50.2 例子
50.2.1 共享内存常规操作--单向同步
tell.h
#ifndef __TELL_H
#define __TELL_H /** 管道初始化 */
extern void init(); /** 利用管道进行等待 */
extern void wait_pipe(); /** 利用管道进行通知 */
extern void notify_pipe(); /** 销毁管道 */
extern void destroy_pipe(); #endif
tell.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "tell.h" static int fd[]; /** 管道初始化 */
void init()
{
if(pipe(fd) < ) {
perror("pipe error");
}
} /** 利用管道进行等待 */
void wait_pipe()
{
char c; /** 管道读写默认是阻塞性的 */
if(read(fd[], &c, ) < ){
perror("wait pipe error");
}
} /** 利用管道进行通知 */
void notify_pipe()
{
char c = 'c';
if(write(fd[], &c, ) != ){
perror("notify pipe error");
}
} /** 销毁管道 */
void destroy_pipe()
{
close(fd[]);
close(fd[]);
}
cal_shm.c
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tell.h" int main(void)
{
/** 创建共享内存 */
int shmid;
if((shmid = shmget(IPC_PRIVATE, , IPC_CREAT | IPC_EXCL | )) < ){
perror("shmget error");
return ;
} pid_t pid;
init(); ///< 初始化管道
if((pid = fork()) < 0){
perror("fork error");
return ;
}
else if(pid > ){
/** 进行共享内存映射 */
int *pi = (int *)shmat(shmid, , );
if(pi == (int *)-){
perror("shmat error");
return ;
} /** 往共享内存中写入数据(通过操作映射的地址即可) */
*pi = ;
*(pi + ) = ; /** 操作完毕解除共享内存的映射 */
shmdt(pi); /** 通知子进程去读取数据 */
notify_pipe(); destroy_pipe(); wait();
}
else{
/** 子进程阻塞,等待父进程先往共享内存中写入数据 */
wait_pipe(); /** 子进程读取共享内存中的数据 */
/** 子进程进行共享内存的映射 */
int *pi = (int *)shmat(shmid, , );
if(pi == (int *)-){
perror("shmat error");
return ;
}
printf("start: %d, end: %d\n", *pi, *(pi + )); /** 读取完毕后解除映射 */
shmdt(pi); /** 删除共享内存 */
shmctl(shmid, IPC_RMID, NULL); destroy_pipe(); } return ;
}
编译运行如下:
50.2.2 进程间互斥案例---银行账户(ATM)
atm_account.h
#ifndef __ATM_ACCOUNT_H__
#define __ATM_ACCOUNT_H__ #include <math.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/ipc.h> /** 账户信息 */
typedef struct {
int code; ///< 银行账户的编码
double balance; ///< 账户余额
}atm_Account; /** 取款 */
extern double atm_account_Withdraw(atm_Account *account, double amt);
/** 存款 */
extern double atm_account_Desposit(atm_Account *account, double amt);
/** 查看账户余额 */
extern double atm_account_BalanceGet(atm_Account *account); #endif
atm_account.c
#include "atm_account.h" /** 取款: 成功,则返回取款金额 */
double atm_account_Withdraw(atm_Account *account, double amt)
{
if(NULL == account) {
return 0.0;
} if(amt < || amt > account->balance) {
return 0.0;
} double balance_tmp = account->balance;
sleep();
balance_tmp -= amt;
account->balance = balance_tmp; return amt;
} /** 存款: 返回存款的金额 */
double atm_account_Desposit(atm_Account *account, double amt)
{
if(NULL == account){
return 0.0;
} if(amt < ){
return 0.0;
} double balance_tmp = account->balance;
sleep();
balance_tmp += amt;
account->balance = balance_tmp; return amt;
} /** 查看账户余额 */
double atm_account_BalanceGet(atm_Account *account)
{
if(NULL == account){
return 0.0;
} double balance_tmp = account->balance; return balance_tmp;
}
atm_handler.h
#ifndef __ATM_HANDLER_H__
#define __ATM_HANDLER_H__ #include "atm_account.h"
#include <sys/wait.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> typedef enum {
ATM_ERROR_NONE,
ATM_ERROR_ACCOUNT_CREATE
}atm_error_t; /** 账户操作结构体 */
typedef struct {
char name[]; ///< 操作人的姓名
atm_Account *account; ///< 操作的账户
double amt; ///< 操作的金额
}atm_handler_t; extern atm_error_t atm_handler_main(void); #endif
atm_handler.c
#include "atm_handler.h" /** 账户操作主函数 */
atm_error_t atm_handler_main(void)
{
/** 在共享内存中创建银行账户 */
int shmid;
if((shmid = shmget(IPC_PRIVATE, sizeof(atm_Account), IPC_CREAT | IPC_EXCL | )) < ) {
perror("shmget error");
return ;
} /** 进程共享内存映射 */
atm_Account *a = (atm_Account *)shmat(shmid, , );
if(a == (atm_Account *)-){
perror("shmat error");
return ;
}
a->code = ;
a->balance = ;
printf("balance: %f\n", a->balance); pid_t pid;
if((pid = fork()) < ){
perror("fork error");
return ;
}
else if(pid > ){
/** 父进程执行取款操作 */
double amt = atm_account_Withdraw(a, );
printf("pid %d withdraw %f form code %d\n", getpid(), amt, a->code);
wait();
/** 对共享内存的操作要在解除映射之前 */
printf("balance: %f\n", a->balance);
shmdt(a);
}
else {
/** 子进程也执行取款操作 */
double amt = atm_account_Withdraw(a, );
printf("pid %d withdraw %f form code %d\n", getpid(), amt, a->code);
shmdt(a);
} return ATM_ERROR_NONE;
}
atm_test.c
#include "atm_handler.h" int main(void)
{
atm_handler_main();
return ;
}
Makefile
#PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
PROJECT_ROOT = $(shell pwd)
SRC_DIR = $(PROJECT_ROOT)/src
INCLUDE_DIR = $(PROJECT_ROOT)/include
OBJ_DIR = $(PROJECT_ROOT)/obj
BIN_DIR = $(PROJECT_ROOT)/bin # 找出 src 目录下的所有 .c 文件
C_SRCS = $(wildcard $(SRC_DIR)/*.c)
# 将所有的 src 下的 .c 文件替换为 .o 文件
C_OBJS = $(patsubst %c, %o, $(C_SRCS))
TARGET = test
SHARE_LIB = libatm.a C_SRC_MAIN = atm_test.c CC = gcc
CCFLAGS += fPIC
LDFLAGS += -shared -fPIC
ASFLAGS +=
ARFLAGS = -crs
LIBS_FLAGS = -L$(BIN_DIR) RM = rm -rf CFLAGS += -Wall -g -I$(INCLUDE_DIR)
INCDIR += -I$(INCLUDE_DIR) .PHONY: all clean test all: $(TARGET)
cp $(SHARE_LIB) $(BIN_DIR)
cp $(SRC_DIR)/*.o $(OBJ_DIR)/
$(RM) $(SHARE_LIB) $(SRC_DIR)/*.o $(TARGET): $(SHARE_LIB)
$(CC) $(C_SRC_MAIN) -o $(TARGET) $(CFLAGS) $(INCDIR) $(LIBS_FLAGS) -latm $(SHARE_LIB): $(C_OBJS)
$(AR) $(ARFLAGS) $(SHARE_LIB) $(C_OBJS)
cp $(SHARE_LIB) $(BIN_DIR) $(C_OBJS) : %.o:%.c
$(CC) -c $(CFLAGS) $(INCDIR) -o $@ $< clean:
$(RM) mshell
$(RM) $(SHARE_LIB)
$(RM) $(OBJ_DIR)/$(OBJS)/*.o
$(RM) $(SRC_DIR)/*.o
编译执行结果如下:
两个账户都取款到 10000元,那是因为我们当前在共享内存的操作没有做互斥,下一节将添加互斥。
五十、进程间通信——System V IPC 之共享内存的更多相关文章
-
System V IPC 之共享内存
IPC 是进程间通信(Interprocess Communication)的缩写,通常指允许用户态进程执行系列操作的一组机制: 通过信号量与其他进程进行同步 向其他进程发送消息或者从其他进程接收消息 ...
-
System V IPC(3)-共享内存
一.概述 1.共享内存允许多个进程共享物理内存的同一块内存区. 2.与管道和消息队列不同,共享内存 ...
-
四十九、进程间通信——System V IPC 之消息队列
49.1 System V IPC 介绍 49.1.1 System V IPC 概述 UNIX 系统存在信号.管道和命名管道等基本进程间通讯机制 System V 引入了三种高级进程间通信机制 消息 ...
-
五十一、进程间通信——System V IPC 之进程信号量
51.1 进程信号量 51.1.1 信号量 本质上就是共享资源的数目,用来控制对共享资源的访问 用于进程间的互斥和同步 每种共享资源对应一个信号量,为了便于大量共享资源的操作引入了信号量集,可对所有信 ...
-
System V IPC 之信号量
本文继<System V IPC 之共享内存>之后接着介绍 System V IPC 的信号量编程.在开始正式的内容前让我们先概要的了解一下 Linux 中信号量的分类. 信号量的分类 在 ...
-
从并发处理谈PHP进程间通信(二)System V IPC
.container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...
-
Linux进程间通信(System V) --- 共享内存
共享内存 IPC 原理 共享内存进程间通信机制主要用于实现进程间大量的数据传输,下图所示为进程间使用共享内存实现大量数据传输的示意图: 共享内存是在内存中单独开辟的一段内存空间,这段内存空间有自己特有 ...
-
进程间通信IPC之--共享内存
每个进程各自有不同的用户地址空间,任何一个进 程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲 区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲 ...
-
System V IPC 之消息队列
消息队列和共享内存.信号量一样,同属 System V IPC 通信机制.消息队列是一系列连续排列的消息,保存在内核中,通过消息队列的引用标识符来访问.使用消息队列的好处是对每个消息指定了特定消息类型 ...
随机推荐
-
百度地图刷新显示不完整?(应该是和div顺序有关系)
解决方案:1异步加载(jquery(function(){loadJScript():})) 2解析加载设置了个延迟(setTimeOut(getInit,1000))
-
Weblogic12c安装与配置详解
Weblogic是什么Weblogic的安装Weblogic创建域Weblogic管理域Weblogic的应用Weblogic是什么 Weblogic这是我入职以后第一次接触到的词汇,我很陌生,就从我 ...
-
MVC小系列(七)【分部视图中的POST】
MVC小系列(七)[分部视图中的POST] 在PartialView中进行表单提交的作用:1 这个表单不止一个地方用到,2 可能涉及到异步的提交问题 这两种情况都可能需要把表单建立在分部视图上, 使用 ...
-
WCF - 契约
契约就是双方或多方就某个问题达成的一种的共识 服务提供者通过契约的形式将服务公布出来 服务消费者根据契约来消费 这样通过契约这个中间者就可以规范服务提供的内容 而服务消费者也可以根据契约来使用服务端 ...
-
C#学习笔记之结构体
1.概述 结构是一种与类相似的数据类型,不过它较类更为轻量,一般适用于表示类似Point.Rectangle.Color的对象.基本上结构能办到的类全都能办到,但在某些情况下使用结构更为合适,后面会有 ...
-
redis怎么动态添加内存,动态配置,无需重启。
在redis的使用过程中,有时候需要急需修改redis的配置,比如在业务运行的情况下,内存不够怎么办,这时要么赶紧删除无用的内存,要么扩展内存.如果有无用的内容可删除那么所有问题都已经解决.如果内容都 ...
-
Learning from the CakePHP source code - Part I
最近开始痛定思痛,研究cakephp的源码. 成长的路上从来没有捷径,没有小聪明. 只有傻傻的努力,你才能听到到成长的声音. 下面这篇文章虽然过时了,但是还是可以看到作者的精神,仿佛与作者隔着时空的交 ...
-
02.将SDK获取到的ECS主机信息入库
1.通过调用阿里SDK,将获取到的ECS信息存入数据库,如果不知道SDK怎么使用,查看:01.阿里云SDK调用,获取ESC主机详细信息 2.import aliSDK应用的是01.阿里云SDK调用,获 ...
-
eclipse的注释
版权声明:本文为博主原创文章,转载请注明出处. 如果能帮助你,那我的目的就达到了 Window --> Java --> Code Style --> Code Templates ...
-
Real time profiler for Delphi applications
xalion提供的资源,这么强,还是免费的,快去试用! ✓ Detailed debug information (internal, TDS, MAP) ✓ Display informat ...