Linux进程间通信之共享存储

共享存储


所谓共享存储,就是一块共享的内存区域,它可被多个进程以读或写的形式访问,以达到进程间通信的目的。
因为这个通信方式,发送端与接收端不需要来回复制要发送接收的信息,所以共享存储是最快的一种IPC(进程间通信方式)。
既然共享存储允许多个进程去访问,那么它必须满足同步与互斥原则,在发送端正在写的时候,接收端不应该去读。通常我们使用信号量或者互斥量来达到这一目的。

Linux进程间通信之共享存储_第1张图片

共享内存同消息队列,信号量相同,同属于XSI IPC。内核都为他们维护了一套数据结构。
共享内存的数据结构:

struct shmid_ds
{
    struct ipc_perm shm_perm;
    size_t shm_segsz;
    pid_t shm_lpid;
    pid_t shm_cpid;
    shmatt_t shm_nattch;//shmatt_t定义为无符号整型,他至少和unsigned short一样大。
    time_t shm_atime;
    time_t shm_dtime;
    time_t shm_ctime;
    ...
}

同样,系统会给每一个共享内存资源设立一个唯一的标识符key,我们可以通过函数ftok得到这个key。数据类型为key_t。

#include
key_t ftok(const char* path,int id);

path指向一个字符串,表示路径名,我们一般设为当前路径“.”,id为项目id,一般指定为一个0~255之间的数。

操作函数


获得共享存储


#include
int shmget(key_t key,size_t size,int flag)

返回值:成功返回共享存储区的shmid,若失败,返回-1。
参数key即为我们调用ftok函数得到的标识符,作为系统对此共享存储资源的唯一标识。
参数size是要申请的共享存储区的大小,以字节为单位,这个数一般为系统页长的整数倍(4K的整数倍)。若申请的size不是页长的整数倍,系统也是按整数倍进行分配的,但是最后一页余下的部分不可用。如果创建一个新的资源,则必须指定size,且段的内容会被初始化为0。若引用一个已经存在的,则将size置为0。
参数flag与消息队列相同,有两个选项,IPC_CREAT和IPC_EXCL。使用的时候有两种情况:
1. IPC_CREAT和IPC_EXCL一起使用(IPC_CREAT|IPC_EXCL),表示申请创建一个新的IPC资源,若要申请的资源已经存在,则错误返回。若不存在,则创建。
2. IPC_CREAT单独使用,表示申请创建一个IPC资源,若要申请的IPC资源已经存在,则直接使用;若不存在,则创建新的。
一般我们还会在后面加上资源的默认权限(如0666)。

删除共享存储


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

返回值:成功返回0,失败返回-1。
参数shmid即为我们调用shmget得到的共享存储的id。
参数cmd指定了要执行了命令,当cmd被设置为IPC_RMID时,该函数执行删除动作。此时,第三个参数设为NULL。
除此之外,cmd还有IPC_STAT,IPC_SET等命令。

将进程挂接到共享存储上


#include
void* shmat(int shmid,const void* attr,int flag);

返回值:若成功,返回指向共享内存存储的指针,若失败,返回-1。
shmid不再多说,attr参数决定了共享存储段连接到调用进程的那个地址。一般我们设置为NULL或0,表示此段连接到由内核选择的第一个可用地址上。这是推荐的使用方式。
如果参数flag指定了SHM_RDONLY,则以只读方式连接,否则以读写的方式连接。
如果此函数成功执行的话,那么内核将使与该共享存储段相关的shmid_ds结构中的shm_nattch计数器值加1。

将进程从共享存储上卸载


#include
int shmdt(const void* addr)

返回值:成功返回0,失败返回-1。
参数addr是之前调用shmat的返回值。如果函数执行成功的话,shmdt将使相关shmid_ds结构中的shm_nattch计数器减1。
当使用完共享存储后,调用此函数令进程与它分离,但共享存储并不会消失,其标识符仍然还在,直到有进程调用shmctl将其删除。

查看系统的共享存储资源


系统同样为我们提供了查看共享内存IPC资源的指令:

ipcs -m

Linux进程间通信之共享存储_第2张图片

删除共享内存资源:

ipcrm -m [shmid]

以共享存储实现客户机-服务器模式


头文件comm.h


#ifndef __COMM__
#define __COMM__

#include
#include
#include
#include

#define PATHNAME "."
#define PROJID 0X666
#define SIZE 4096*1

int commMem(int flags);
int creatMem();
int getMem();
int destoryMem(int shmid);


#endif  //__COMM__

源文件comm.c


#include"comm.h"

int commMem(int flags)
{
    key_t _k = ftok(PATHNAME,PROJID);
    if(_k < 0)
    {
        perror("ftok");
        return -1;
    }
    int shmid = shmget(_k,SIZE,flags);
    if(shmid < 0)
    {
        perror("shmget");
        return -1;
    }
    return shmid;
}

int creatMem()
{
    return commMem(IPC_CREAT | IPC_EXCL | 0666);
}

int getMem()
{
    return commMem(IPC_CREAT);
}

int destoryMem(int shmid)
{
    int ret = shmctl(shmid,IPC_RMID,NULL);
    if(ret < 0)
    {
        perror("destory shmctl");
        return -1;
    }
    return 0;
}

客户机文件client.c

#include"comm.h"

int main()
{
    printf("client\n");
    int shmid = getMem();
    if(shmid < 0)
    {
        printf("getMem error !");
        return -1;
    }
    printf("shmid : %d\n",shmid);
    sleep(5);
    char* addr = shmat(shmid,NULL,0);
    if(addr == NULL)
    {
        printf("NULL!!!!!!!\n");
    }
    //printf("Debug addr %s",addr);
    if(addr < 0)
    {
        perror("client shmat");
        return -1;
    }
    int i = 0;
    while(i < SIZE - 1)
    {
        addr[i] = 'L';
        addr[i+1] = '\0';
        sleep(1);
        i++;
    }
    int ret = shmdt(addr);
    printf("Debug shmdt \n");
    if(ret < 0)
    {
        perror("client shmdt");
        return -1;
    }
    return 0;
}

服务器文件server.c


#include"comm.h"

int main()
{
    int shmid = creatMem();
    sleep(10);
    if(shmid < 0)
    {
        printf("CreatMem error !\n");
        return -1;
    }
    char *addr = shmat(shmid,NULL,0);
    if(addr < 0)
    {
        perror("shmat");
        return -1;
    }
    while(1){
        printf("#\n");
        printf("%s\n",addr);
        printf("#\n");
        sleep(1);
    }
    int ret = shmdt(addr);

    if(ret < 0)
    {
        perror("shmdt");
        return -1;
    }
    int retdes = destoryMem(shmid);
    if(retdes < 0)
    {
        printf("destoryMem error !\n");
        return -1;
    }

    return 0;
}

你可能感兴趣的:(Linux学习笔记,Linux学习之路)