相关文章推荐

共享内存 区域是 被多个进程共享 的一部分物理内存

多个进程都可把该共享内存映射到自己的虚拟内存空间 。所有用户空间的进程若要操作共享内存,都要将其映射到自己虚拟内存空间中,通过映射的虚拟内存空间地址去操作共享内存,从而达到进程间的数据通信。

③共享内存是进程间共享数据的一种最快的方法, 一个进程向共享内存区域写入数据,共享这个内存区域的所有进程就可以立刻看到其中的内容

④本身不提供同步机制,可通过信号量进行同步。

⑤提升数据处理效率, 一种效率最高的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 //不安全!(可用信号量解决)
 
推荐文章