zoukankan      html  css  js  c++  java
  • Linux进程间通信--共享内存

    本系列文章主要是学习记录Linux下进程间通信的方式。

    常用的进程间通信方式:管道、FIFO、消息队列、信号量以及共享存储。

    参考文档:《UNIX环境高级编程(第三版)》

    参考视频:Linux进程通信  推荐看看,老师讲得很不错

    Linux核心版本:2.6.32-431.el6.x86_64

    注:本文档只是简单介绍IPC,更详细的内容请查看参考文档和相应视频。

    本文介绍利用共享内存进行进程间的通信

    1  介绍

    • 共享存储允许两个或多个进程共享一个指定的存储区。当有进程正在往共享存储中写入数据时,其它进程不能从共享存储中取数据。通常使用信号量来同步共享存储的访问。
    • 多个进程都可把该共享内存映射到自己的虚拟内存空间。所有用户空间的进程若要操作共享内存,都要将其映射到自己虚拟内存空间中,通过映射的虚拟内存空间地址去操作共享内存,从而达到进程间的数据通信。
    • 共享内存是进程间共享数据的一种最快的方法。一个进程先共享内存区写入了数据,共享这个内存区域的所有进程就可以立即看到其中的内容。
    • 本身不提供同步机制,可通过信号量进行同步。
    • 提升数据处理效率,一种效率最高的IPC机制。

    2  共享内存属性

     1 struct shmid_ds {
     2     struct ipc_perm shm_perm;    /* Ownership and permissions */
     3     size_t          shm_segsz;   /* Size of segment (bytes) */
     4     time_t          shm_atime;   /* Last attach time */
     5     time_t          shm_dtime;   /* Last detach time */
     6     time_t          shm_ctime;   /* Last change time */
     7     pid_t           shm_cpid;    /* PID of creator */
     8     pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
     9     shmatt_t        shm_nattch;  /* No. of current attaches */
    10     ...
    11 };

    3  使用步骤

    1. 使用shmget函数创建共享内存;
    2. 使用shmat函数创建共享内存,将这段创建的共享内存映射到具体的进程虚拟内存空间中。

    4  函数原型

    1 #include <sys/ipc.h>
    2 #include <sys/shm.h>
    3 int shmget(key_t key, size_t size, int shmflg);
    4 说明:创建共享内存
    5 返回:成功返回内存中共享内存的的标识ID;失败返回-16 参数key:用户指定的共享内存键值;
    7 参数size:共享内存大小;
    8 参数shmflg:IPC_CREAT、IPC_EXCL等权限组合。
    1 #include <sys/ipc.h>
    2 #include <sys/shm.h>
    3 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    4 说明:共享内存控制
    5 参数shmid:共享内存ID;
    6 参数buf:共享内存属性指针;
    7 参数cmd:IPC_STAT:获取共享内存段属性;IPC_SET:设置共享内存段属性;
    IPC_RMID:删除共享内存段;SHM_LOCK:锁定共享内存段页面(页面映射到屋里内存不和外存进行换入换出操作);
    SHM_UNLOCK:解除共享内存段页面的锁定。
     1 #include <sys/types.h>
     2 #include <sys/shm.h>
     3 void *shmat(int shmid, const void *shmaddr, int shmflg);
     4 说明:共享内存映射
     5 返回:成功返回共享内存映射到进程虚拟内存空间中的地址;失败返回-1 6 int shmdt(const void *shmaddr);
     7 说明:共享内存解除映射;
     8 返回:如果失败,则返回-1 9 参数shmid:共享内存ID;
    10 参数shmaddr:映射到进程虚拟内存空间的地址,建议设置为0,由操作系统分配;
    11 参数shmflg:若shmaddr设置为0,则shmflg也设置为0。SHM_RND:随机;SHMLBA:地址为2的乘方;SHM_RDONLY:只读方式链接。
    12 注:子进程不继承父进程创建的共享内存,大家是共享的,子进程继承父进程映射的地址。

    5  实现案例

    (1)案例一:同步使用共享内存

    父进程向共享内存中写入数据,子进程等待父进程写入数据并从中读取数据。对共享进程的同步使用管道实现。

    管道的头文件:

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

    管道的C文件:

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

    父子进程对共享进程的访问:

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

    测试步骤:

    1、编译:[root@192 ipc]# gcc -o bin/cal_shm -Iinclude tell.c cal_shm.c 

    2、运行:

  • 相关阅读:
    Codeforces Round 546 (Div. 2)
    Codeforces Round 545 (Div. 2)
    Codeforces Round 544(Div. 3)
    牛客小白月赛12
    Codeforces Round 261(Div. 2)
    Codeforces Round 260(Div. 2)
    Codeforces Round 259(Div. 2)
    Codeforces Round 258(Div. 2)
    Codeforces Round 257 (Div. 2)
    《A First Course in Probability》-chaper5-连续型随机变量-随机变量函数的分布
  • 原文地址:https://www.cnblogs.com/mrlayfolk/p/13047317.html
Copyright © 2011-2022 走看看