Linux的内存I/O映射

版权声明:转载请声明 https://blog.csdn.net/qq_40732350/article/details/82828740

内存的I/O映射是将文件某区域的内容映射到进程的虚拟空间的技术

通过对文件的内存IO映射,可使用用户对文件的操作转换为对内存的操作,这样不仅使用方便而且提高了存储速度。

1.定义存储空间

void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);

addr:参数用于指定映射存储区的起始地址。通常将其设置为0,这表示由系统选择该映射区的起始地址。此函数的返回值是该映射区的起始地址。

fd:参数是指定要被映射文件的描述符。在文件映射到地址空间之前,必须先打开该文件。

length:参,数是映射的字节数,

offset:是要映射字节在文件中的起始偏移量(有关off值的一些限制将在后面说明),

prot:参数指定了映射存储区的保护要求,如图14-25所示。可设置为下面的任意组合

注意:prot的设置不能超过open调用的权限,如open打开为只读,但在这用了可写,这是不允许的

flags:会影响映射区域的各种特性

  • MAP_FIXED:如果参数 start 所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此标志。
  • MAP_SHARED:对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。存储操作立刻修改映射文件内容
  • MAP_PRIVATE:对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。存储操作导致创建映射文件的副本,并对副本读写
  • MAP_ANONYMOUS:建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。
  • MAP_DENYWRITE:只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。
  • MAP_LOCKED:将映射区域锁定住,这表示该区域不会被置换(swap)。
  • 在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。

off的值和addr的值(如果指定了MAP_FIXED)通常被要求是系统虚拟存储页长度的倍数。

虚拟存储页长可用带参数_SC_PAGESIZE或_SC_PAGE_SIZE的sysconf函数(见2.5.4节)得到。因为off和addr常常指定为0,所以这种要求一般并不重要。

与映射区相关的信号有SIGSEGV和SIGBUS

信号SIGSEGV通常用于指示进程试图访问对它不可用的存储区。如果映射存储区被mmap指定成了只读的,那么进程试图将数据存入这个映射存储区的时候,也会产生此信号。

如果映射区的某个部分在访问时已不存在,则产生SIGBUS信号。例如,假设用文件长度映射了一个文件,但在引用该映射区之前,另一个进程已将该文件截断。此时,如果进程试图访问对应于该文件已截去部分的映射区,将会接收到SIGBUS信号。

注意:

子进程能通过fork继承存储映射区(因为子进程复制父进程地址空间,而存储映射区是该地址空间中的一部分),

但是由于同样的原因,新程序则不能通过exec继承存储映射区。

 

2.更改现有映射的权限

int mprotect(void *addr, size_t len, int prot);  

prot的合法值与mmap中prot参数的一样(见图14-25),请注意,地址参数addr的值必须是,系统页长的整数倍。

如果修改的页是通过MAP SHARED标志映射到地址空间的,那么修改并不会立即写回到文件中。相反,何时写回脏页由内核的守护进程决定,决定的依据是系统负载和用来限制在系统失败事件中的数据损失的配置参数。因此,如果只修改了一页中的一个字节,当修改被写回到文件中,时,整个页都会被写回。

3.冲洗存储映射区

如果共享映射中的页已修改,那么可以调用msync将该页冲洗到被映射的文件中. msync函数类似于fsync (见3.13节),但作用于存储映射区。

int msync(void *addr, size_t length, int flags);

4.解除内存映射

int munmap(void *addr, size_t length);

实例代码:

功能:实现类似命令cp的功能

#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <string.h>
#include <sys/mman.h>


#define COPYINCR (1024*1024*1024) /* 1 GB */
int
main(int argc, char *argv[])
{
	int fdin, fdout;
	void *src, *dst;
	size_t copysz;
	struct stat sbuf;
	off_t fsz = 0;
	if (argc != 3)
		printf("usage: %s <fromfile> <tofile>", argv[0]);
	if ((fdin = open(argv[1], O_RDONLY)) < 0)
		printf("can’t open %s for reading", argv[1]);
	if ((fdout = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0666)) < 0)
		printf("can’t creat %s for writing", argv[2]);
	if (fstat(fdin, &sbuf) < 0) /* need size of input file */
		printf("fstat error");
	if (ftruncate(fdout, sbuf.st_size) < 0) /* set output file size */
		printf("ftruncate error");
	while (fsz < sbuf.st_size) 
	{
		if ((sbuf.st_size - fsz) > COPYINCR)
			copysz = COPYINCR;
		else
			copysz = sbuf.st_size - fsz;
		if ((src = mmap(0, copysz, PROT_READ, MAP_SHARED, fdin, fsz)) == MAP_FAILED)
			printf("mmap error for input");
		if ((dst = mmap(0, copysz, PROT_READ | PROT_WRITE,MAP_SHARED, fdout, fsz)) == MAP_FAILED)
			printf("mmap error for output");
		memcpy(dst, src, copysz); /* does the file copy */
		munmap(src, copysz);
		munmap(dst, copysz);
		fsz += copysz;
	}
	exit(0);
}

猜你喜欢

转载自blog.csdn.net/qq_40732350/article/details/82828740