• 内存管理,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 = 0;
        /*申请2^3个物理内存页 并映射*/
        pages_addr = __get_free_pages(GFP_KERNEL, 3);
        if(!pages_addr)
        {
            printk("<1>" "get pages failed!");
            ret = -ENOMEM;
            goto failure_pages;
        }
        printk("<1>" "get pages vir=%#x  phys=%#x
    ",
                pages_addr, virt_to_phys(pages_addr));
    
        /*申请200字节*/
        kmalloc_addr = kmalloc(200, GFP_KERNEL);
        if(!kmalloc_addr)
        {
            ret = -ENOMEM;
            goto failure_kmalloc;
        }
        printk("<1>" "kmalloc vir=%#x phys=%#x
    ",
                kmalloc_addr, virt_to_phys(kmalloc_addr));
        vmalloc_addr = vmalloc(1024*10);
        if(!vmalloc_addr)
        {
            ret = -ENOMEM;
            goto failure_vmalloc;
        }
        printk("<1>" "vmalloc vir=%#x phys=%#x
    ",
                vmalloc_addr, virt_to_phys(vmalloc_addr));
    
        return 0;
    failure_vmalloc:
        kfree(kmalloc_addr);
    failure_kmalloc:
        free_pages(pages_addr, 3);
    failure_pages:
        return ret;
    }
    void __exit kernelspace_exit(void)
    {
        vfree(vmalloc_addr);
        kfree(kmalloc_addr);
        free_pages(pages_addr, 3);
        
    }
    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 = 0;
        /*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 &= ~(3<<24);
        data |= (1<<24);
        writel(data, base+0x20);
    
        writel(readl(base+0x04)|(1<<12), base+0x04);
    
        writel(readl(base+0x00)&(~(1<<12)), base+0x00);
    
    
        return 0;
    }
    void __exit led_drv_exit(void)
    {
        writel(readl(base+0x00)|(1<<12), 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 = 0;
    int led_open(struct inode *inode, 
                 struct file *filp)
    {
        return 0;
    }
    int led_release(struct inode *inode, 
                    struct file *filp)
    {
        return 0;
    }
    int k_status = 1; //灭灯
    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 0;
    }
    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>>12,
                        vma->vm_end - vma->vm_start,
                        vma->vm_page_prot//访问属性
                );
        return 0;
    }
    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, 8, 1, "myleds");
    
        /*2 初始化cdev*/
        cdev_init(&led_cdev, &led_fops);
        /*3 注册cdev*/
        cdev_add(&led_cdev, dev, 1);
        /*4 自动创建设备文件*/
    
        /*会导致 "/sys/class/LEDS/" */
        cls = class_create(THIS_MODULE, "LEDS");
        /*会导致 "/sys/class/LEDS/myleds/"*/
        device_create(cls, NULL, dev, NULL, 
                      "myleds");
    
        return 0;
    }
    void __exit led_drv_exit(void)
    {
        /*自动销毁设备文件*/
        device_destroy(cls, dev);
        class_destroy(cls);
    
        /*4 注销cdev*/
        cdev_del(&led_cdev);
        /*5 注销设备号*/
        unregister_chrdev_region(dev, 1);
    }
    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 = 0;
    
        if(argc != 2)
        {
            printf("usage: %s <on/off>
    ",
                    argv[0]);
            return -1;
        }
    
        int fd = open("/dev/myleds", O_RDWR);
    
        if(fd < 0)
        {
            perror("open failed");
            return -1;
        }
        printf("open successed,using device....
    ");
        
        gpioc_base = mmap(NULL,0x1000, 
                          PROT_READ|PROT_WRITE,
                          MAP_SHARED,
                          fd,
                          0);
        gpiocout = (volatile unsigned int *)(gpioc_base+0);
        gpiocoutenb = (volatile unsigned int *)(gpioc_base+4);
        gpiocaltfn0 = (volatile unsigned int *)(gpioc_base+0x20);
        /*选择功能1*/
        *gpiocaltfn0 &= ~(3<<24);
        *gpiocaltfn0 |= (1<<24);
        /*设置为输出模式*/
        *gpiocoutenb |= (1<<12);
    
        if(strcmp(argv[1], "on") == 0)
            *gpiocout &= ~(1<<12);
        else
            *gpiocout |= 1<<12;
    
        /*取消映射*/
        munmap(gpioc_base, 0x1000);
        
        close(fd);
    
        return 0;
    }
    #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 ={0};
    static struct fb_var_screeninfo fb_var ={0};
    
    long screen_size=0;
    
    int *fb32 =NULL;
    int main()
    {
        int fd = -1;
        int x,y;
        fd =open("/dev/fb0",O_RDWR);
        if(fd < 0)
        {
            printf("open dev fb0 fail.
    ");
            return -1;
        }
        //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/8);
    
        fb32 =mmap(0,screen_size,PROT_READ |PROT_WRITE,MAP_SHARED,fd,0);
    
        if(fb32 == NULL)
        {
            printf("mmap framebuffer fail.
    ");
            return -1;
        }
    
        /*将以下代码替换为显示tarena_logo图片*/
        
        for(y=0;y< fb_var.yres/3;y++)
        {
            for(x=0;x< fb_var.xres;x++)
            {
                *(fb32 +y*fb_var.xres + x) = _COLOR_RED;
            }
        }
    
        for(;y< fb_var.yres*2/3;y++)
        {
            for(x=0;x< fb_var.xres;x++)
            {
                *(fb32 +y*fb_var.xres + x) = _COLOR_GREEN;
            }
        }
    
        for(;y< fb_var.yres;y++)
        {
            for(x=0;x< fb_var.xres;x++)
            {
                *(fb32 +y*fb_var.xres + x) = _COLOR_BLUE;
            }
        }
        munmap(fb32,screen_size);
        close(fd);
        return 0;
    }
  • 相关阅读:
    OS + Linux + zipTool / tar / tar.gz / zst
    project scm
    product wiki confluence
    script ActionScript / ColdFusion
    链表例题
    链表原理
    链表例题
    链表原理
    链表原理
    链表原理
  • 原文地址:https://www.cnblogs.com/DXGG-Bond/p/11892261.html
Copyright © 2020-2023  润新知