您的位置:首页 > 其它

进程间通信的方式(二):共享映射区

2017-01-18 00:28 218 查看

进程间通信方式(二)

共享映射区

(一).通信方式:

进程间通信又称IPC(Inter Process Communication),它可以通过文件 管道 有名管道 共享内存 消息队列 信号量 套接字这几个方式进行通信,但是文件这种以及消息队列基本已被淘汰。

所以常用的通信方式有:

1.管道

2.信号

3.共享映射区

4.本地套接字

(二).共享映射区基本概念:

通过mmap系统调用,把普通文件映射到存储空间的一个缓冲区中,使得两个或多个进程之间可以共享这一块区域,就达到了进程间通信的目的。这种通信方式比通过文件进行通信更快,因为是直接操作缓冲区,而不是通过read以及write等函数进行操作。

这种方式可以用于父子间进程通信,也可以用于无血缘关系的进程之间通信。

当我们使用mmap建立了共享映射区之后,进行的操作就好比我们操作字符数组一样。

(三).使用共享映射区进行通信:

函数原型:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);


所需头文件:
#include <sys/mman.h>


参数含义:

1.第一个参数代表建立共享映射区在内存中的起始地址,由系统自动分配即可,所以传入NULL就行了;

2.第二个参数代表创建的映射区的大小;

3.第三个参数代表映射区的访问权限,由几个宏确定,常用的有:PROT_READ(可读),PROT_WRITE(可写),PROT_READ | PROT_WRITE(可读可写);

4.第四个参数代表标志位,常用的有:MAP_SHARED、MAP_PRIVATE、MAP_ANON,MAP_SHARED的意思是对这块映射区内容的修改会影响到被映射的文件 或者 多个进程间独享该映射区,MAP_PRIVATE的意思是对这块映射区内容的修改不会影响到被映射的文件 或者 多个进程间共享该映射区,MAP_ANON用于匿名映射;

5.第五个参数代表要映射的文件的文件描述符;

6.最后一个参数代表操作这块映射区的起始偏移量,和对数组的操作类似,不过必须是4k的整数倍。

返回值:

该函数成功调用会返回创建映射区的首地址,如果失败会返回一个宏:MAP_FAILED

由于mmap函数其实涉及到内存的分配,有了分配,自然要有释放。

释放的函数原型:
int munmap(void *addr, size_t length)


头文件:
#include <sys/mman.h>


参数含义:

1.第一个参数代表共享映射区的起始地址

2.第二个参数代表分配的大小

返回值:

成功返回0,失败返回-1

例子1:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>

int main()
{
int len, ret;
char *p = NULL;
int fd = open("mytest.txt", O_CREAT | O_RDWR, 0666);  //以读写的方式打开文件,不存在则创建
if(fd < 0)
{
perror("open error:");
exit(1);
}
len = ftruncate(fd, 4);  //扩展文件大小
if(len == -1)
{
perror("ftruncate error:");
exit(1);
}
p = mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //以读写的方式建立映射区
if(p == MAP_FAILED)
{
perror("mmap error");
exit(1);
}
strcpy(p, "abc");  //将字符串abc赋给这块映射区

ret = munmap(p, 4);  //释放
if(ret == -1)
{
perror("munmap error:");
exit(1);
}
close(fd);

return 0;
}


这个例子虽然简单,但是其实有几个需要注意的地方。

1.当我们打开一个文件的时候,操作该文件的权限是不能小于通过该文件建立映射区的权限的,即如果文件是只读的,那么映射区这边也不能进行写操作。

2.创建映射区时,包含着一次对映射文件的读操作,因为要把内容都映射到缓冲区中。所以文件的读权限应该要有才行。

3.偏移量必须是4k的整数倍。

4.munmap的第一个参数必须是映射区的首地址。

例子2:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/wait.h>

int main()
{
int *p;
pid_t pid;

//这次我们不打开文件,而是进行匿名映射。因为打开文件其实主要是起一个映射的作用,略显鸡肋,如果直接有这一块区域进行通信,那不更好
p = (int *)mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); //原本的文件描述符参数应该填-1

if(p == MAP_FAILED)
{
perror("mmap error");
exit(1);
}

pid = fork();
if(pid == 0)
{
*p = 2;
printf("child, *p = %d\n", *p);
}
else
{
sleep(1);
printf("parent, *p = %d\n", *p);
wait(NULL);

int ret = munmap(p, 4);
if(ret == -1)
{
perror("mumap error");
exit(1);
}
}
return 0;

}


注意事项:

MAP_ANON参数只适合在linux系统中使用。

例子3:

/****************读数据******************/
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>

struct STU
{
int id;
char name[20];
char sex;
};

int main(int argc, char *argv[])  //argv[1]用于传递要映射的文件名
{
int fd;
struct STU student;
struct STU *mm;

fd = open(argv[1], O_RDONLY);

mm = mmap(NULL, sizeof(student), PROT_READ, MAP_SHARED, fd, 0);  //注意这里必须是MAP_SHARED,不然数据无法共享
if(mm == MAP_FAILED)
{
perror("mmap error:");
exit(1);
}
close(fd);

while(1)  //读出映射区的数据
{
printf("id = %d\tname = %s\t %c\n", mm->id, mm->name, mm->sex);
sleep(2);
}
munmap(mm, sizeof(student));
return 0;
}

/***************写端***************/
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>

struct STU
{
int id;
char name[20];
char sex;
};

int main(int argc, char *argv[])
{
int fd;
struct STU student = {10, "xiaoming", 'm'};
char *mm;

fd = open(argv[1], O_RDWR | O_CREAT, 0664);
ftruncate(fd, sizeof(student));

mm = mmap(NULL, sizeof(student), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(mm == MAP_FAILED)
{
perror("mmap error");
exit(1);
}
close(fd);

while(1)  //写数据
{
memcpy(mm, &student, sizeof(student));
student.id++;
sleep(1);
}
munmap(mm, sizeof(student));
return 0;
}


注意两个进程之间打开映射区的标志位必须是MAP_SHARED,而不是MAP_PRIVATE,否则数据无法共享。两个或多个无血缘关系的进程只要通过共享一块映射区,就可以达到通信的目的了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: