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

进程间通信方式(二)

共享映射区
(一).通信方式:

进程间通信又称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,否则数据无法共享。两个或多个无血缘关系的进程只要通过共享一块映射区,就可以达到通信的目的了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值