您的位置:首页 > 运维架构 > Linux

linux进程通信----共享内存

2013-07-06 14:08 465 查看
共享内存是一种最为高效的进程间通信方式,因为进程可以直接读写内存,不需要任何数据的复制,为了在多个进程间交换信息,内核专门留出了一块内存区,这块内存进程可以直接将其映射到进程的私有空间中,可以直接读写,所以大大提高了效率,由于多个进程共享一个内存区,所以需要依靠某种同步机制,例如信号量,互斥锁等。

共享内存原理示意图:



实现共享内存有三个步骤:

创建共享内存,使用shmget(),也就是从内存中获取一段共享内存区域
映射共享内存,把创建的共享内存映射到具体的进程空间中,使用函数shmat(),现在就可以使用共享内存了,可以使用不带缓冲的的I/O读写命令对其进行操作
撤销映射的操作,使用shmdt()函数即可

shmget()的函数语法格式:



shmat()函数语法如下:



shmdt语法如下:



这里需要补充一个删除共享内存的函数shmctl()



#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 2048
int main()
{
    pid_t pid ;
    int shmid ;
    char *shm_addr ;
    char flag[] = "WROTE" ;
    char *buff ;
    //创建共享内存
    if ((shmid = shmget(IPC_PRIVATE, BUFFER_SIZE, 0666)) < 0)
    {
        perror("shmget") ;
        exit(1) ;
    }
    else
    {
        printf("create shared-memory:%d\n", shmid) ;
    }
    system("ipcs -m") ;//通过ipcs -m获取共享内存的信息
    pid = fork() ;
    if (pid == -1)
    {
        perror("fork") ;
        exit(1) ;
    }
    else if (pid == 0)
    {
    //shm_addr是传回来的映射地址,判断其是否等于(void *)-1???
        if ((shm_addr = shmat(shmid, 0, 0)) == (void *)-1)
        {
            perror("child:shmat") ;
            exit (1) ;
        }
        else
        {
            printf("child:attach shared memeory:%p\n", shm_addr) ;
        }
        system("ipcs -m") ;//子进程映射之后查看共享内存的状态
        /*通过查看共享内存是否含有flag,如果有表示父进程已经向共享内存中
        写入了数据,以此做了一个简单的同步的工作*/
        while(strncmp(shm_addr, flag, strlen(flag)))
        {
            printf("child:wait for enable data...\n") ;
            sleep(5) ;
        }
        strcpy(buff, shm_addr + strlen(flag)) ;
        printf("child:shared-memory:%s\n", buff) ;
        if ((shmdt(shm_addr)) < 0)//撤销映射
        {
            perror("shmdt") ;
            exit(1) ;
        }
        else
        {
            printf("child:deattach shared-memory\n") ;
        }
        system("ipcs -m") ;//撤销映射之后共享内存的状态
        if (shmctl(shmid, IPC_RMID, NULL) == -1)//删除共享内存
        {
            perror("shmctl") ;
            exit(1) ;
        }
        else
        {
            printf("delete shared-memory\n") ;
        }
        system("ipcs -m") ;//删除共享内存之后,查看共享内存的状态
    }
    else
    {
        if ((shm_addr = shmat(shmid, 0, 0)) == (void *) - 1)//父进程映射内存
        {
            perror("shmat") ;
            exit(1) ;
        }
        else
        {
            printf("parent: attach shared-memory:%p\n", shm_addr) ;
        }
        sleep(1) ;
        printf("\ninput some string\n") ;
        fgets(buff, BUFFER_SIZE, stdin) ;//从标准输入中提取BUFFER_SIZE个字符
        strncpy(shm_addr + strlen(flag), buff, strlen(buff)) ;//向共享内存中写入
        strncpy(shm_addr, flag, strlen(flag)) ;
        if (shmdt(shm_addr) < 0)//解除映射
        {
            perror("shmdt") ;
            exit(1) ;
        }
        else
        {
            printf("deatch shared-memory\n") ;
        }
        system("ipcs -m") ;
        waitpid(pid, NULL, 0) ;//等待子进程结束
        printf("finished\n") ;
    }
    exit(0) ;
}
创建共享内存时我们指定键值为IPC_PRIVATE,用于创建当前进程的私有共享内存,这个例子中创建的共享内存是父子进程之间的共用部分,所以指定了IPC_PRIVATE

可以看一下运行结果:

在创建共享内存时:



父子进程分别作了映射:





此时看共享内存的状态:



然后我们向共享内存输入一个字符串"zhang",子进程将会读出这个字符串,然后父子进程分别解除映射

最后子进程删除共享内存,此时将查看不到共享内存.

因为共享内存是这里使用一个标志字符串作为同步的标志

这里有个问题弄不懂:

共享内存需要互斥访问,父子进程是怎么同步的?

子进程:
while(strncmp(shm_addr, flag, strlen(flag)))
   {
      printf("child:wait for enable data...\n") ;
      sleep(5) ;
   }

父进程:

printf("\ninput some string\n") ;
 fgets(buff, BUFFER_SIZE, stdin) ;//从标准输入中提取BUFFER_SIZE个字符
 strncpy(shm_addr + strlen(flag), buff, strlen(buff)) ;//向共享内存中写入
 strncpy(shm_addr, flag, strlen(flag)) ;
父子进程都对共享内存进行了操作,那怎么互斥访问呢?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: