mmap&&shm mmap文件阅读器

调用mmap()通过映射一个普通文件实现共享内存。系统V则是通过映射特殊文件系统shm中的文件实现进程间的共享内存通信。也就是说,每个共享内存区域对应特殊文件系统shm中的一个文件(这是通过shmid_kernel结构联系起来的),后面还将阐述。

1、系统V共享内存原理

进程间需要共享的数据被放在一个叫做IPC共享内存区域的地方,所有需要访问该共享区域的进程都要把该共享区域映射到本进程的地址空间中去。系统V共享内存通过shmget获得或创建一个IPC共享内存区域,并返回相应的标识符。内核在保证shmget获得或创建一个共享内存区,初始化该共享内存区相应的shmid_kernel结构注同时,还将在特殊文件系统shm中,创建并打开一个同名文件,并在内存中建立起该文件的相应dentry及inode结构,新打开的文件不属于任何一个进程(任何进程都可以访问该共享内存区)。所有这一切都是系统调用shmget完成的。

注:每一个共享内存区都有一个控制结构structshmid_kernel,shmid_kernel是共享内存区域中非常重要的一个数据结构,它是存储管理和文件系统结合起来的桥梁,定义如下:

struct shmid_kernel {               struct kern_ipc_perm    shm_perm;        struct file *           shm_file;        int                     id;        unsigned long           shm_nattch;        unsigned long           shm_segsz;        time_t                  shm_atim;        time_t                  shm_dtim;        time_t                  shm_ctim;        pid_t                   shm_cprid;        pid_t                   shm_lprid;};

该结构中最重要的一个域应该是shm_file,它存储了将被映射文件的地址。每个共享内存区对象都对应特殊文件系统shm中的一个文件,一般情况下,特殊文件系统shm中的文件是不能用read()、write()等方法访问的,当采取共享内存的方式把其中的文件映射到进程地址空间后,可直接采用访问内存的方式对其访问。

这里我们采用[1]中的图表给出与系统V共享内存相关数据结构:



正如消息队列和信号灯一样,内核通过数据结构struct ipc_idsshm_ids维护系统中的所有共享内存区域。上图中的shm_ids.entries变量指向一个ipc_id结构数组,而每个ipc_id结构数组中有个指向kern_ipc_perm结构的指针。到这里读者应该很熟悉了,对于系统V共享内存区来说,kern_ipc_perm的宿主是shmid_kernel结构,shmid_kernel是用来描述一个共享内存区域的,这样内核就能够控制系统中所有的共享区域。同时,在shmid_kernel结构的file类型指针shm_file指向文件系统shm中相应的文件,这样,共享内存区域就与shm文件系统中的文件对应起来。

在创建了一个共享内存区域后,还要将它映射到进程地址空间,系统调用shmat()完成此项功能。由于在调用shmget()时,已经创建了文件系统shm中的一个同名文件与共享内存区域相对应,因此,调用shmat()的过程相当于映射文件系统shm中的同名文件过程,原理与mmap()大同小异。

回页首

2、系统V共享内存API

对于系统V共享内存,主要有以下几个API:shmget()、shmat()、shmdt()及shmctl()。

#include #include 

shmget()用来获得共享内存区域的ID,如果不存在指定的共享区域就创建相应的区域。shmat()把共享内存区域映射到调用进程的地址空间中去,这样,进程就可以方便地对共享区域进行访问操作。shmdt()调用用来解除进程对共享内存区域的映射。shmctl实现对共享内存区域的控制操作。这里我们不对这些系统调用作具体的介绍,读者可参考相应的手册页面,后面的范例中将给出它们的调用方法。

注:shmget的内部实现包含了许多重要的系统V共享内存机制;shmat在把共享内存区域映射到进程空间时,并不真正改变进程的页表。当进程第一次访问内存映射区域访问时,会因为没有物理页表的分配而导致一个缺页异常,然后内核再根据相应的存储管理机制为共享内存映射区域分配相应的页表。

回页首

3、系统V共享内存限制

在/proc/sys/kernel/目录下,记录着系统V共享内存的一下限制,如一个共享内存区的最大字节数shmmax,系统范围内最大共享内存区标识符数shmmni等,可以手工对其调整,但不推荐这样做。

在[2]中,给出了这些限制的测试方法,不再赘述。

回页首

4、系统V共享内存范例

本部分将给出系统V共享内存API的使用方法,并对比分析系统V共享内存机制与mmap()映射普通文件实现共享内存之间的差异,首先给出两个进程通过系统V共享内存通信的范例:

