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的更多相关文章

  1. java虚拟机学习-JVM内存管理:深入Java内存区域与OOM(3)

    概述 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又 ...

  2. Go语言内存管理(一)内存分配

    Go语言内存管理(一)内存分配 golang作为一种"高级语言",也提供了自己的内存管理机制.这样一方面可以简化编码的流程,降低因内存使用导致出现问题的频率(C语言使用者尤其是初学 ...

  3. 启动期间的内存管理之bootmem_init初始化内存管理–Linux内存管理(十二)

    1. 启动过程中的内存初始化 首先我们来看看start_kernel是如何初始化系统的, start_kerne定义在init/main.c?v=4.7, line 479 其代码很复杂, 我们只截取 ...

  4. JVM内存管理:深入Java内存区域与OOM

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝 ...

  5. Objective-C 【多个对象内存管理(野指针&内存泄漏)】

    ------------------------------------------- 多个对象内存管理(野指针&内存泄漏) (注:这一部分知识请结合"单个对象内存管理"去 ...

  6. 常用Actoin算子 与 内存管理 、共享变量、内存机制

    一.常用Actoin算子 (reduce .collect .count .take .saveAsTextFile . countByKey .foreach ) collect:从集群中将所有的计 ...

  7. 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  8. 待解决问题:c++栈对象的析构、虚拟内存与内存管理的关系、内存管理的解决方案。

    待解决问题:c++栈对象的析构.虚拟内存与内存管理的关系.内存管理的解决方案.

  9. 七.OC基础加强--1.内存管理 2.野指针,内存泄露 3.set方法的内存管理 4.@property参数 5.@class和循环retain的使用 6.NSString的内存管理

    1,内存管理简单介绍 1,为什么要有内存管理? malloc selloc dealloc```需要回头复习 一般的内存 4s 是512m内存:6 是1024m内存: 当内存过大时,会耗尽内存.出现程 ...

  10. 【转】Java内存管理:深入Java内存区域

    转自:http://www.cnblogs.com/gw811/archive/2012/10/18/2730117.html 本文引用自:深入理解Java虚拟机的第2章内容 Java与C++之间有一 ...

随机推荐

  1. 浅析Block闭包

    浅析Block闭包 简单来说,block就是将函数及其上下文封装起来的对象,从功能上可以把它看作是C++中的匿名函数,也可称之为块. Block类型写法: 返回值+(^块名)+(参数)= ^(参数){ ...

  2. poj2762 判断一个图中任意两点是否存在可达路径 也可看成DAG的最小覆盖点是否为1

      Going from u to v or from v to u? Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 179 ...

  3. Tomcat——启动报错:Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/Servlet_app02a]]

    java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start com ...

  4. mysql 赋权语句

    grant all privileges on phplampDB.* to phplamp@localhost identified by '1234';

  5. SEPC:使用3D卷积从FPN中提取尺度不变特征,涨点神器 | CVPR 2020

    论文提出PConv为对特征金字塔进行3D卷积,配合特定的iBN进行正则化,能够有效地融合尺度间的内在关系,另外,论文提出SEPC,使用可变形卷积来适应实际特征间对应的不规律性,保持尺度均衡.PConv ...

  6. 实验四:Linux系统C语言开发环境学习

    项目 内容 这个作业属于哪个课程 班级课程主页链接 这个作业的要求在哪里 作业要求 学号-姓名 17043133-木腾飞 作业学习要求 1.学习Linux系统中如何查看帮助文档:2.在Linux系统中 ...

  7. vue 上拉刷新组件

    背景,项目中经常会出现需要上拉加载更多或者下拉刷新的需求,一直以来呢都是借用各种UI库来实现,但是不知道啥情况,最近在使用的时候,一直有问题,出不了效果,然人很恼火,于是只能自己动手来实现以下, 这次 ...

  8. [杂谈-随口一说]Keep learning!

    随口一说 好些日子没有发表公号文章了, 想说,最近真是忙呢,有时候觉得真忙,有时候还觉得忙的脑子一团乱麻. 原计划的公众号文章将近一个月了一篇没写,时间,都去哪儿了? 周末自己搬家,工作中的任务,学习 ...

  9. JavaScript ——内部函数和匿名函数

    在JS中,函数是一种数据类型,可以将它赋值给变量,因此函数可以这样创建: var func=function(){ alert("func"); } func(); 既然函数是一种 ...

  10. 【译】Gartner CWPP市场指南

    https://www.gartner.com/doc/reprints?id=1-1YSHGBQ8&ct=200416&st=sb?utm_source=marketo&ut ...