zoukankan      html  css  js  c++  java
  • 五十、进程间通信——System V IPC 之共享内存

    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

     1 #ifndef __TELL_H
     2 #define __TELL_H
     3 
     4 
     5 /** 管道初始化 */
     6 extern void init();
     7 
     8 /** 利用管道进行等待 */
     9 extern void wait_pipe();
    10 
    11 /** 利用管道进行通知 */
    12 extern void notify_pipe();
    13 
    14 /** 销毁管道 */
    15 extern void destroy_pipe();
    16 
    17 #endif

      tell.c

     1 #include <stdlib.h>
     2 #include <stdio.h>
     3 #include <string.h>
     4 #include <unistd.h>
     5 #include "tell.h"
     6 
     7 static int fd[2];
     8 
     9 /** 管道初始化 */
    10 void init()
    11 {
    12     if(pipe(fd) < 0) {
    13         perror("pipe error");
    14     }
    15 }
    16 
    17 /** 利用管道进行等待 */
    18 void wait_pipe()
    19 {
    20     char c;
    21 
    22     /** 管道读写默认是阻塞性的 */
    23     if(read(fd[0], &c, 1) < 0){
    24         perror("wait pipe error");
    25     }
    26 }
    27 
    28 /** 利用管道进行通知 */
    29 void notify_pipe()
    30 {
    31     char c = 'c';
    32     if(write(fd[1], &c, 1) != 1){
    33         perror("notify pipe error");
    34     }
    35 }
    36 
    37 /** 销毁管道 */
    38 void destroy_pipe()
    39 {
    40     close(fd[0]);
    41     close(fd[1]);
    42 }

      cal_shm.c

     1 #include <sys/shm.h>
     2 #include <sys/ipc.h>
     3 #include <sys/types.h>
     4 #include <sys/wait.h>
     5 #include <unistd.h>
     6 #include <stdio.h>
     7 #include <stdlib.h>
     8 #include <string.h>
     9 #include "tell.h"
    10 
    11 
    12 int main(void)
    13 {
    14     /** 创建共享内存 */
    15     int shmid;
    16     if((shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT | IPC_EXCL | 0777)) < 0){
    17         perror("shmget error");
    18         return 1;
    19     }
    20 
    21     pid_t pid;
    22     init(); ///< 初始化管道
    23     if((pid = fork()) < 0){
    24         perror("fork error");
    25         return 1;
    26     }
    27     else if(pid > 0){
    28         /** 进行共享内存映射 */
    29         int *pi = (int *)shmat(shmid, 0, 0);
    30         if(pi == (int *)-1){
    31             perror("shmat error");
    32             return 1;
    33         }
    34 
    35         /** 往共享内存中写入数据(通过操作映射的地址即可) */
    36         *pi = 100;
    37         *(pi + 1) = 20;
    38 
    39         /** 操作完毕解除共享内存的映射 */
    40         shmdt(pi);
    41 
    42         /** 通知子进程去读取数据 */
    43         notify_pipe();
    44 
    45         destroy_pipe();
    46 
    47         wait(0);
    48     }
    49     else{
    50         /** 子进程阻塞,等待父进程先往共享内存中写入数据 */
    51         wait_pipe();
    52 
    53         /** 子进程读取共享内存中的数据 */
    54         /** 子进程进行共享内存的映射 */
    55         int *pi = (int *)shmat(shmid, 0, 0);
    56         if(pi == (int *)-1){
    57             perror("shmat error");
    58             return 1;
    59         }
    60         printf("start: %d, end: %d
    ", *pi, *(pi + 1));
    61 
    62         /** 读取完毕后解除映射 */
    63         shmdt(pi);
    64 
    65         /** 删除共享内存 */
    66         shmctl(shmid, IPC_RMID, NULL);
    67 
    68         destroy_pipe();
    69 
    70     }
    71 
    72     return 0;
    73 }

      编译运行如下:

      

    50.2.2 进程间互斥案例---银行账户(ATM)

      

      atm_account.h

     1 #ifndef __ATM_ACCOUNT_H__
     2 #define __ATM_ACCOUNT_H__
     3 
     4 #include <math.h>
     5 #include <malloc.h>
     6 #include <stdlib.h>
     7 #include <stdio.h>
     8 #include <string.h>
     9 #include <unistd.h>
    10 #include <sys/shm.h>
    11 #include <sys/ipc.h>
    12 
    13 /** 账户信息 */
    14 typedef struct {
    15     int         code;       ///< 银行账户的编码
    16     double      balance;    ///< 账户余额
    17 }atm_Account;
    18 
    19 /** 取款 */
    20 extern double atm_account_Withdraw(atm_Account *account, double amt);
    21 /** 存款 */
    22 extern double atm_account_Desposit(atm_Account *account, double amt);
    23 /** 查看账户余额 */
    24 extern double atm_account_BalanceGet(atm_Account *account);
    25 
    26 #endif

      atm_account.c

     1 #include "atm_account.h"
     2 
     3 /** 取款: 成功,则返回取款金额 */
     4 double atm_account_Withdraw(atm_Account *account, double amt)
     5 {
     6     if(NULL == account) {
     7         return 0.0;
     8     }
     9 
    10     if(amt < 0 || amt > account->balance) {
    11         return 0.0;
    12     }
    13 
    14     double balance_tmp = account->balance;
    15     sleep(1);
    16     balance_tmp -= amt;
    17     account->balance = balance_tmp;
    18 
    19     return amt;
    20 }
    21 
    22 /** 存款: 返回存款的金额 */
    23 double atm_account_Desposit(atm_Account *account, double amt)
    24 {
    25     if(NULL == account){
    26         return 0.0;
    27     }
    28 
    29     if(amt < 0){
    30         return 0.0;
    31     }
    32 
    33     double balance_tmp = account->balance;
    34     sleep(1);
    35     balance_tmp += amt;
    36     account->balance = balance_tmp;
    37 
    38     return amt;
    39 }
    40 
    41 /** 查看账户余额 */
    42 double atm_account_BalanceGet(atm_Account *account)
    43 {
    44     if(NULL == account){
    45         return 0.0;
    46     }
    47 
    48     double balance_tmp = account->balance;
    49 
    50     return balance_tmp;
    51 }

      atm_handler.h

     1 #ifndef __ATM_HANDLER_H__
     2 #define __ATM_HANDLER_H__
     3 
     4 #include "atm_account.h"
     5 #include <sys/wait.h>
     6 #include <sys/shm.h>
     7 #include <sys/ipc.h>
     8 #include <stdio.h>
     9 #include <stdlib.h>
    10 #include <string.h>
    11 
    12 typedef enum {
    13     ATM_ERROR_NONE,
    14     ATM_ERROR_ACCOUNT_CREATE
    15 }atm_error_t;
    16 
    17 /** 账户操作结构体 */
    18 typedef struct {
    19     char            name[20];   ///< 操作人的姓名
    20     atm_Account     *account;   ///< 操作的账户
    21     double          amt;        ///< 操作的金额
    22 }atm_handler_t;
    23 
    24 extern atm_error_t atm_handler_main(void);
    25 
    26 #endif

      atm_handler.c

     1 #include "atm_handler.h"
     2 
     3 
     4 /** 账户操作主函数 */
     5 atm_error_t atm_handler_main(void)
     6 {
     7     /** 在共享内存中创建银行账户 */
     8     int shmid;
     9     if((shmid = shmget(IPC_PRIVATE, sizeof(atm_Account), IPC_CREAT | IPC_EXCL | 0777)) < 0) {
    10         perror("shmget error");
    11         return 1;
    12     }
    13 
    14     /** 进程共享内存映射 */
    15     atm_Account *a = (atm_Account *)shmat(shmid, 0, 0);
    16     if(a == (atm_Account *)-1){
    17         perror("shmat error");
    18         return 1;
    19     }
    20     a->code = 100001;
    21     a->balance = 10000;
    22     printf("balance: %f
    ", a->balance);
    23 
    24     pid_t pid;
    25     if((pid = fork()) < 0){
    26         perror("fork error");
    27         return 1;
    28     }
    29     else if(pid > 0){
    30         /** 父进程执行取款操作 */
    31         double amt = atm_account_Withdraw(a, 10000);
    32         printf("pid %d withdraw %f form code %d
    ", getpid(), amt, a->code);
    33         wait(0);
    34         /** 对共享内存的操作要在解除映射之前 */
    35         printf("balance: %f
    ", a->balance);
    36         shmdt(a);
    37     }
    38     else {
    39         /** 子进程也执行取款操作 */
    40         double amt = atm_account_Withdraw(a, 10000);
    41         printf("pid %d withdraw %f form code %d
    ", getpid(), amt, a->code);
    42         shmdt(a);
    43     }
    44 
    45     return ATM_ERROR_NONE;
    46 }

      atm_test.c

    1 #include "atm_handler.h"
    2 
    3 int main(void)
    4 {
    5     atm_handler_main();
    6     return 0;
    7 }

      Makefile

     1 #PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
     2 PROJECT_ROOT = $(shell pwd)
     3 SRC_DIR = $(PROJECT_ROOT)/src
     4 INCLUDE_DIR = $(PROJECT_ROOT)/include
     5 OBJ_DIR = $(PROJECT_ROOT)/obj
     6 BIN_DIR = $(PROJECT_ROOT)/bin
     7 
     8 
     9 # 找出 src 目录下的所有 .c 文件
    10 C_SRCS = $(wildcard $(SRC_DIR)/*.c)
    11 # 将所有的 src 下的 .c 文件替换为 .o 文件
    12 C_OBJS = $(patsubst %c, %o, $(C_SRCS))
    13 TARGET = test
    14 SHARE_LIB = libatm.a
    15 
    16 C_SRC_MAIN = atm_test.c
    17 
    18 CC = gcc
    19 CCFLAGS += fPIC
    20 LDFLAGS += -shared -fPIC
    21 ASFLAGS +=
    22 ARFLAGS = -crs
    23 LIBS_FLAGS = -L$(BIN_DIR)
    24 
    25 RM = rm -rf
    26  
    27 CFLAGS += -Wall -g -I$(INCLUDE_DIR)
    28 INCDIR += -I$(INCLUDE_DIR)
    29 
    30 
    31 
    32 .PHONY: all clean test
    33 
    34 all: $(TARGET)
    35     cp $(SHARE_LIB) $(BIN_DIR)
    36     cp $(SRC_DIR)/*.o $(OBJ_DIR)/
    37     $(RM) $(SHARE_LIB) $(SRC_DIR)/*.o
    38 
    39 $(TARGET): $(SHARE_LIB)
    40     $(CC) $(C_SRC_MAIN) -o $(TARGET) $(CFLAGS) $(INCDIR) $(LIBS_FLAGS) -latm
    41 
    42 $(SHARE_LIB): $(C_OBJS)
    43     $(AR) $(ARFLAGS) $(SHARE_LIB) $(C_OBJS)
    44     cp $(SHARE_LIB) $(BIN_DIR)
    45 
    46 $(C_OBJS) : %.o:%.c
    47     $(CC) -c $(CFLAGS) $(INCDIR) -o $@ $<
    48 
    49 clean:
    50     $(RM) mshell 
    51     $(RM) $(SHARE_LIB) 
    52     $(RM) $(OBJ_DIR)/$(OBJS)/*.o 
    53     $(RM) $(SRC_DIR)/*.o

      编译执行结果如下:

      

      两个账户都取款到 10000元,那是因为我们当前在共享内存的操作没有做互斥,下一节将添加互斥。

  • 相关阅读:
    现代软件工程 第一章 概论 第4题——邓琨
    现代软件工程 第一章 概论 第9题——邓琨
    现代软件工程 第一章 概论 第7题——张星星
    现代软件工程 第一章 概论 第5题——韩婧
    hdu 5821 Ball 贪心(多校)
    hdu 1074 Doing Homework 状压dp
    hdu 1074 Doing Homework 状压dp
    hdu 1069 Monkey and Banana LIS变形
    最长上升子序列的初步学习
    hdu 1024 Max Sum Plus Plus(m段最大子列和)
  • 原文地址:https://www.cnblogs.com/kele-dad/p/10317092.html
Copyright © 2011-2022 走看看