Linux 进程通信(共享内存区)

共享内存是由内核出于在多个进程间交换信息的目的而留出的一块内存区(段)。
如果段的权限设置恰当,每个要访问该段内存的进程都可以把它映像到自己的私有地址空间中。
如果一个进程更新了段中的数据,其他进程也立即会看到更新。
由一个进程创建的段,也可以由另一个进程读写。
每个进程都把它自己对共享内存的映像放入自己的地址空间。
创建共享内存区
int shmget(key_t key,size_t size,int shm-flg);
参数key既可以是IPC_PRIVATE(IPC_PRIVATE表示让系统分配一个Key),也可以是ftok函数返回的一个关键字。
参数size指定段的大小。
参数flgs--八进制数,0666,转化为二进制后分别代表rw-rw-rw-
shmget成功返回段标识符,失败返回-1(一般不会创建失败,除非系统没有内存).
注意:只有创建权限是0666的才可以用命令行"ipcs -m"查看,其他类型权限的共享内存区无法被这个命令所查看。
//创建共享内存
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <sys/ipc.h>
#include <sys/shm.h>

int main(int arg, char *args[])
{
    int shmid=shmget(IPC_PRIVATE,sizeof(char)*1024,0666);
    if(shmid==-1)
    {
        printf("error
");
        return -1;
    }
    printf("创建共享内存成功!内存段标识符是%d
",shmid);
    return 0;
}
在命令行执行"ipcs -m "显示已经成功的创建了一块共享内存区。
nattch字段显示已经附加到这个内存区的进程数。
附加共享内存区
void * shmat(int shmid,void * shmaddr,int shmflg);
int shmdt(const * void shmaddr);
参数shmid是要附加的共享内存区标识符(在命令行执行ipcs -m 显示)。
总是把参数shmaddr设为0,0表示系统会自动在被附加的进程内创建一个内存段,映射到共享内存区。当然也可以指定一个被附加的进程内的一块内存,但是需要用户自己malloc分配内存,并将地址传给shmaddr参数,比较麻烦,一般使用系统自动分配。
参数shmflg可以为SHM_RDONLY,这意味着附加段是只读的,参数为0时,表示可以读写。
shmat成功返回被附加了段的地址,失败返回-1,并设置errno,虽然返回值是指针类型,但是返回的的确是-1。
函数shmdt是将附加在shmaddr的段从调用进程的地址空间分离出去,这个地址必须是shmat返回的(即从自己进程内释放被附加的共享内存,强调不是释放系统的那个共享内存)。
注意:进程里附加共享内存区的大小一定是和系统共享内存一样大,为了方便映射(两块内存不一样大如何同步数据?)。
//创建附加共享内存
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/shm.h>

int main(int arg, char *args[])
{
    if (arg < 3)
    {
        printf("请输入2个参数!
");
        return -1;
    }
    int flag = 0;
    int shmid = 0;
    shmid = atoi(args[1]);
    flag = atoi(args[2]);
    //创建本进程被附加的共享内存
    void * shmbuf = NULL;
    shmbuf = shmat(shmid, 0, 0);
    /*
     编译警告:warning: cast from pointer to integer of different size
     这句警告意思是指针类型大小和Int类型大小不同,根据以前的知识,指针都是4个字节大小,
     其实指针的大小不是固定的4个字节。32位cpu的计算机上是4字节,64位cpu的计算机上是8个字节。
     这是因为32位计算机中的地址是32位的(可以在32位系统下查看指针的值例如--0x003bf9a3)
     但是在64位计算机中地址是64位的(可以在64位系统下查看指针的值例如--0xffffffffffffffff)
     十六进制数字的1位等于二进制的4位
     虽然指针的值在32位系统和64位系统中有区别,但是一个字节的大小在两个系统中是相同的,
     举例说Int类型数据,不管是在32位系统和64位系统中都在内存中占据4个字节大小的内存空间
     */
    if ((int) shmbuf == -1)
    {
        printf("创建附加共享内存区失败!error message:%s
", strerror(errno));
        return -1;
    }
    sleep(10);
    if (flag == 1)
    {
        //将数据写入本进程自己的附加共享内存区
        //这里系统共享内存区的大小是1kb
        read(STDIN_FILENO, shmbuf, sizeof(char) * 1024);
    } else if (flag == 2)
    {
        printf("%s", shmbuf);
    }
    return 0;
}
shmdt函数
--功能:将共享内存段与当前进程脱离
--参数
    shmarr:由shmat所返回的指针
--返回值:成功返回0,失败返回-1
--注意:将共享内存段与当前进程脱离不等于删除共享内存
释放共享内存区
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数shmid是共享内存区段标识符,
参数cmd一般有三个值IPC_STAT(获取共享内存区的状态,由第三个参数获取状态),IPC_SET(设置共享内存区),IPC_RMID(删除共享内存区,此时第三个参数一般传0);
shmctl成功返回0,失败返回-1,并且设置errno的值
//创建共享内存区
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main(int arg,char * args[])
{
    if(arg<2)
    {
        printf("请输入一个参数!
");
        return -1;
    }
    int shmid=atoi(args[1]);
    int flag=shmctl(shmid,IPC_RMID,0);
    if(flag<0)
    {
        printf("error message:%s
",strerror(errno));
        return -1;
    }
    printf("返回值是%d
",flag);
    return 0;
}
命令行释放共享内存区
在命令行执行"ipcrm -m shmid" 或者 "ipcrm shm shmid"
原文地址:https://www.cnblogs.com/zhanggaofeng/p/5839836.html