首页 > 程序开发 > 软件开发 > 其他 >

进程通信之共享内存

2017-02-20

进程通信之共享内存:在通信的进程之间存在一块可直接访问的共享空间,通过对这片共享空间进行写 读操作实现进程之间的信息交换。

进程通信之共享内存:在通信的进程之间存在一块可直接访问的共享空间,通过对这片共享空间进行写/读操作实现进程之间的信息交换。
共享存储是进程通信中最灵活,速度最快的通信方式。因为挂接到同一块内存区域的两个进程可以直接访问内存区域。而其他几种方式需要拷贝到内核,再从内核拷贝出去,进行通信。
需要注意的是,用户进程空间一般都是独立的,要想让两个用户进程共享空间必须通过特殊的系统调用实现,而进程内的线程是自然共享进程空间的。
需要用到的函数有:
1、获得一个共享内存的标识符

#include
int shmget(key_t key, size_size, int flags);

参数key是唯一标识,可以由ftok函数产生
参数size是该共享存储段的长度(单位:字节)。实现通常将其向上取为系统页长的整数倍。但是,若应用指定的size值并非系统页长的整数倍,那么最后一页的余下部分是不可使用的。如果正在创建一个新段(一般是在服务器进程中),则必须指定其size。如果正在引用一个现存的段(一个客户进程),则将size指定为0。当创建一新段时,段内的内容初始化为0。
参数flags指定创建共享内存段的方式,是新建还是引用。
2、shmctl函数对共享存储段执行多种操作。

include 
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//返回值:若成功则返回0,若出错则返回-1

cmd参数指定下列5中命令中一种,使其在shmid指定的段上执行。
IPC_STAT 取此段的shmid_ds结构,并将它存放在由buf指向的结构中。
IPC_SET 按buf指向结构中的值设置与此段相关结构中的下列三个字段:shm_perm.uid、shm_perm.gid以及shm_perm.mode。此命令只能由下列两种进程执行:一种是其有效用户ID等于shm_perm.cuid或shm_perm.uid的进程;另一种是具有超级用户特权的进程。
IPC_RMID 从系统中删除该共享存储段。因为每个共享存储段有一个连接计数(shmid_ds结构中的shm_nattach字段),所以除非使用该段的最后一个进程终止或与该段脱节,否则不会实际上删除该存储段。不管此段是否仍在使用,该段标识符立即被删除,所以不能再用shmat与该段连接。此命令只能由下列两种进程执行:一种是其有效用户ID等于shm_perm.cuid或shm_perm.uid的进程,另一种是具有超级用户特权的进程。
3、创建了一个共享存储段,进程就可调用shmat将其连接到它的地址空间中。

include 
void *shmat(int shmid, const void *addr, int flag);
//返回值:若成功则返回指向共享存储的指针,若出错则返回-1

共享存储段连接到调用进程的哪个地址上与addr参数以及在flag中是否指定SHM_RND位有关。
如果addr为0,则此段连接到由内核选择的第一个可用地址上。这是推荐的使用方式。
如果addr非0,并且没有指定SHM_RND,则此段连接到addr所指定的地址上。
如果addr非0,并且指定了SHM_RND,则此段连接到(addr-(addr mod ulus SHMLBA))所表示的地址上。SHM_RND命令的意思是“取整”。SHMLBA的意思是“低边界地址倍数”,它总是2的乘方。该算式是将地址向下取最近1个SHMLBA的倍数。
除非只计划在一种硬件上运行应用程序(这在当今是不大可能的),否则不应指定共享段所连接到的地址。所以一般应指定addr为0,以便由内核选择地址。
如果在flag中指定了SHM_RDONLY位,则以只读方式连接此段。否则以读写方式连接此段。
shmat的返回值是该段所连接的实际地址,如果出错则返回-1。如果shmat成功执行,那么内核将使该共享存储段shmid_ds结构中的shm_nattach计数器值加1.
当对共享存储段的操作已经结束时,则调用shmdt脱接该段。注意,这并不从系统中删除其标识符以及数据结构。该标识符仍然存在,直至某个进程(一般是服务器进程)调用shmctl(带命令IPC_RMID)特地删除它。
4、取消挂接

include 
int shmdt(void *addr);
//返回值:若成功则返回0,若出错则返回-1

addr参数是以前调用shmat时的返回值。如果成功,shmdt将使相关shmid_ds结构中的shm_nattach计数器值减1。
附测试代码:

//Makefile
cli=client
cc=gcc
ser=server
serSrc=comm.c server.c
cliSrc=comm.c client.c

.PHONY:all
all:$(cli) $(ser)

$(cli):$(cliSrc)
    $(cc) -o $@ $^
$(ser):$(serSrc)
    $(cc) -o $@ $^

.PHONY:clean
clean:
    rm -f $(cli) $(ser)

//comm.h
#ifndef _COMM_
#define _COMM_

#include
#include
#include
#include

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

int creatShm();
int getShm();
int destoryShm(int shmid);
#endif

//comm.c

#include"comm.h"

static int commShm(int flags)
{
    //ftok函数生成key_t值。
    key_t _k=ftok(PATHNAME, PROJID);
    if(_k < 0)
    {
        perror("ftok");
        return -1;
    }
    //shmget函数获取共享存储标识符ID
    int shmid = shmget(_k, SIZE, flags);
    if(shmid < 0)
    {
        perror("shmget");
        return -2;
    }
    return shmid;
}
int getShm()
{
    return commShm(IPC_CREAT); 
}
int creatShm()
{
    return commShm(IPC_CREAT|IPC_EXCL|0666 );
}
int destoryShm(int shmid)
{
    //shmctl函数对共享内存段执行多种操作,此处执行销毁操作
    if(shmctl(shmid, IPC_RMID,NULL) < 0)
    {
        perror("shmctl");
        return -1;
    }
    return 0;
}
//server.c

#include "comm.h"
int main()
{
    int shmid =  creatShm();
    //调用shmath函数将次进程挂接到共享内存段(类似于malloc函数)
    char *mem = (char *)shmat(shmid,NULL, 0);
    while(1)
    {
        sleep(1);
        printf("%s\n",mem);
    }
    //shmdt函数进行去挂接操作
    shmdt(mem);
    destoryShm(shmid);
    return 0;
}

//client.c
#include "comm.h"

int main()
{
    int shmid =  getShm();
    char *mem = (char *)shmat(shmid,NULL, 0);
    int i = 0;
    while(1)
    {
        sleep(1);
        mem[i++]=&#39;S&#39;;
        i %= (SIZE-1);
        mem[i] = &#39;\n&#39;;
    }
    shmdt(mem);
    return 0;
}


相关文章
最新文章
热点推荐