Linux操作系统提供的IPC方法有很多,如文件,管道,信号,共享内存,消息队列,套接字,命名管道等,常用的进程间通信方法有:

  • 管道 (使用最简单)
  • 信号 (开销最小)
  • 共享映射区 (无血缘关系)
  • 本地套接字 (最稳定)

mmap函数基本用法

上述IPC方法中的共享映射区就是使用mmap函数实现的,该函数的功能是将磁盘文件的内容映射到内存中,在不适用read和write函数的情况下,使用地址(指针)完成I/O操作

void *mmap(void *adrr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length); /* 删除共享映射区 */

参数解析:

  • addr: 建立映射区的首地址,由Linux内核指定。使用时,直接传递NULL
  • length: 欲创建映射区的大小
  • prot: 映射区权限PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
  • flags: 标志位参数(常用于设定更新物理区域、设置共享、创建匿名映射区)
MAP_SHARED:  会将映射区所做的操作反映到物理设备(磁盘)上
MAP_PRIVATE: 映射区所做的修改不会反映到物理设备
  • fd: 用来建立映射区的文件描述符
  • offset: 映射文件的偏移(4k的整数倍)

返回值:

  • 成功返回共享内存的首地址
  • 失败返回 MAP_FAILED((void *) -1)

mmap函数注意事项

先看一个示例程序:

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

#include <sys/stat.h>

int main()
{
	int fd_test;
	unsigned char *buf = NULL;
	int ret;
	
	fd_test = open("temp", O_RDWR | O_CREAT, 0644);
	if (fd_test == -1)
	{
		perror("open err");
		exit(0);
	}
	/*
	 * 删除文件的目录项,使之具备被删除的条件,没有真正被删除
	 * 在所有占有该文件描述符的进程结束后该文件被删除
	 */
	//unlink("temp");	/* 程序结束后删除temp */		
	
	int n;
	printf("请输入文件的大小\n");
	scanf("%d",&n);
	ret = ftruncate(fd_test, n); 	/* 设置文件的大小 */
	if (ret == -1)
	{
		perror("ftruncate err:");
		exit(0);
	}
	
	struct stat fd_stat;
	ret = fstat(fd_test, &fd_stat);	/* 获取文件的大小 */
	if (ret == -1)
	{
		perror("fstat err:");
		exit(0);
	}
	int fd_len = fd_stat.st_size;
	printf("fd_len = %d \n",fd_len);
	
	/* 映射一个与文件大小相同的共享内存区域 */
	buf = (unsigned char *)mmap(NULL, fd_len, PROT_READ | PROT_WRITE, MAP_SHARED, 
	fd_test, 0);
	if (buf == MAP_FAILED)
	{
		perror("mmap failed");
		exit(0);
	}
	
	int buf_len = strlen((char *)buf);
	printf("buf len = %d \n", buf_len);
	printf("修改前文件的内容:\n%s\n", buf);
	
	char rcv_buf[1024];
	printf("输入写入文件的数据:\n");

	while ( fgets(rcv_buf, sizeof(rcv_buf), stdin) != NULL )
	{
		if (strlen(rcv_buf) <= 1)
			continue;
		break;
	}
	
	memcpy(buf, rcv_buf, strlen(rcv_buf));
	printf("修改后文件的内容:\n%s\n", buf);
	
	close(fd_test);
	munmap(buf, 10);
	return 0;
}

代码中使用了几个系统调用,

  • unlink,删除文件的目录项,使之具备被删除的条件,没有真正被删除,在所有占有该文件描述符的进程结束后该文件被删除,如果在程序结束后就无需使用这个文件,则调用它
  • ftruncate,设置文件的大小
  • fstat, 获取文件的大小

mmap函数的核心在注意事项,主要有以下几个

  • 新创建的文件无法mmap,必须要有实际的大小,可以使用ftruncate函数设置文件实际大小
  • 对映射区的权限要小于等于打开文件的权限,映射的过程中隐藏了一次读操作,因此文件权限至少是可读
  • 最后一个参数offset必须是4K的整数倍
  • 映射的内存超出了文件的大小,不报错,但无法写入超出的部分
  • 一定要检查返回值

运行结果:

可以看到,设置文件的大小为10,但写入的数据超过10,虽然写入了共享内存区域,但是超出文件大小的部分并没有写入文件中

mmap函数用于进程间通信

实质上mmap是内核借助文件帮我们创建了一个映射区,多个进程之间利用该映射区完成数据传递。mmap可以用于父子间通信,也可以用于无血缘关系进程间通信,前提是必须要使用MAP_SHARED选项

匿名映射

使用映射区必须要使用一个文件来创建共享内存,比较麻烦,也可以使用匿名映射来创建共享映射区,方法是使用MAP_ANONYMOUS (或MAP_ANON)

int *p = mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);

该方法只可能在Linux操作系统使用,无法在类Unix操作系统使用。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

守护进程 上一篇
makefile基础 下一篇