内存管理,goto的使用,内存的申请和释放,mmap,ioremap
1、内存管理 (将物理内存映射到内核空间(3G~4G)并使用)
深入内核: 伙伴系统
1.1基本概念
1)linux内核管理内存是以物理内存页为单位
一个物理内存页通常为4KB
内核会为每个物理内存页创建如下结构变量
struct page {
//记录该物理内存页被引用的次数 为0 代表空闲页
atomic_t _count
...
}
2) 内核管理内存时对所有的内存并不是一视同仁
低端内存: 介于0~896M(可调)的内存称为低端内存
采用静态映射方式
该段内存的虚拟地址
虚拟地址=0xc0000000 + 物理偏移
高端内存:>896M(可调)的内存称为高端内存
采用动态映射方式
使用该段物理内存时
动态建立和虚拟地址的映射关系
使用完毕后立即解除该映射关系
1.2 内核中动态申请内存的方式
1.2.1 按页分配内存
方式一:
//连续申请2^order个物理内存页
struct page *alloc_pages(gfp_t gfp_mask, unsigned int order)
//完成申请到的物理内存页的映射
//返回起始虚拟地址
void *page_address(const struct page *page)
方式二:
//连续申请2^order物理内存页 并映射
//返回对应的起始虚拟地址
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
void free_pages(unsigned long addr, unsigned int order)
addr, __get_free_pages的返回值
order,连续释放2^order个物理内存页
方式三:
//申请2^0个物理内存页 并返回映射后的起始虚拟地址
__get_free_page(gfp_mask)
1.2.2 按字节分配内存
void *kmalloc(size_t size, gfp_t flags)
size, 要连续申请的字节数
flags, 常用的取值
GFP_KERNEL:申请内存不成功时 阻塞等待 不能用于中断上下文
GFP_ATOMIC:申请不成功 立即返回错误信息
void kfree(const void *objp)
objp, kmalloc的返回值
void *vmalloc(unsigned long size)
size, 要连续申请的字节数
分配的空间位于高端内存
void vfree(const void *addr)
addr, vmalloc的返回值
kmalloc和vmalloc的区别:
kmalloc申请得到的物理内存一定是连续的
vmalloc申请得到的物理内存不一定连续
1.2.3建立映射后
可以使用以下函数完成物理地址和虚拟地址的转换
phys_addr_t virt_to_phys(const volatile void *x)
void *phys_to_virt(phys_addr_t x)
2、ioremap(将特殊功能寄存器地址映射到内核空间(3~4G))
2.1 基本概念
统一编址, 内存和外设使用同一套编号(0~4G)
ARM
加载存储指令:ldr / str
mov r1, #0x48000000
ldr r0, [r1]
str r0, [r1]
ldr r1, #0xc001c020
ldr r0, [r1]
str r0, [r1]
独立编址,内存一套编号,外设一套编号
X86
给出地址0x100
mov指令 地址0x100 指的是内存
in/out指令 操作外设
linux内核中将使用统一编址的外设称为I/O内存
将使用独立编址的外设称为I/O端口
2.2 如何编程操作特殊功能寄存器(外设)
1)申请I/O内存
request_mem_region(start,n,name)
start,要申请使用的I/O内存的起始物理地址
n, 要申请的连续字节数
name, 名称
2)映射I/O内存
void __iomem *ioremap(phys_addr_t start, unsigned long n)
start, 要映射的起始物理地址
n, 要映射的字节数
返回值,映射之后的起始虚拟地址
例如 start=0x48000000 n =0x100
返回值为0xc0008000
意味着
虚拟地址 物理地址
0xc0008000 0x48000000
0xc0008001 0x48000001
0xc0008002 0x48000002
。。。 。。。
0xc00080ff 0x480000ff
3)访问I/O内存
方式一:
*((volatile unsigned int *)addr)
方式二:
readl(addr)
writel(val, addr)
addr, 是虚拟地址
4)取消映射
void iounmap(void __iomem *addr)
addr,ioremap时的返回值
5)释放I/O内存
release_mem_region(start,n)
start, 要释放的起始物理地址
n, 连续释放的字节数
ioremap的意义:
对于GPIO管脚来说 控制方式有两种
1)gpio库函数
2)ioremap 之后直接操作特殊功能寄存器
如果操作uart控制器 i2c控制器
只能通过ioremap映射特殊功能寄存器 然后操作
3、mmap(将内存/特殊功能寄存器映射到用户空间(0~3G))
3.1 应用编程
fd = open("a.txt", ...)
addr = mmap(....,fd,size)
/*文件写入*/
addr[10] = 'c';
3.2 嵌入式环境
fd=("/dev/xxx", ...)
addr=mmap(..., fd, size)
------------------------------------
sys_mmap
xxx_mmap(struct file filp*, struct vm_area_struct *vma)
{
remap_pfn_range(vma, vma->vm_start,
要映射到用户空间物理地址>>12 (页号),
vma->vm_end - vma->vm_start, vma->vm_page_prot);
}
3.2.1通过mmap将LCD的显存映射到用户空间
3.2.2将camer的缓存映射到用户空间
总结:(非重点)
如果在用户空间需要对该设备执行mmap操作
1)设置驱动函数struct file_operations要实现mmap
2) xxx_mmap函数中要调用remap_pfn_range
3)remap_pfn_range的调用方式
remap_pfn_range(vma, vma->vm_start,
要映射起始物理地址>>12,
vma->vm_end-vma->vm_start,
vma->vm_page_prot);
fd = open("/dev/xxx", ....)
//addr中保存的虚拟地址对应的物理地址
//就是remap_pfn_range第三个参数
addr = mmap(..., fd,size);
通常在实际驱动编程过程不需要实现该函数
它违背了linux内核的初衷
实际开发过程只会把camer和lcd 显存(缓存)映射到用户空间
这类映射函数内核中已经实现完毕了
例如:lcd的映射函数是内核中的fb_mmap
#include "../../global.h" #include <linux/vmalloc.h>
#include <linux/slab.h> unsigned long pages_addr;
void *kmalloc_addr;
void *vmalloc_addr; int __init kernelspace_init(void)
{
int ret = ;
/*申请2^3个物理内存页 并映射*/
pages_addr = __get_free_pages(GFP_KERNEL, );
if(!pages_addr)
{
printk("<1>" "get pages failed!");
ret = -ENOMEM;
goto failure_pages;
}
printk("<1>" "get pages vir=%#x phys=%#x\n",
pages_addr, virt_to_phys(pages_addr)); /*申请200字节*/
kmalloc_addr = kmalloc(, GFP_KERNEL);
if(!kmalloc_addr)
{
ret = -ENOMEM;
goto failure_kmalloc;
}
printk("<1>" "kmalloc vir=%#x phys=%#x\n",
kmalloc_addr, virt_to_phys(kmalloc_addr));
vmalloc_addr = vmalloc(*);
if(!vmalloc_addr)
{
ret = -ENOMEM;
goto failure_vmalloc;
}
printk("<1>" "vmalloc vir=%#x phys=%#x\n",
vmalloc_addr, virt_to_phys(vmalloc_addr)); return ;
failure_vmalloc:
kfree(kmalloc_addr);
failure_kmalloc:
free_pages(pages_addr, );
failure_pages:
return ret;
}
void __exit kernelspace_exit(void)
{
vfree(vmalloc_addr);
kfree(kmalloc_addr);
free_pages(pages_addr, ); }
module_init(kernelspace_init);
module_exit(kernelspace_exit);
#include "../../global.h"
#include <linux/io.h>
#include <linux/ioport.h> #define GPIOC_START (0xc001c000)
#define GPIOC_SIZE (0x24)
static void __iomem *base = NULL; int __init led_drv_init(void)
{
unsigned int data = ;
/*1 申请I/O内存*/
request_mem_region(GPIOC_START,GPIOC_SIZE,"led1");
/*2 映射I/O内存*/
base = ioremap(GPIOC_START, GPIOC_SIZE);
/*3 访问I/O内存*/
data = readl(base+0x20);
data &= ~(<<);
data |= (<<);
writel(data, base+0x20); writel(readl(base+0x04)|(<<), base+0x04); writel(readl(base+0x00)&(~(<<)), base+0x00); return ;
}
void __exit led_drv_exit(void)
{
writel(readl(base+0x00)|(<<), base+0x00);
/*4 取消映射*/
iounmap(base);
/*5 释放I/O内存*/
release_mem_region(GPIOC_START, GPIOC_SIZE);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/mm.h> MODULE_LICENSE("GPL"); #define CMD_LED_ON 0x10001
#define CMD_LED_OFF 0x10002 struct class *cls = NULL;
/*1 定义struct cdev类型变量*/
struct cdev led_cdev;
dev_t dev = ;
int led_open(struct inode *inode,
struct file *filp)
{
return ;
}
int led_release(struct inode *inode,
struct file *filp)
{
return ;
}
int k_status = ; //灭灯
ssize_t led_write(struct file *filp,
const char __user *buf,
size_t len,
loff_t *offset)
{ return len;
}
ssize_t led_read(struct file *filp,
char __user *buf,
size_t len,
loff_t *offset)
{
return len;
}
long led_ioctl(struct file *filp,
unsigned int cmd,
unsigned long arg)
{
return ;
}
int led_mmap(struct file *filp,
struct vm_area_struct *vma)
{
/*关闭对该段区域读写时的cache特性
*确保对寄存器的写入操作及时完成
* */
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
/*建立映射关系*/
remap_pfn_range(vma,//对象指针
//映射后的起始虚拟地址
vma->vm_start,
//页号(按页对齐)
0xc001c000>>,
vma->vm_end - vma->vm_start,
vma->vm_page_prot//访问属性
);
return ;
}
struct file_operations led_fops =
{
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.write = led_write,
.read = led_read,
.unlocked_ioctl = led_ioctl,
.mmap = led_mmap,
};
int __init led_drv_init(void)
{
/*申请注册设备号*/
alloc_chrdev_region(&dev, , , "myleds"); /*2 初始化cdev*/
cdev_init(&led_cdev, &led_fops);
/*3 注册cdev*/
cdev_add(&led_cdev, dev, );
/*4 自动创建设备文件*/ /*会导致 "/sys/class/LEDS/" */
cls = class_create(THIS_MODULE, "LEDS");
/*会导致 "/sys/class/LEDS/myleds/"*/
device_create(cls, NULL, dev, NULL,
"myleds"); return ;
}
void __exit led_drv_exit(void)
{
/*自动销毁设备文件*/
device_destroy(cls, dev);
class_destroy(cls); /*4 注销cdev*/
cdev_del(&led_cdev);
/*5 注销设备号*/
unregister_chrdev_region(dev, );
}
module_init(led_drv_init);
module_exit(led_drv_exit);
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h> /*
*./test <on/off>
* */
int main(int argc ,char *argv[])
{
void *gpioc_base = NULL;
volatile unsigned int *gpiocout= NULL;
volatile unsigned int *gpiocoutenb= NULL;
volatile unsigned int *gpiocaltfn0= NULL; unsigned int tmp = ; if(argc != )
{
printf("usage: %s <on/off>\n",
argv[]);
return -;
} int fd = open("/dev/myleds", O_RDWR); if(fd < )
{
perror("open failed");
return -;
}
printf("open successed,using device....\n"); gpioc_base = mmap(NULL,0x1000,
PROT_READ|PROT_WRITE,
MAP_SHARED,
fd,
);
gpiocout = (volatile unsigned int *)(gpioc_base+);
gpiocoutenb = (volatile unsigned int *)(gpioc_base+);
gpiocaltfn0 = (volatile unsigned int *)(gpioc_base+0x20);
/*选择功能1*/
*gpiocaltfn0 &= ~(<<);
*gpiocaltfn0 |= (<<);
/*设置为输出模式*/
*gpiocoutenb |= (<<); if(strcmp(argv[], "on") == )
*gpiocout &= ~(<<);
else
*gpiocout |= <<; /*取消映射*/
munmap(gpioc_base, 0x1000); close(fd); return ;
}
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h> #define _COLOR_RED 0x00ff0000
#define _COLOR_GREEN 0x0000ff00
#define _COLOR_BLUE 0x000000ff static struct fb_fix_screeninfo fb_fix ={};
static struct fb_var_screeninfo fb_var ={}; long screen_size=; int *fb32 =NULL;
int main()
{
int fd = -;
int x,y;
fd =open("/dev/fb0",O_RDWR);
if(fd < )
{
printf("open dev fb0 fail.\n");
return -;
}
//get lcd param
ioctl(fd,FBIOGET_FSCREENINFO,&fb_fix); ioctl(fd,FBIOGET_VSCREENINFO,&fb_var);
//显存的大小
screen_size = fb_var.xres*fb_var.yres*(fb_var.bits_per_pixel/); fb32 =mmap(,screen_size,PROT_READ |PROT_WRITE,MAP_SHARED,fd,); if(fb32 == NULL)
{
printf("mmap framebuffer fail.\n");
return -;
} /*将以下代码替换为显示tarena_logo图片*/ for(y=;y< fb_var.yres/;y++)
{
for(x=;x< fb_var.xres;x++)
{
*(fb32 +y*fb_var.xres + x) = _COLOR_RED;
}
} for(;y< fb_var.yres*/;y++)
{
for(x=;x< fb_var.xres;x++)
{
*(fb32 +y*fb_var.xres + x) = _COLOR_GREEN;
}
} for(;y< fb_var.yres;y++)
{
for(x=;x< fb_var.xres;x++)
{
*(fb32 +y*fb_var.xres + x) = _COLOR_BLUE;
}
}
munmap(fb32,screen_size);
close(fd);
return ;
}
内存管理,goto的使用,内存的申请和释放,mmap,ioremap的更多相关文章
- java虚拟机学习-JVM内存管理:深入Java内存区域与OOM(3)
概述 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又 ...
- Go语言内存管理(一)内存分配
Go语言内存管理(一)内存分配 golang作为一种"高级语言",也提供了自己的内存管理机制.这样一方面可以简化编码的流程,降低因内存使用导致出现问题的频率(C语言使用者尤其是初学 ...
- 启动期间的内存管理之bootmem_init初始化内存管理–Linux内存管理(十二)
1. 启动过程中的内存初始化 首先我们来看看start_kernel是如何初始化系统的, start_kerne定义在init/main.c?v=4.7, line 479 其代码很复杂, 我们只截取 ...
- JVM内存管理:深入Java内存区域与OOM
Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝 ...
- Objective-C 【多个对象内存管理(野指针&内存泄漏)】
------------------------------------------- 多个对象内存管理(野指针&内存泄漏) (注:这一部分知识请结合"单个对象内存管理"去 ...
- 常用Actoin算子 与 内存管理 、共享变量、内存机制
一.常用Actoin算子 (reduce .collect .count .take .saveAsTextFile . countByKey .foreach ) collect:从集群中将所有的计 ...
- 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...
- 待解决问题:c++栈对象的析构、虚拟内存与内存管理的关系、内存管理的解决方案。
待解决问题:c++栈对象的析构.虚拟内存与内存管理的关系.内存管理的解决方案.
- 七.OC基础加强--1.内存管理 2.野指针,内存泄露 3.set方法的内存管理 4.@property参数 5.@class和循环retain的使用 6.NSString的内存管理
1,内存管理简单介绍 1,为什么要有内存管理? malloc selloc dealloc```需要回头复习 一般的内存 4s 是512m内存:6 是1024m内存: 当内存过大时,会耗尽内存.出现程 ...
- 【转】Java内存管理:深入Java内存区域
转自:http://www.cnblogs.com/gw811/archive/2012/10/18/2730117.html 本文引用自:深入理解Java虚拟机的第2章内容 Java与C++之间有一 ...
随机推荐
- 浅析Block闭包
浅析Block闭包 简单来说,block就是将函数及其上下文封装起来的对象,从功能上可以把它看作是C++中的匿名函数,也可称之为块. Block类型写法: 返回值+(^块名)+(参数)= ^(参数){ ...
- poj2762 判断一个图中任意两点是否存在可达路径 也可看成DAG的最小覆盖点是否为1
Going from u to v or from v to u? Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 179 ...
- Tomcat——启动报错:Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/Servlet_app02a]]
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start com ...
- mysql 赋权语句
grant all privileges on phplampDB.* to phplamp@localhost identified by '1234';
- SEPC:使用3D卷积从FPN中提取尺度不变特征,涨点神器 | CVPR 2020
论文提出PConv为对特征金字塔进行3D卷积,配合特定的iBN进行正则化,能够有效地融合尺度间的内在关系,另外,论文提出SEPC,使用可变形卷积来适应实际特征间对应的不规律性,保持尺度均衡.PConv ...
- 实验四:Linux系统C语言开发环境学习
项目 内容 这个作业属于哪个课程 班级课程主页链接 这个作业的要求在哪里 作业要求 学号-姓名 17043133-木腾飞 作业学习要求 1.学习Linux系统中如何查看帮助文档:2.在Linux系统中 ...
- vue 上拉刷新组件
背景,项目中经常会出现需要上拉加载更多或者下拉刷新的需求,一直以来呢都是借用各种UI库来实现,但是不知道啥情况,最近在使用的时候,一直有问题,出不了效果,然人很恼火,于是只能自己动手来实现以下, 这次 ...
- [杂谈-随口一说]Keep learning!
随口一说 好些日子没有发表公号文章了, 想说,最近真是忙呢,有时候觉得真忙,有时候还觉得忙的脑子一团乱麻. 原计划的公众号文章将近一个月了一篇没写,时间,都去哪儿了? 周末自己搬家,工作中的任务,学习 ...
- JavaScript ——内部函数和匿名函数
在JS中,函数是一种数据类型,可以将它赋值给变量,因此函数可以这样创建: var func=function(){ alert("func"); } func(); 既然函数是一种 ...
- 【译】Gartner CWPP市场指南
https://www.gartner.com/doc/reprints?id=1-1YSHGBQ8&ct=200416&st=sb?utm_source=marketo&ut ...