#include #include #include #include typedef struct{        char name[4];        int age;} people;main(int argc, char** argv){        int shm_id,i;        key_t key;        char temp;        people *p_map;        char* name = "/dev/shm/myshm2";        key = ftok(name,0);        if(key==-1)                perror("ftok error");        shm_id=shmget(key,4096,IPC_CREAT);              if(shm_id==-1)        {                perror("shmget error");                return;        }        p_map=(people*)shmat(shm_id,NULL,0);        temp='a';        for(i = 0;i<10;i++)        {                temp+=1;                memcpy((*(p_map+i)).name,&temp,1);                (*(p_map+i)).age=20+i;        }        if(shmdt(p_map)==-1)                perror(" detach error ");}#include #include #include #include typedef struct{        char name[4];        int age;} people;main(int argc, char** argv){        int shm_id,i;        key_t key;        people *p_map;        char* name = "/dev/shm/myshm2";        key = ftok(name,0);        if(key == -1)                perror("ftok error");        shm_id = shmget(key,4096,IPC_CREAT);            if(shm_id == -1)        {                perror("shmget error");                return;        }        p_map = (people*)shmat(shm_id,NULL,0);        for(i = 0;i<10;i++)        {        printf( "name:%sn",(*(p_map+i)).name );        printf( "age %dn",(*(p_map+i)).age );        }        if(shmdt(p_map) == -1)                perror(" detach error ");}

testwrite.c创建一个系统V共享内存区,并在其中写入格式化数据;testread.c访问同一个系统V共享内存区,读出其中的格式化数据。分别把两个程序编译为testwrite及testread,先后执行./testwrite及./testread则./testread输出结果如下:

name: b       age 20; name: c age 21; name: d age 22; name: e age 23; name: f age 24;name: g age 25; name: h age 26; name: I age 27; name: j age 28; name: k age 29;

通过对试验结果分析,对比系统V与mmap()映射普通文件实现共享内存通信,可以得出如下结论:

1、系统V共享内存中的数据,从来不写入到实际磁盘文件中去;而通过mmap()映射普通文件实现的共享内存通信可以指定何时将数据写入磁盘文件中。注:前面讲到,系统V共享内存机制实际是通过映射特殊文件系统shm中的文件实现的,文件系统shm的安装点在交换分区上,系统重新引导后,所有的内容都丢失。

2、系统V共享内存是随内核持续的,即使所有访问共享内存的进程都已经正常终止,共享内存区仍然存在(除非显式删除共享内存),在内核重新引导之前,对该共享内存区域的任何改写操作都将一直保留。

3、通过调用mmap()映射普通文件进行进程间通信时,一定要注意考虑进程何时终止对通信的影响。而通过系统V共享内存实现通信的进程则不然。注:这里没有给出shmctl的使用范例,原理与消息队列大同小异。

回页首

结论:

共享内存允许两个或多个进程共享一给定的存储区,因为数据不需要来回复制,所以是最快的一种进程间通信机制。共享内存可以通过mmap()映射普通文件(特殊情况下还可以采用匿名映射)机制实现,也可以通过系统V共享内存机制实现。应用接口和原理很简单,内部机制复杂。为了实现更安全通信,往往还与信号灯等同步机制共同使用。

共享内存涉及到了存储管理以及文件系统等方面的知识,深入理解其内部机制有一定的难度,关键还要紧紧抓住内核使用的重要数据结构。系统V共享内存是以文件的形式组织在特殊文件系统shm中的。通过shmget可以创建或获得共享内存的标识符。取得共享内存标识符后,要通过shmat将这个内存区映射到本进程的虚拟地址空间。

charfilepath[] = "./.bns";
typedefstruct{
char name[4];
int age;
}people;

intfd,i;
people*p_map;
chartemp;

//openfile
fd=open(filepath, O_CREAT|O_RDWR, 00666);
if(fd< 0)
{
printf("open file failed : %sn", strerror(errno));
mmap&&shm mmap文件阅读器
return -1;
}

printf("fileopenedn");

if(ftruncate(fd, 4096)==-1)
{
perror("ftruncate fail:");
close(fd);
exit(1);
}

//createmmap
p_map =(people*) mmap(NULL, sizeof(people)*100, PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0);
close( fd);

//writemmap
temp ='a';
for(i=0;i<100; i++)
{
temp += 1;
memcpy(( *(p_map+i)).name, &temp, 2);
(*(p_map+i)).age = 20+i;
}

i =msync((void *)p_map, sizeof(people)*100, MS_SYNC);


printf("initialize over %d %dn", i, errno);

//unmapthe mmap
//munmap((char*)p_map, sizeof(people)*100);
printf("umapokn");

FILE *fp;
charbuffer[1024];
errno =0;

fp=popen("get_instance_by_service cccc -i -T 1", "r");
printf("000000 %d n", errno);

while(fgets(buffer, 1000, fp) != NULL)
{
printf("%s ", buffer);
}

typedefstruct{
char name[4];
int age;
}people;

charfilepath[] = "./.bns";
intfd,i;
people*p_map;

//openfile
fd=open(filepath,O_CREAT|O_RDWR,00666 );

//createmmap and read
p_map =(people*)mmap(NULL, sizeof(people)*100, PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
for(i =0;i<100;i++)
{
printf( "name: %s age %d;n",(*(p_map+i)).name, (*(p_map+i)).age);
}

//unmap
munmap((char*)p_map,sizeof(people)*100);

posix共享内存

1、posix共享内存区涉及两个步骤:

1)指定一个名字参数调用shm_open,以创建一个新的共享内存区对象或打开一个以存在的共享内存区对象。

