mmap底层驱动实现(remap_pfn_range函数)

news2024/11/17 21:57:04

mmap底层驱动实现

myfb.c(申请了128K空间)
#include <linux/init.h>
#include <linux/tty.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/export.h>
#include <linux/mm_types.h>
#include <linux/mm.h>
#include <linux/slab.h>

#define 	BUFF_SIZE 		(32 * 4 * 1024)

static char *buff;
static int major;
static struct class * myfb_class;

static int myfb_mmap (struct file *fp, struct vm_area_struct *vm)
{
	int res;
	//表示该vma在虚拟地址空间中的偏移地址,单位是页(4K)
	//unsigned long offset = vm->vm_pgoff << PAGE_SHIFT;   
	
	//计算被映射的物理内存的物理页帧号(物理地址+偏移),以页为单位, virt_to_phys将虚拟地址转成物理地址
	//vm->pgoff表示的是用户空间映射时在VMA中的偏移(即mmap最后一个参数,单位是字节,但vm->pgoff自动转成页单位)
	unsigned long pfn_start = (virt_to_phys(buff) >> PAGE_SHIFT);   

	res = remap_pfn_range(vm, vm->vm_start, 
				pfn_start + vm->vm_pgoff,   //在物理页帧号上加上偏移
				vm->vm_end - vm->vm_start, 
				vm->vm_page_prot);
	if(res){
		printk("remap_pfn_range failed\n");
		return -1;
	}

	printk("[kernel] pfn_start = 0x%lx, vm->vm_pgoff = 0x%lx, \
		\n[kernel] vm->vm_start = 0x%lx, vm->vm_end = 0x%lx, vir_ker_start = 0x%lx\n",  \
		pfn_start, vm->vm_pgoff, vm->vm_start, vm->vm_end, (unsigned long)buff);
	return 0;
}

static struct file_operations myfb_fops = {
	.owner = THIS_MODULE,
	.mmap  = myfb_mmap,
};

static int myfb_init(void)
{
	buff = kzalloc(BUFF_SIZE, GFP_KERNEL);
	if (!buff){
		printk("kzalloc failed!\n");
		return -ENOMEM;
	}
	printk("kzalloc success!\n");

	major = register_chrdev(0, "myfb", &myfb_fops);
	myfb_class = class_create(THIS_MODULE, "myfb_class");
	device_create(myfb_class, NULL, MKDEV(major, 0), NULL, "myfb");
	
	return 0;
}

static void myfb_exit(void)
{
	device_destroy(myfb_class, MKDEV(major, 0));
	class_destroy(myfb_class);
	unregister_chrdev(major, "myfb");
	
	kfree(buff);
}


module_init(myfb_init);
module_exit(myfb_exit);
MODULE_LICENSE("GPL");



mmap_read.c
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>

#define   PAGE_SIZE    (4*1024)
#define   BUFF_SIZE    (1 * PAGE_SIZE)
#define   OFFSET       (2 * PAGE_SIZE)

char *p;
int fd;

void ctrlc(int signum)
{
	munmap(p, BUFF_SIZE);
	close(fd);
}

int main(void)
{
	signal(SIGINT, ctrlc);
	fd = open("/dev/myfb", O_RDWR);

       	p = (char *)mmap(NULL, BUFF_SIZE, PROT_READ | PROT_WRITE , MAP_SHARED, fd, OFFSET);

	if(p){
		printf("mmap addr = 0x%x\n", p);
		printf("data = %s\n", p);
	}else{
		printf("mmap failed\n");
	}

	while(1){
		sleep(1);
	}

	return 0;
}
mmap_write.c
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>

#define   PAGE_SIZE    (4*1024)
#define   BUFF_SIZE    (1 * PAGE_SIZE)
#define   OFFSET       (0 * PAGE_SIZE)

char *p;
int fd;

void ctrlc(int signum)
{
	munmap(p, BUFF_SIZE);
	close(fd);
}

