IPC(四)---------共享内存

一、共享内存简介

      1、共享内存是被多个进程共享的一部分物理内存。

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

      3、共享内存是进程间共享数据的一种最快的方式,一种高效的IPC机制。

二、API

    1、创建共享内存

          int shmget(key_t key, size_t size , int shmflag);

          参数:

              key: 共享内存的键值,用户指定。

              size: 共享内存的大小。

             shmflag: IPC_CREAT、IPC_EXCL、0777等权限的组合

        返回:  成功,返回内核中共享内存的标识ID, 失败,返回-1。

  2、共享内存控制

         int shmctl ( int shmid, int cmd, struct shmid_ds *buf);

         参数:

              shmid:共享内存ID

              cmd: 

                      IPC_STAT  获取共享内存属性

                      IPC_SET   设置共享内存属性

                      IPC_RMID  删除共享内存

            buf : 共享内存属性指针

 

          返回: 成功返回0,出错返回-1

    3、共享内存映射

          void * shmat( int shmid ,char * shmaddr, int shmflag);

          参数:

               shmid : 共享内存ID

              shmaddr:  映射到进程虚拟内存空间的地址,一般设置0,由系统分配

              shmflag : 若shmaddr 设置0,shmflag也设置为0

         返回:成功返回共享内存映射到虚拟内存空间中的地址,失败返回-1.

         Note: 子进程不继承父进程创建的共享内存,大家是共享的,但是子进程继承父进程映射的地址。     

     4、共享内存解除映射

         int shmdt(char * shmaddr);

         返回:  失败返回-1.

三、代码实践

      父进程创建共享内存,父进程向共享内存中写数据,写完后通知子进程,子进程从共享内存中读数据。

      细节:1、父进程在未写完共享内存之前,子进程处于等待状态,此时的通知和阻塞暂时用管道的特性来模拟(后面用信号量取代)。

                 2、子进程继承父进程创建的管道,所以两个进程用完后都有将管道销毁

                 3、子进程只是继承了父进程共享内存映射到虚拟内存的地址,共享内存就一段,所以两个进程只销毁一次共享内存就够了,但都要解除共享内存映射。

    创建tell.c 和 tell.h 用管道的特性类实现两个进程的信息同步。

    

#include <stdio.h>
#include <unistd.h>
#include "tell.h"



int fd[2];

//管道初始化
void init()
{
    if(pipe(fd) < 0)
    {
        printf("pipe create error\n");
    }

}


//利用管道进行等待
void wait_pipe()
{
    char c;



    //利用管道默认是阻塞的特性
    if(read(fd[0],&c,1) < 0)
    {
        printf("pipe read error\n");
    }
}


//利用管道进行通知
void notify_pipe()
{
    char c = 'c';

    if(write(fd[1],&c,1) < 0)
    {
        printf("pipe write error\n");
    }

}


//销毁管道
void destory_pipe()
{
    close(fd[0]);
    close(fd[1]);

}

实现共享内存的进程间访问

#include <stdio.h>
#include "tell.h"
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>




/*  父进程创建共享内存,父进程向共享内存中写数据,
 *子进程从共享内存中读数据.
 * */



int main(void)
{

    //父进程创建共享内存,大小1024
    int shmId = shmget(IPC_PRIVATE,1024,IPC_CREAT|IPC_EXCL|0777);
    
    if(shmId < 0)
    {
        printf("share mem create error\n");

        return -1;
    }
    
    //初始化管道
    init();

    pid_t pid = fork();

    if(pid > 0)
    {//parent process
        //将共享内存映射到父进程虚拟内存空间中
        int *p = (int *)shmat(shmId,0,0);

        if(p == (int *) (-1))
        {
            printf("parent process shmat error\n");

            return -1;
        }
        
        //向共享内存中写入数据
        *p = 100;
        *(p+1) = 200;

        //通知数据已经写好了
        notify_pipe();

        //父进程共享内存结束映射
        shmdt((char *)p);

        //父进程创建的pipe,子进程也会继承,所以父子进程都有读端和写段两个管道,
        //用完了都需要将其销毁
        destory_pipe();

        
        //等待回收子进程
        wait(0);

    
    }else if(pid == 0)
    {//child parent

        //等待共享内存写好数据
        wait_pipe();
        //子进程将共享内存映射到自己虚拟空间中
        int *p = (int *)shmat(shmId,0,0);

        if(p == (int *)-1)
        {
            printf("child process shmat error\n");

            return -1;
        }
            
        //读共享内存    
        printf("share mem contex:start:%d,end:%d\n",*p,*(p+1));

        //子进程共享内存结束映射
        shmdt((char *)p);

        //子进程也要销毁管道
        destory_pipe();


        //共享内存不需要继承,只有一份,所以,在子进程或者父进程删除一次就行
        shmctl(shmId,IPC_RMID,NULL);
    }


    return 0;
}

测试结果:

  

猜你喜欢

转载自blog.csdn.net/weixin_40204595/article/details/112696725