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

    每个进程各自有不同的用户地址空间,任何一个进 程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲 区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)

    如下图所示:
    进程间通信共七种方式:
    第一类:传统的unix通信机制:

      # 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
      # 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

      # 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
     
    第二类:System V IPC: 
      # 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
      # 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
      # 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。

    第三类:BSD 套接字:
      # 套接字( socket ) : 套解字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
     
    介绍一下IPC之共享内存:
      共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。进程可以直接读取共享内存,不需要拷贝数据。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。
    步骤:
    共享内存的使用,主要有以下几个API:ftok()、shmget()、shmat()、shmdt()及shmctl()
    ①创建/打开共享内存
    int shmget(key_t key,int size,int shmflag);
    例:
    key = ftok(".", 'm');
    shmid = shmget(key,1024,0666|IPC_CREAT|IPC_EXCL);
    参数说明:
      key:是这块共享内存的标识符。如果是父子关系的进程间通信的话,这个标识符用IPC_PRIVATE来代替。如果两个进程没有任何关系,所以就用ftok()算出来一个标识符(或者自己定义一个)使用了。
      产生key的方法:key_t ftok(const char *pathname, int proj_id);
      1)pathname一定要在系统中存在并且进程能够访问的
      2)proj_id是一个1-255之间的一个整数值,典型的值是一个ASCII值。如'a',考虑到应用系统可能在不同的主机上应用,可以直接定义一个key,而不用ftok获得:

      #define IPCKEY 0x344378

      size:申请内存的大小
      shmflag:  这块内存的模式(mode)以及权限标识(0666等)。 
      模式可取如下值:        
      IPC_CREAT 新建(如果已创建则返回目前共享内存的id)
      IPC_EXCL   与IPC_CREAT结合使用,如果已创建则则返回错误 
      其他模式略
      申请的内存里面为空,即全为0
    ②映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
    void *shmat( int shmid , char *shmaddr , int shmflag );
    例:
    void *p;
    p=shmat(shmid,NULL,0);

    参数说明:

      shmid:是那块共享内存的ID。
      shmaddr:共享内存映射的起始地址,一般不指定即NULL
      shmflag:本进程对该内存的操作模式。如果是SHM_RDONLY的话,就是只读模式。0是读写模式
      返回值: 成功时,这个函数返回共享内存的起始地址。失败时返回-1。
    ③撤销共享内存映射,删除本进程对这块内存的使用
      int shmdt(const void* shmaddr);
      例:
      shmdt(p);
      参数说明:
      shmaddr:共享内存的起始地址。
      返回值:成功时返回0。失败时返回-1。
    ④删除共享内存对象
      int shmctl( int shmid , int cmd , struct shmid_ds *buf );
      例:
      shmctl(shmid, IPC_RMID, NULL);(删除共享内存)
      参数说明:
      shmid:共享内存的ID。
      cmd:控制命令,可取值如下:
            IPC_STAT        得到共享内存的状态
            IPC_SET         改变共享内存的状态
            IPC_RMID        删除共享内存
    buf:一个结构体指针。用以保存/设置属性。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定。
    返回值:成功时返回0。失败时返回-1。
     
    功能说明:两个进程通过共享内存一个写一个读并打印数据
    写:
    /*name:writer.c
     *function:写端进程向共享内存写数据
     * */
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <signal.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #define N 64
    typedef struct 
    {
        pid_t pid;
        char buf[N];
    } SHM;
    void handler(int signo)
    {
        //printf("get signal
    ");
        return;
    }
    int main()
    {
        key_t key;
        int shmid;
        SHM *p;
        pid_t pid;
        if ((key = ftok(".", 'm')) < 0)
        {
            perror("fail to ftok");
            exit(-1);
        }
        signal(SIGUSR1, handler);//注册一个信号处理函数
        if ((shmid = shmget(key, sizeof(SHM), 0666|IPC_CREAT|IPC_EXCL)) < 0)
        {
            if (EEXIST == errno)//存在则直接打开
            {
                shmid = shmget(key, sizeof(SHM), 0666);
                p = (SHM *)shmat(shmid, NULL, 0);
                pid = p->pid;
                p->pid = getpid();
                kill(pid, SIGUSR1);
            }
            else//出错
            {
                perror("fail to shmget");
                exit(-1);
            }
        }
        else//成功 
        {
            p = (SHM *)shmat(shmid, NULL, 0);
            p->pid = getpid();//把自己的pid写到共享内存
            pause();
            pid = p->pid;//得到读端进程的pid
        }
        while ( 1 )
        {
            printf("write to shm : ");
            fgets(p->buf, N, stdin);//接收输入
            kill(pid, SIGUSR1);//向读进程发SIGUSR1信号
            if (strcmp(p->buf, "quit
    ") == 0) break;
            pause();//阻塞,等待信号
        }
        shmdt(p);
        shmctl(shmid, IPC_RMID, NULL);//删除共享内存
        return 0;
    }

    读:

    /*name:reader.c
     *function:读端进程从共享内存中读数据
     * */
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <signal.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #define N 64
    typedef struct 
    {
        pid_t pid;
        char buf[N];
    } SHM;
    void handler(int signo)
    {
        //printf("get signal
    ");
        return;
    }
    int main()
    {
        key_t key;
        int shmid;
        SHM *p;
        pid_t pid;
        if ((key = ftok(".", 'm')) < 0)
        {
            perror("fail to ftok");
            exit(-1);
        }
        signal(SIGUSR1, handler);//注册一个信号处理函数
        if ((shmid = shmget(key, sizeof(SHM), 0666|IPC_CREAT|IPC_EXCL)) < 0)
        {
            if (EEXIST == errno)//存在则直接打开
            {
                shmid = shmget(key, sizeof(SHM), 0666);
                p = (SHM *)shmat(shmid, NULL, 0);
                pid = p->pid;
                p->pid = getpid();//把自己的pid写到共享内存
                kill(pid, SIGUSR1);
            }
            else//出错
            {
                perror("fail to shmget");
                exit(-1);
            }
        }
        else//成功
        {
            p = (SHM *)shmat(shmid, NULL, 0);
            p->pid = getpid();
            pause();
            pid = p->pid;//得到写端进程的pid
        }
        while ( 1 )
        {
            pause();//阻塞,等待信号
            if (strcmp(p->buf, "quit
    ") == 0) exit(0);//输入"quit结束"
            printf("read from shm : %s", p->buf);
            kill(pid, SIGUSR1);//向写进程发SIGUSR1信号
        }
        return 0;
    }
     
  • 相关阅读:
    JSJ—类与对象
    JSJ—案例谈面向对象
    实现简单神经网络
    SSM+Netty项目结合思路
    SSM-Netty实现软硬件通信,真实项目案例
    Netty实战十四之案例研究(一)
    Netty实战十三之使用UDP广播事件
    Netty实战十一之预置的ChannelHandler和编解码器
    Netty实战十二之WebSocket
    Netty实战十之编解码器框架
  • 原文地址:https://www.cnblogs.com/jiangzhaowei/p/5558750.html
Copyright © 2011-2022 走看看