int main(void)
{
	signal(SIGINT, ctrlc);

	fd = open("/dev/myfb", O_RDWR);

       	p = (char *)mmap(NULL, BUFF_SIZE, PROT_READ | PROT_WRITE , MAP_SHARED, fd, OFFSET);

	if(p){
		printf("mmap addr = 0x%x\n", p);
		memcpy(p, "hello world", 20);
	}else{
		printf("mmap failed\n");
	}

	while(1){
		sleep(1);
	}
	return 0;
}

Makefile

KERNEL_DIR = /home/me/Kernel_Uboot/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek

all:
	make -C $(KERNEL_DIR) M=`pwd` modules
	$(CROSS_COMPILE)gcc mmap_read.c -o mmap_read
	$(CROSS_COMPILE)gcc mmap_write.c -o mmap_write

clean:
	make -C $(KERNEL_DIR) M=`pwd` modules clean

obj-m += myfb.o

当读和写的进程内存映射地址的偏移都为0时,读进程能把写进程写入的数据读出
在这里插入图片描述
当写进程内存映射地址偏移为0,读进程内存映射地址**偏移为2(单位页)**时,读进程读出数据为空
在这里插入图片描述

PS:注意到读写进程的pfn_start相同,这个值是映射的物理内存地址,vm->vm_pgoff 是偏移(单位页,一页=4K(4096))

但是两个进程映射的虚拟地址结果不一定相同,虚拟地址是进程自己独有的,这点很容易理解。


1. 查看虚拟内存分布

查看读写进程的pid
在这里插入图片描述
写进程的虚拟内存分布
在这里插入图片描述

读进程的虚拟内存分布
在这里插入图片描述

1.1 分析虚拟内存映射部分

以读进程为例,/dev/myfb所在行即是内存映射的部分

76ffa000: vm->vm_start 的值

76ffb000: vm->vm_end 的值

rw-s: 表示的是 vm->vm_flags,"rw"表示可读可写,"s"表示 share共享,"p"表示 private 私有

00000000: 表示偏移量,即 vm->vm_pgoff(单位页,此处的偏移量单位是字节,需要做一下换算)

00:06 : 表示主次设备号

2564: 表示 inode 值

/dev/myfb: 表示设备节点名

1.2 关于偏移量

将读进程中mmap函数最后一个参数改为2*4096(2页)后,进程的虚拟内存映射部分的地址分布如下。

可以看到偏移量为 00002000,即 2*4096字节 = 2页 = 8K

偏移量指的是mmap最后一个参数、同样也是vm->vm_pgoff(单位页),指的是映射时在文件的物理内存上的偏移,只映射了文件的部分内容,单位是页4K
在这里插入图片描述


2. remap_pfn_range函数
2.1 remap_pfn_range函数原型
/**
 * remap_pfn_range - remap kernel memory to userspace
 * @vma: user vma to map to
 * @addr: target user address to start at
 * @pfn: physical address of kernel memory
 * @size: size of map area
 * @prot: page protection flags for this mapping
 *
 *  Note: this is only safe if the mm semaphore is held when called.
 */
int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
		    unsigned long pfn, unsigned long size, pgprot_t prot)
参数含义
vm虚拟内存区域描述符,用于表示映射的虚拟内存区域
addr映射的虚拟内存区域的首地址
pfn物理页帧号
size映射区域的大小
prot物理页面的操作属性,例如读/写/执行权限
2.2 remap_pfn_range函数使用

这里主要搞懂 myfb.c 驱动代码中的 remap_pfn_range 中的如下代码

unsigned long pfn_start = (virt_to_phys(buff) >> PAGE_SHIFT);   

res = remap_pfn_range(vm, vm->vm_start, 
                      pfn_start + vm->vm_pgoff,   //在物理页帧号上加上偏移
                      vm->vm_end - vm->vm_start, 
                      vm->vm_page_prot);
  • vm: 调用mmap时内核自动生成的VMA(虚拟内存描述符)
  • vm->vm_start: 该VMA的起始地址
  • vm->_end: 该VMA的结束地址
  • virt_to_phys: 将虚拟地址转成物理地址
  • PAGE_SHIFT: 宏,值为12,1<<PAGE_SHIFT表示4096,即4K,一页的大小
  • vm->vm_pgoff: 指的是映射时在文件的物理内存上的偏移,只映射了文件的部分内容,单位是页(4K)
  • vm->vm_page_prot: 该虚拟内存的访问权限,由mmap的参数决定,例如可读可写,共享等

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1065391.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mybatis 使用参数时$与#的区别

