• 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、运行:

  • 相关阅读:
    数据结构01-线性表
    java-04流程控制语句
    从0开始的Python学习002python的数据类型
    从0开始的Python学习001快速上手手册
    MySQl ifnull()和substr()
    parent.fraInterface.xxxxxx
    身份证的校验规则
    onclick="return function()"的使用情况
    jsp include 报错:illegal to have multiple occurrences of contentType with different values (old: text/html; charset=UTF-8, new: text/html; carset=UTF-8)
    Oracle数据库忘记用户名密码的解决方案
  • 原文地址:https://www.cnblogs.com/mrlayfolk/p/13047317.html
Copyright © 2020-2023  润新知