2)调用mmap把这个共享内存区映射到调用进程的地址空间。传递给shm_open的名字参数随后由希望共享该内存区的任何其他进程使用。

2、函数

(1)创建

#include

int shm_open(const char *name,int oflag,mode_tmode);

1)name 共享内存区的名字

2)oflag 标志位

3)mode 权限位,它指定O_CREAT标志的前提下使用。

(2)删除

int shm_unlink(const char *name);

(3)调整大小

普通文件或共享内存区对象的大小都可以通过调用ftruncate修改。

int ftruncate(int fd,off_t length);

(4)查看状态

当打开一个已存在的共享内存区对象时,我们可调用fstat来获取有关该对象的信息。

#include

#include

#include

int stat(const char *file_name,struct stat*buf);

对于普通文件stat结构可以获得12个以上的成员信息,然而当fd指代一个共享内存区对象时,只有四个成员含有信息。

struct stat

{

mode_t st_mode;

uid_t st_uid;

gid_t st_gid;

off_t st_size;

};

g++ -g -o statt fstatt.cpp -lrt

3、示例

示例1

ViewCode
#include #include #include #include int main(int argc,char **argv){    int shm_id;    struct stat buf;    char *ptr;    if(argc!=2)    {        printf("usage:shm_open n");        exit(1);    }    shm_id=shm_open(argv[1],O_RDWR|O_CREAT,0644);    ftruncate(shm_id,100);    fstat(shm_id,&buf);    ptr=mmap(NULL,buf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,shm_id,0);    strcpy(ptr,"hello linux");    printf("%sn",ptr);    shm_unlink(argv[1]);}
复制代码

示例2

pshm_w
#include #include #include #include #include #include #include #include int main(int argc,char **argv){    int shm_id;    char *ptr;    sem_t *sem;        if(argc != 2)    {        printf("usage:shm_open n");        exit(1);    }    shm_id = shm_open(argv[1], O_RDWR, 0644);    sem = sem_open(argv[1], O_RDWR);    ptr = (char*)mmap(NULL,100,PROT_READ | PROT_WRITE, MAP_SHARED, shm_id, 0);    while(1)    {         sem_wait(sem);         fgets(ptr, 15, stdin);         printf("user:%s", ptr);         if((strcmp(ptr, "qn")) == 0)             exit(0);         sem_post(sem);         sleep(1);     }     return (0);}
复制代码pshm_r
#include #include #include #include #include #include #include #include int main(int argc,char **argv){    int shm_id;    char *ptr;    sem_t *sem;        if(argc != 2)    {        printf("usage:shm_open n");        exit(1);    }    shm_id = shm_open(argv[1], O_RDWR | O_CREAT, 0644);    ftruncate(shm_id, 100);    sem = sem_open(argv[1], O_CREAT, 0644, 1);    ptr = (char*)mmap(NULL, 100, PROT_READ | PROT_WRITE, MAP_SHARED, shm_id, 0);    strcpy(ptr, ""); //清空共享内存区    while(1)    {        if((strcmp(ptr, "")) ==0)        {            continue;        }            else        {             if((strcmp(ptr, "qn"))==0)                break;             sem_wait(sem);             printf("server:%s", ptr);             strcpy(ptr, "");             sem_post(sem);         }         sem_unlink(argv[1]);         shm_unlink(argv[1]);     }

  

爱华网本文地址 » http://www.413yy.cn/a/25101011/49069.html

更多阅读

手机文件管理器哪个好? 手机文件管理器

手机文件管理器哪个好?——简介不明觉厉 极助手小而美——从此告别数据线连接之后,通过电脑浏览器便可直接管理手机内的文件,支持所有基础操作如复制,粘贴,重命名等。此外,文件采用全智能分类,图片,音乐,视频,应用等文件自动分类显示,清晰明了

KINDLE电子书阅读器怎么下载电子书 电子书阅读器下载

KINDLE电子书阅读器怎么下载电子书——简介KINDLE电子书阅读器是亚马逊推出的全球流行的电子阅读器,因为跟亚马逊绑定,下载购买的书非常多,而且待机时间超长,不伤眼。现在大部份国内用户买到后都会对其进行刷下机,刷了机后就能支持电子

安卓手机PDF阅读器哪个好用?有哪些? pdf阅读器下载安卓版

安卓手机PDF阅读器哪个好用?有哪些?——简介本篇经验介绍一些自己常用的安卓手机PDF阅读器软件,有哪些PDF阅读器软件呢?哪个有比较好用呢?不排行,分列一些自己用过的,感觉还不错的阅读器。安卓手机PDF阅读器哪个好用?有哪些?——安卓手机PDF

小说阅读器哪个好 有什么小说阅读器

小说阅读器哪个好——简介根据个人经验,对各大手机看书软件进行对比,分析各自优缺点,希望能给广大书虫提供一些参考。小说阅读器哪个好——方法/步骤小说阅读器哪个好 1、多看阅读

声明:《mmap&&shm mmap文件阅读器》为网友萬物不相爭分享!如侵犯到您的合法权益请联系我们删除