①
共享内存
区域是
被多个进程共享
的一部分物理内存
②
多个进程都可把该共享内存映射到自己的虚拟内存空间
。所有用户空间的进程若要操作共享内存,都要将其映射到自己虚拟内存空间中,通过映射的虚拟内存空间地址去操作共享内存,从而达到进程间的数据通信。
③共享内存是进程间共享数据的一种最快的方法,
一个进程向共享内存区域写入数据,共享这个内存区域的所有进程就可以立刻看到其中的内容
。
④本身不提供同步机制,可通过信号量进行同步。
⑤提升数据处理效率,
一种效率最高的IPC机制
。
(2)共享内存属性结构体
(3)共享内存的使用步骤
①使用shmget函数
创建共享内存
②使用
shmat函数映射共享内存
,将这段创建的共享内存映射到具体的进程虚拟内存空间中。
③解除映射
④删除共享内存
(4)共享内存的创建、控制、映射和解除映射
①创建共享内存
void* shmat(int shmid, char* shmaddr, int shmflag);
//
映射,
成功返回共享内存映射到进程虚拟内存空间的地址,失败返回-1
int shmdt(char* shmaddr);
//
解除映射
。成功返回0,失败返回-1。
(1)shmid: 共享内存ID
(2)shmaddr:
映射到进程虚拟内存的地址
。建议设置为0,由操作系统分配。
(3)shmflag:若shmaddr设置为0,则shmflag也设置为0。
①SHM_RND:随机
②SHM_BA: 地址为2的平方
③SHM_RDONLY: 只读方式链接
int
shmid;
if
((shmid = shmget(IPC_PRIVATE,
1024
,
//
大小为1024字节
IPC_CREAT | IPC_EXCL |
0777
)) <
0
){
perror(
"
shmget error
"
);
exit(
1
);
pid_t pid;
init();
//
初始化管道
//
创建子进程
if
((pid = fork()) <
0
){
perror(
"
fork error
"
);
exit(
1
);
}
else
if
(pid >
0
){
//
parent process
//
进行共享内存的映射
int
* pi = (
int
*)shmat(shmid,
0
,
0
);
if
(pi == (
int
*)-
1
){
perror(
"
shmat error
"
);
exit(
1
);
//
往共享内存中写入数据(通过地址即可操作!)
*pi =
100
;
*(pi +
1
) =
200
;
//
操作完毕,解除映射
shmdt(pi);
//
通知子进程到读取共享内存中的数据
notify_pipe();
destroy_pipe();
wait(
0
);
//
删除共享内存
shmctl(shmid, IPC_RMID, NULL);
}
else
{
//
child process
//
子进程阻塞,等待父进程先往共享内存中写入数据
wait_pipe();
/*
子进程从共享内存中读取数据
*/
//
子进程进行共享内存映射
int
* pi = (
int
*)shmat(shmid,
0
,
0
);
if
(pi == (
int
*)-
1
){
perror(
"
shmat error
"
);
exit(
1
);
printf(
"
start: %d end: %d\n
"
, *pi, *(pi +
1
));
shmdt(pi);
destroy_pipe();
【编程实验】共享内存实现ATM(没有互斥、是不安全!)
(1)银行帐户创建在共享内存中(而不是原来的堆)
(2)改多线程为多进程程序。
//account.h
#ifndef __ACCOUNT_H__
#define __ACCOUNT_H__
typedef struct
int code; //帐号
double balance; //余额
}Account;
//取款
extern double withdraw(Account* a, double amt); //amt == amount
//存款
extern double deposit(Account* a, double amt);
//查看帐户余额
extern double get_balance(Account* a);
#endif //__ACCOUNT_H__
//account.c
#include "account.h"
#include <string.h>
#include <assert.h>
//取款
double withdraw(Account* a, double amt) //amt == amount
assert(a != NULL);
if(amt < 0 || amt > a->balance){
return 0.0;
double balance = a->balance; //先取余额
sleep(1); //为模拟进程下可能出现的问题
balance -= amt;
a->balance = balance; //更新余额。在读取余额和更新余额之间有
//故意留出“时间窗口”。
return amt;
//存款
double deposit(Account* a, double amt)
assert(a != NULL);
if(amt < 0){
return 0.0;
double balance = a->balance; //先取余额
sleep(1); //为模拟多进程下可能出现的问题
balance += amt;
a->balance = balance; //更新余额。
return amt;
//查看帐户余额
double get_balance(Account* a)
assert(a != NULL);
double balance = a->balance;
return balance;
//account_test.c
#include "account.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>
int main(void)
//在共享内存中创建银行帐户
int shmid;
if((shmid = shmget(IPC_PRIVATE, sizeof(Account), IPC_CREAT | IPC_EXCL | 0777)) < 0){
perror("shmget error");
exit(1);
//进程共享内存映射(a为返回的映射地址)
Account* a= (Account*)shmat(shmid, 0, 0);
if(a == (Account*)-1){
perror("shmat error");
exit(1);
//银行帐户初始化
a->code = 100001;
a->balance = 10000;
printf("balance: %f\n", a->balance);
//父子进程都进行取款
pid_t pid;
if((pid = fork()) < 0){
perror("fork error");
exit(1);
}else if(pid > 0){ //parent process
//父进程进行取款操作
double amt = withdraw(a, 10000);
printf("pid %d withdraw %f from code %d\n", getpid(), amt, a->code);
//解除映射
shmdt(a);
wait(0);
//删除共享内存区
shmctl(shmid, IPC_RMID, NULL);
}else{ //child process
//子进程会继承父进程映射的共享内存地址
//子进程进行取款操作
double amt = withdraw(a, 10000);
printf("pid %d withdraw %f from code %d\n", getpid(), amt, a->code);
//解除映射
shmdt(a);
return 0;
/*输出结果:
balance: 10000.000000
pid 1939 withdraw 10000.000000 from code 100001
pid 1940 withdraw 10000.000000 from code 100001 //不安全!(可用信号量解决)