mmap 用mmap实现cp命令


mmap 可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对
应的内存地址,对文件的读写可以直接用指针来做而不需要read/write 函数。

#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off);
int munmap(void *addr, size_t len);

参数:addr 一般为NULL,内核自动分配
   len 映射区大小
   prot 创建映射区的权限
           flag
                   MAP_SHARED 修改会更新到物理层上,即修改内容,物理磁盘也会跟着被修改
                   MAP_PRIVATE 不会修改到物理层
           filedes  文件描述符等
           off  偏移量的起始位置,以页为单位

    

如果addr 参数为NULL,内核会自己在进程地址空间中选择合适的地址建立映
射。如果addr 不是NULL,则给内核一个提示,应该从什么地址开始映射,内
核会选择addr 之上的某个合适的地址开始映射。建立映射后,真正的映射首地
址通过返回值可以得到。len 参数是需要映射的那一部分文件的长度。off 参数
是从文件的什么位置开始映射,必须是页大小的整数倍(在32 位体系统结构上
通常是4K)。filedes 是代表该文件的描述符。
prot 参数有四种取值:
• PROT_EXEC 表示映射的这一段可执行,例如映射共享库
• PROT_READ 表示映射的这一段可读
• PROT_WRITE 表示映射的这一段可写
• PROT_NONE 表示映射的这一段不可访问

flag 参数有很多种取值,这里只讲两种,其它取值可查看mmap(2)
• MAP_SHARED 多个进程对同一个文件的映射是共享的,一个进程对映射的
内存做了修改,另一个进程也会看到这种变化。
• MAP_PRIVATE 多个进程对同一个文件的映射不是共享的,一个进程对映
射的内存做了修改,另一个进程并不会看到这种变化,也不会真的写到
文件中去。
如果mmap 成功则返回映射首地址,如果出错则返回常数MAP_FAILED。当进程
终止时,该进程的映射内存会自动解除,也可以调用munmap 解除映射。munmap

成功返回0,出错返回-1。



/*
 *使用mmap,将一个内容为“hello”的文件内容,改为“abcdo”,不使用read/write做
 */


//创建一个文件,内容为hello
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

int main(void)
{
	int *p;
	int fd;
	fd = open("hello.txt", O_RDWR);
	if(fd < 0){
		perror("open hello.txt");
		exit(1);
	}

	int len = lseek(fd, 0, SEEK_END);
	printf("lseek:%d\n", len);
	/*
	 *NULL		:让内核自动制定映射区首地址
	 *len		:制定映射区的大小
	 *PROT_WRITE:表示映射的这一段可写
	 *MAP_SHARED:多进程共享,修改会更新到物理设备上
	 *fd		:要映射的文件的描述符
	 *0			:从文件的0号偏移位置开始映射
	 */
	p = mmap(NULL, len, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);
	if(p == MAP_FAILED){	//映射出错
		perror("mmap");
		exit(1);
	}
	close(fd);				//只要映射成功文件即可关闭	

//	p[0] = 0x64636261;
	strcpy(p, "abdco");
	munmap(p, len);			//解除映射

	return 0;
}


/*
akaedu@akaedu-G41MT-D3:~/T74_system/0819_chp1_lseek_ctl_mmap_dup2$ ./mmap
lseek:6
*/
//打开文件,内容已修改为abcdo












/*
 *./mmap_cp src dst 使用mmap实现的命令行参数cp命令
 *mmap的好处,把文件当成数组来看待,简化程序逻辑,提高程序速度
 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>

int main(int argc, char *argv[])
{
	int in, out;		//在两个映射区之间进行复制
	char *s, *d;

	in = open(argv[1], O_RDONLY);
	if(in == -1){		//检查返回值
		perror("open src error");
		exit(1);
	}
	/*
	 *映射区权限必须小于等于文件的权限,建立映射区的过程隐含着一次文件的
	 *读操作,所以这里应该是“读写”权限,而不是“只写”权限
	 *使用O_WRONLY建立映射区会报错
	 */
	out = open(argv[2], O_RDWR|O_TRUNC|O_CREAT, 0644); 
	if(out == -1){
		perror("open dst error");
		exit(1);
	}

	int len = lseek(in, 0, SEEK_END);		//获取文件长度
	/*
	 *NULL		:由内核指定映射区首地址
	 *len		:映射区的大小
	 *PROT_READ	:读写权限为只读
	 *MAP_PRIVATE:进程间不共享映射区,映射区被关闭后,
	 *			  修改消失,不会更新到物理设备上
	 *in		:文件描述符
	 *0			:映射文件的偏移位置
	 */
	s = (char *)mmap(NULL, len, PROT_READ, MAP_PRIVATE, in, 0);
	if(s == MAP_FAILED){
		perror("mmap src error");
		exit(1);
	}
	/*
	 *由于映射区的大小不允许超过原始文件的大小
	 *而目的文件是使用O_CREAT|O_TRUNC创建的,所以建立映射区的时候
	 *目的文件的大小是0个字节,所以应该先拓展目的文件的大小,
	 *然后在创建跟目的文件对应的映射区, 如不拓展,将来在对映射区进行
	 *写操作的时候就会出现“总线错误”
	 *
	 *所以:建立目的映射区之前,应该先拓展目的文件
	 */
	lseek(out, len-1, SEEK_SET);//不拓展,会出"总线错误"

	//lseek只是修改记录,并不真正拓展文件,真正拓展是写(IO)操作做的。
	write(out, "a", 1);				//len-1 +'a'= len
	
	//目的映射区权限只写,映射区选项是MAP_SHARED,才能更新到磁盘上
	d = (char *)mmap(NULL, len, PROT_WRITE, MAP_SHARED, out, 0);
	if(d == MAP_FAILED){
		perror("mmap dst error");
		exit(1);
	}
	close(in);close(out);  //映射区建立完毕,文件即可关闭了。

	memcpy(d, s, len);
#if 0
	for(i = 0; i < len; i++)
		*d++ = *s++;
#endif

	return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值