之前我们介绍了mybatis中参数的使用&#xff0c;本篇我们在此基础上介绍Mybatis中使用参数时$与#的区别。 如果您对mybatis中参数的使用不太了解&#xff0c;建议您先进行了解后再阅读本篇&#xff0c;可以参考&#xff1a; Mybatis参数(parameterType)https://blog.csdn.net…

知识图谱和大语言模型的共存之道

源自&#xff1a;开放知识图谱 “人工智能技术与咨询” 发布 导 读 01 知识图谱和大语言模型的历史 图1 图2 图3 图4 图5 02 知识图谱和大语言模型作为知识库的优缺点 图6 图7 表1 表2 图8 图9 03 知识图谱和大语言模型双知识平台融合 图10 图11 04 总结与展望 声明:公众号转…

C# OpenCvSharp Yolov8 Pose 姿态识别

效果 项目 代码 using OpenCvSharp; using OpenCvSharp.Dnn; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms;namespace OpenC…

中国企业400电话在线申请办理

在当今竞争激烈的商业环境中&#xff0c;企业需要寻求各种方式来提升客户服务和市场竞争力。而拥有一个专属的400电话号码&#xff0c;不仅可以为企业带来更多的商机&#xff0c;还能提升企业形象和客户满意度。本文将介绍如何在线申请办理中国企业400电话&#xff0c;并提供一…

京东数据接口|电商运营中数据分析的重要性

在电商运营中&#xff0c;数据分析是非常重要的一环&#xff0c;它可以帮助电商企业更好地了解市场、了解消费者、了解产品、了解销售渠道等各种信息&#xff0c;从而制定更为科学有效的运营策略&#xff0c;提高销售效益。 数据方面用户可以直接选择使用数据接口来获取&#…

面试高频手撕算法 - 背包问题1

目录 1. 前言 2. 什么是 01 背包&#xff0c; 什么是完全背包 3. 01 背包 3.1 【模板】01背包 3.2 分割等和子集 3.3 分割等和子集 3.4 最后一块石头的重量 1. 前言 为什么要专门去搞一下这个背包问题呢 ? 因为作者已经在两场面试中吃了这个亏, 尤其是在面深信服的测开岗…

信创办公–基于WPS的EXCEL最佳实践系列 (条件格式)

信创办公–基于WPS的EXCEL最佳实践系列 &#xff08;设置条件格式&#xff09; 目录 应用背景操作步骤1、选用条件格式1.1 筛选出迟到次数超过3次的数据1.2 筛选出早退次数位于前三的数据1.3 个人加班时长在总体中所占的在的位置 2、删除条件格式2.1 清除规则2.2 管理规则 应用…

钡铼BL124PN:简单快速转换Profinet到Ethernet/IP

钡铼技术BL124PN是一款高性能的Profinet转Ethernet/IP网关设备。该网关专为工业自动化领域设计&#xff0c;用于实现不同协议之间的互连和通信。BL124PN采用可靠稳定的硬件和先进的通信技术&#xff0c;具有以下主要特点&#xff1a; 协议转换能力&#xff1a;BL124PN能够将Pr…

WIN10 查看端口占用情况

输入命令&#xff0c;其中 5082 为需要查看的端口 C:\Users\chenjian>netstat -ano|findstr "5082"TCP 0.0.0.0:5082 0.0.0.0:0 LISTENING 21708可以看到 5082 这个端口被 “21708”这个进程占用了。 输入命令查看进程的信息 C…

ST2110基础介绍(初识)

前言 随着超高清视频产业迅速发展&#xff0c;4K/8K超高清信号对带宽提出更高的要求&#xff0c;传统的基于SDI (数字串行接口)采集制作、调度分发的方式已经不能满足技术更新的需求。行业内的共识是采用基于ICT(网络和通信技术)技术的IP化架构&#xff0c;一方面解决高带宽信…

