Linux进程间通信---共享内存

时间:2022-08-28 21:44:48
  • 共享内存允许多个进程共享一个给定的内存空间,进程可以直接读写内存,因此是IPC中速度最快的。
  • Linux中,内核专门留出了一块内存区作为共享内存区,用于多个进程交换信息。需要通信的进程将共享内存区映射到自己的私有地址空间,从而使读写进程地址空间就相当于读写内存区。使用共享内存的头文件是#include <sys/shm.h>
  • 由于多个进程读写同一块内存区,所以需要进行同步处理,一般要和信号量联合使用(也可使用互斥量和记录锁)。
    • Linux进程间通信---共享内存Linux进程间通信---共享内存
  • 共享内存段默认是32M字节。
  • 共享内存的操作流程(只使用于相关进程,即亲缘进程间的通信):
    • 创建/打开一块共享内存区
    • 把指定的共享内存映射到进程的地址空间
    • 撤销共享内存映射
    • 删除共享内存对象(key代表的IPC对象)
  • 共享内存常用函数:
    • shmget(key, size, flag):创建新的共享内存段或者取得已有的共享内存段,函数返回共享存储段的ID(shmid)。key标识共享内存的键值,两个进程用相同的key时用shmget得到的shmid是相同的,此时可以访问同一块共享内存。size表示获得的共享内存大小。flag表示共享内存块的访问权限,如果想共享内存段不存在时新建一个,用IPC_CREAT与权限值做位与操作即可。
    • shmat(shmid, addr, flag):将共享存储段映射到进程的地址空间。应当制定addr为0,由系统选择地址(该地址位于堆栈之间)。 flag是一组标志位,一般也为0。调用成功后,返回指向共享内存第一个字节的指针。
    • shmdt(addr):将进程地址空间与该共享内存段分离,使该共享内存对当前进程而言不可用。
    • shmctl(shm_id, command, buf):控制共享内存。command参数:IPC_RMID(删除共享内存段)、IPC_STAT和IPC_SET(不常用)
  • 无关进程(非亲缘进程)共享内存的方法:
    • 使用XSI共享存储函数
    • 使用mmap将同一文件映射到多个进程的地址空间,为此要使用MAP_SHARED标志以保证:一个进程写到存储段,另一个进程可见。
  • 使用共享内存的优缺点:
    • 优点:方便,接口简单;数据不用传送而是直接读写内存,效率高;没有无名管道那种亲缘进程才能通信的限制,适用于不相关进程通信。
    • 缺点:需要借助外部的同步机制
  • 共享内存的使用例子,创建两个进程,shmwrite向共享内存写数据,shmread从共享内存读数据:
    • shmread进程,创建一块共享内存段,将共享内存段映射到自己的内存空间,从内存中读数据。
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <sys/shm.h>
struct shared_use_st  
{  
    int wirte_read_flag;        //作为一个标志,非0:表示可读,0表示可写  
    char text[1024];              //记录写入和读取的文本  
};
int main()  
{  
    int running = 1;    //程序是否继续运行的标志  
    void *shm = NULL;    //分配的共享内存的原始首地址  
    struct shared_use_st *shared;   //指向shm  
    int shmid;         //共享内存标识ID号  
    //创建共享内存,当key一样时返回的shmid也是一样的,则两个进程使用同一块共享内存,IPC_CREAT表示创建一块指定key的共享内存块
    shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);  
    if(shmid == -1)  
    {  
        fprintf(stderr, "shmget failed\n");  
        exit(EXIT_FAILURE);  
    }  
    //将共享内存连接到当前进程的地址空间  
    shm = shmat(shmid, 0, 0);  
    if(shm == (void*)-1)  
    {  
        fprintf(stderr, "shmat failed\n");  
        exit(EXIT_FAILURE);  
    }  
    printf("\nMemory attached at %X\n", (int)shm);  
    //设置共享内存  
    shared = (struct shared_use_st*)shm;  
    shared->wirte_read_flag = 0;  //设为可写
    while(running)   //读取共享内存中的数据  
    {  
        //可读模式,从进程的地址空间shm里读就相当于从共享内存里读
        if(shared->wirte_read_flag != 0)  
        {  
            printf("Receive Message: %s", shared->text);  
            sleep(rand() % 3);  
            //读取完数据,设置wirte_read_flag使共享内存段可写  
            shared->wirte_read_flag = 0;  
            //输入了end,退出循环  
            if(strncmp(shared->text, "end", 3) == 0)  
                running = 0;  
        }  
        else        //有其他进程在写数据,不能读取数据  
            sleep(1);  
    }  
    //把共享内存从当前进程中分离  
    if(shmdt(shm) == -1)  
    {  
        fprintf(stderr, "shmdt failed\n");  
        exit(EXIT_FAILURE);  
    }  
    //删除共享内存  
    if(shmctl(shmid, IPC_RMID, 0) == -1)  
    {  
        fprintf(stderr, "shmctl(IPC_RMID) failed\n");  
        exit(EXIT_FAILURE);  
    }  
    exit(EXIT_SUCCESS);  
}  
    • shmwrite进程,取得共享内存,将共享内存映射到自己的内存空间,向内存中写数据。
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <string.h>  
#include <sys/shm.h>  
struct shared_use_st  
{  
    int wirte_read_flag;        //作为一个标志,非0:表示可读,0表示可写  
    char text[1024];              //记录写入和读取的文本  
};
int main()  
{  
    int running = 1;  
    void *shm = NULL;  
    struct shared_use_st *shared = NULL;  
    char buffer[1024 + 1];  //用于保存输入的文本  
    int shmid;  
    //创建共享内存,当key一样时,返回的shmid也是一样的,则两个进程访问同一块共享内存,IPC_CREAT表示创建一块指定key的共享内存
    shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);  
    if(shmid == -1)  
    {  
        fprintf(stderr, "shmget failed\n");  
        exit(EXIT_FAILURE);  
    }  
    //将共享内存连接到当前进程的地址空间  
    shm = shmat(shmid, (void*)0, 0);  
    if(shm == (void*)-1)  
    {  
        fprintf(stderr, "shmat failed\n");  
        exit(EXIT_FAILURE);  
    }  
    printf("Memory attached at %X\n", (int)shm);  
    //设置共享内存  
    shared = (struct shared_use_st*)shm;  
    while(running)    //向共享内存中写数据  
    {  
        //数据还没有被读取,则等待数据被读取,不能向共享内存写
        while(shared->wirte_read_flag == 1)  
        {  
            sleep(1);  
            printf("Waiting...\n");  
        }  
        //向共享内存中写入数据,向进程的地址空间shm里写就相当于往共享内存里写!  
        printf("Enter some text: ");  
        fgets(buffer, BUFSIZ, stdin);  
        strncpy(shared->text, buffer, 1024);  
        //写完数据,设置written使共享内存段可读  
        shared->wirte_read_flag = 1;  
        //输入了end,退出循环  
        if(strncmp(buffer, "end", 3) == 0)  
            running = 0;  
    }  
    //把共享内存从当前进程中分离  
    if(shmdt(shm) == -1)  
    {  
        fprintf(stderr, "shmdt failed\n");  
        exit(EXIT_FAILURE);  
    }  
    sleep(2);  
    exit(EXIT_SUCCESS);  
}  

(ps:以上程序转自http://blog.csdn.net/ljianhui/article/details/10253345