当长假来临,如何定向应用AI?科技力量变革您的假日生活!

“今夜月明人尽望&#xff0c;不知秋思落谁家。”中秋国庆的双节组合&#xff0c;让万千中国家庭迎来了难得的团圆欢庆时刻。长达八天的假期已经开启&#xff0c;现在的你是不是已经背上行囊&#xff0c;浪迹远方了呢&#xff1f; &#xff08;金秋时分&#xff0c;假日光景&am…

Java基于SpringBoot的社区医院管理服务

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W,Csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 1、效果演示2、 前言介绍3. 技术栈4系统设计4.1数据库设计4.2系统整体设计4.2.1 系统设计思想4.2.…

网络安全工程师日常工作有哪些?初学者怎么适应

在往期的很多文章当中介绍过如何成为一名合格的网络安全工程师所需技能以及学习方法&#xff0c;今天给大家更新网络安全工程师的主要日常工作&#xff0c;相信刚学习的小伙伴一定很好奇&#xff0c;话不多说&#xff0c;往下看吧。 网络安全工程师分为哪些方向 网络安全工程…

SwiftUIArkUI-曲线动画Path和路径动画motionPath

OpenHarmony Path ArkUI 高性能 motionPath 动效 三次贝塞尔曲线 曲线动画 SwiftUI SwiftUI通过Path可以绘制路径动画&#xff0c;通过addCurve可用绘制三次贝塞尔曲线。 ArkUI是鸿蒙的核心UI布局框架&#xff0c;使用motionPath绘制路径动画&#xff0c;通过绘制路径可以自定…

开箱即用版本 满分室间质评之GATK Somatic SNV+Indel+CNV+SV

最近准备为sliverworkspace 图形化生信平台开发报告设计器&#xff0c;需要一个较为复杂的pipeline作为测试数据&#xff0c;就想起来把之前的 满分室间质评之GATK Somatic SNVIndelCNVSV&#xff08;下&#xff09;性能优化翻出来用一下。跑了一遍发现还是各种问题&#xff0c…

想用iPhone录视频同时拍照片?这篇教你!附送10个苹果手机技巧

如果你在使用苹果手机录制视频时还想要拍摄静态照片&#xff0c;打开录制功能后&#xff0c;可以点击苹果手机屏幕右侧的白色圈圈&#xff0c;这时候&#xff0c;静态的照片就会自动保存到相册啦&#xff01; 哈哈&#xff0c;如果你想要反过来操作——用苹果手机拍照时顺便录…

对程序员来说,技术能力和业务逻辑哪个更重要?

一、前言 大家好&#xff0c;我是苍何。话说&#xff0c;小明和小华都是程序员&#xff0c;小明今年刚毕业在一家小金融公司实习&#xff0c;小华是工作了 8 年的 Java 开发&#xff0c;他们两最近都面临同样的问题「技术能力和业务逻辑哪个更重要&#xff1f;」&#xff0c;于…

【数据结构】手撕归并排序(含非递归)

目录 一&#xff0c;归并排序&#xff08;递归&#xff09; 1&#xff0c;基本思想 2&#xff0c;思路实现 二&#xff0c;归并排序&#xff08;非递归&#xff09; 1&#xff0c;思路实现 2&#xff0c;归并排序的特性总结&#xff1a; 一&#xff0c;归并排序&#xff0…

七、Thymeleaf对象的访问

7.1、实体对象属性的访问 使用变量表达式访问对象属性时&#xff0c;可以使用"对象.属性名"的语法。注意此处的属性名是对象属性getter方法的名称。 示例 在项目包下添加domain包&#xff0c;在包中创建“User”类&#xff0c;添加userId和userName属性 import jav…

极验文字点选验证

测试网址&#xff1a; 验证码验证形式展示-滑动图片验证-点选验证-极验交互安全极验GEETEST提供丰富多样的行为验证形式来应对网络攻击&#xff0c;如滑动拼图验证&#xff0c;智能无感验证&#xff0c;文字/图标/语序点选验证和空间推理验证等……这些验证形式在PC端和移动端…