/***************************************************分配内存******************************************************************/

/*

*linux对内存的管理总体上可以分为两大类:一是物理内存的管理;二是对虚拟内存的管理,前者用于特定的平台架构上的实际物理内存空间的管理,后者用于特定的处理器体系架构上的虚拟地址空间的管理。

*

*linux的物理内存管理:其引入了内存节点(node)、内存区域(zone)和内存页(page)的概念,其对物理内存的管理总体上又可以分成两大部分:最底层实现的是页面级内存管理,然后是基于页面级管理之上的slab内存管理

*/

#include

void kmalloc(size_t size, int flags); //size为要分配的内存大小,flags为分配标志

//kmalloc分配的内存区域仍然保持原有的数据,并没有清零,它分配的内存区域在物理内存中也是连续的

//flags标志的取值常用的有以下几种:

GFP_ATOMIC   //用于在中断处理例程或其他运行于进程上下文之外的代码中分配内存,不会休眠

GFP_KERNEL   //内核内存的通常分配方法,可能引起休眠,最常用

GFP_USER     //用于用户空间页分配内存,可能会休眠

//上面的标志可以和下面常用标志“或”起来使用,下面这些标志控制如何进行分配

__GFP_DMA        //该标志请求分配发送在可进行DMA的内存区域

__GFP_COLD       //对于DMA读取的页面分配,可使用这个标志

__GFP_REPEAT     //如果分配不成功,使用这个标志时,它会重新尝试分配,但仍有可能失败

__GFP_NORETRY    //告诉分配器,如果所请求的内存不可获得,就立即返回

/*

*linux内核把内存分为3个区段:可用于DMA的内存、常规内存以及高端内存,通常的内存分配发生在常规内存区

*内核负责管理物理内存,物理内存只能按页面进行分配

*   linux处理内存分配的方法是,创建一系列的内存对象池,每个池中的内存块大小是固定一致,处理分配请求时,就直接在包含有足够大的内存块的池中传递一个整块给请求者,对kmalloc能够分配的内存块大小

*存在一个上限,如果希望代码具有完整的可移植性,则不应该分配大于128K的内存

*/

/*后备高速缓存(能够反复使用内存池(设备驱动程序通常不会涉及到它))*/:

//linux内核的高速缓存管理有时称为“slab分配器”。

#include //slab分配器实现的高速缓存具有kmem_cache_t类型,可通过调用kmem_cache_creat创建:

kmem_cache_t *xxx_cache;

kmem_cache_t *kmem_cache_create(const char *name, size_t size,size_t offset,unsigned long flags,void (*constructor)(void *, kmem_cache_t *,unsigned long flags),

void (*destructor)(void *, kmem_cache_t *,unsigned long flags));  //该函数创建一个新的高速缓存对象,其中可以容纳任意数目的内存区域,这些区域的大小都相同,由size指定,

//name 参数和这个缓存关联并且作为一个在追踪问题时有用的管理信息; 通常, 它被设置为被缓存的结构类型的名子. 这个缓存保留一个指向

//name 的指针, 而不是拷贝它, 因此驱动应当传递一个指向在静态存储中的名子的指针(常常这个名子只是一个文字字串). 这个名子不能包含空格

//offset 是页内的第一个对象的偏移; 它可被用来确保一个对被分配的对象的特殊对齐, 但是你最可能会使用 0 表示使用默认值

//flags 控制如何进行分配并且是下列标志的一个位掩码:1:SLAB_NO_REAP设置这个标志保护缓存在系统查找内存时不会被减少. 设置这个标志通常是

//个坏主意; 重要的是避免不必要地限制内存分配器的行动自由:2:SLAB_HWCACHE_ALIGN这个标志需要每个数据对象被对齐到一个缓存行

//SLAB_CACHE_DMA这个标志要求每个数据对象在 DMA 内存区分配.

//函数的 constructor 和 destructor 参数是可选函数( 但是不能只有destructor 而没有 constructor ); 前者可以用来初始化新分配的对象,

//后者可以用来"清理"对象在它们的内存被作为一个整体释放回给系统之前.

//一旦某个对象的高速缓存被创建,就可以调用kmem_cache_alloc从中分配内存对象了:

void *kmem_cache_alloc(kmem_cache_t *cache,int flags); //cache是前面创建的高速缓存,flags同kmalloc的flags相同

void *kmem_cache_free(kmem_cache_t *cache,const void *obj); //释放一个内存对象

int *kmem_cache_destory(kmem_cache_t *cache); //释放高速缓存,注意检查返回状态

/******内存池(在内存分配不允许失败的使用它)*********/:

#include mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn,  mempool_free_t *free_fn, void *pool_data); //创建内存池对象,min_nr表示内存池应始终保持的已分配对象的最少数目

//对象的实际分配和释放由alloc_fn和free_fn函数处理,pool_data为它们的参数,其原型如下:

typedef void *(mempool_alloc_t)(int gfp_mask, void *pool_data);  //一般为mempool_alloc_slab函数,是内核已经定义好的函数

typedef void (mempool_free_t)(void *element, void *pool_data);   //一般为mempool_free_slab函数,是内核已经定义好的函数

//在建立内存池后,可通过下面函数分配和释放对象:

void * mempool_alloc(mempool_t *pool, gfp_t gfp_mask);   //分配内存池对象

void mempool_free(void *element, mempool_t *pool);  //释放内存池对象

int mempool_resize(mempool_t *pool, int new_min_nr, int gfp_mask);//调整mempool的大小,如果调用成功,将把内存池的大小调整为至少有new_min_nr个预分配对象

void mempool_destroy(mempool_t *pool);  //如果不需要内存池了,可使用它将其返还给系统

//注意:应尽量避免在驱动程序中使用mempool

/*********如果需要分配大块的内存,使用下面分配页面函数************/

get_zeroed_page(unsigned int flags);  //返回一个指向新页的指针并且将页面清零

__get_free_page(unsigned int flags);  //类似于 get_zeroed_page, 但是没有清零该页

__get_free_pages(unsigned int flags, unsigned int order);  //分配若干(物理连续)页面,返回一个指向第一个内存区第一个字节的指针, 但不清零页面

//flags同kmalloc的flags相同

//order是要申请或释放的页面数的以2为底的对数即log2N,如果你要一个页 order 为 0, 如果你请求 8 页就是 3.

//如果 order 太大(没有那个大小的连续区可用), 页分配失败,get_order 函数, 它使用一个整数参数, 可以用来从一个 size 中提取

//order(它必须是 2 的幂)给主机平台. order 允许的最大值是 10 或者 11 (对应于 1024 或者 2048 页), 依赖于体系. 但是, 一个

//order=10 的分配在除了一个刚刚启动的有很多内存的系统中成功的机会是小的.

//当不再需要使用页面时,可以使用下面函数之一来释放:

void free_page(unsigned long addr);

void free_pages(unsigned long addr, unsigned long order);

//基于页面的分配策略的优点实际不是在速度上,而是在于更有效地使用内存

/*********vmalloc及其辅助函数**********/:

//它分配虚拟地址空间的连续内存,这段区域在物理上可能是不连续的,vmalloc在发生错误时返回NULL,成功时返回一个指针,大多数情况下不鼓励使用vmalloc,它获得的内存使用起来效率不高

#include void *vmalloc(unsigned long size);

void vfree(void * addr);

void *ioremap(unsigned long offset, unsigned long size);  //它和vmalloc一样也需要新建页表,但其并不实际分配内存,它的返回值是一个特殊的虚拟地址,可以用来访问指定的物理内存区域,它更多的用于映射物理地址到内核空间

void iounmap(void * addr);

/*

*kmalloc和__get_free_pages返回的内存地址也是虚拟地址,其实际仍然要由MMU处理才能转换物理地址

*   用vmalloc分配得到的地址是不能在处理器之外使用的,因为它们在处理器的内存管理单元上才有意义,当驱动程序需要真正的物理地址时就不能使用vmalloc了

*使用vmalloc的正确场合是在分配一大块连续的、只在软件中存在、用于缓冲的内存区域的时候

*/

/****************************************************end**********************************************************************/

/****************************************************使用I/O端口***************************************************************/

#include struct resource *request_region(unsigned long first, unsigned long n,const char *name); //申请I/O端口,这个函数告诉内核,我们要使用起始first的n个端口,参数name应该是设备的名称,

//分配成功,返回非NULL值,返回NULL那我们就不能使用这些端口,所有的端口分配可从/proc/ioports中得到

void release_region(unsigned long first, unsigned long n);  //如果不在使用某组I/O端口,调用它将这些端口返回给系统

int check_region(unsigned long first, unsigned long n);  //检查给定的I/O端口是否可用,如果不可用,返回负的错误码

#include

unsigned inb(unsigned port);

void outb(unsigned char byte, unsigned port);  //读或写字节端口( 8 位宽 ). port 参数定义为 unsigned long 在某些平台以及 unsigned short 在一些的平台上. inb 的返回类型也是跨体系而不同的

unsigned inw(unsigned port);

void outw(unsigned short word, unsigned port); //这些函数访问16位端口

unsigned inl(unsigned port);

void outl(unsigned longword, unsigned port); //这些函数访问 32位 端口. longword 根据不同的平台被定义为 unsigned long 或者unsigned int

void insb(unsigned port, void *addr, unsigned long count);

void outsb(unsigned port, void *addr, unsigned long count);//从内存地址addr开始连续读或写count数目的字节. 数据读自或者写入单个port 端口

void insw(unsigned port, void *addr, unsigned long count);

void outsw(unsigned port, void *addr, unsigned long count); //对16位端口连续读写16位数据

void insl(unsigned port, void *addr, unsigned long count);

void outsl(unsigned port, void *addr, unsigned long count); //对32位端口连续读写32位数据,它们直接将字节流从端口中读取或写入

struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);   //分配I/O内存区域,该函数从start开始分配len字节长的内存区域,如果成功,返回非NULL指针

void release_mem_region(unsigned long start, unsigned long len);  //释放用上面函数分配的I/O内存区域

int check_mem_region(unsigned long start, unsigned long len);  //用来检查给定的I/O内存区域是否可用的老函数,这个函数不安全,应避免使用

#include #define ioremap(cookie,size)  __arm_ioremap(cookie, size, MT_DEVICE)  /* 为I/O内存区域分配虚拟地址,一旦调用这个函数后,设备驱动程序即可访问任意的I/O内存地址了 */

#define iounmap(cookie)   __iounmap(cookie)  /* 取消映射 */

#define ioread8(p) ({ unsigned int __v = __raw_readb(p); __v; })   /* 从I/O内存p读取8位数据 */

#define ioread16(p) ({ unsigned int __v = le16_to_cpu((__force __le16)__raw_readw(p)); __v; })/* 从I/O内存p读取16位数据 */

#define ioread32(p) ({ unsigned int __v = le32_to_cpu((__force __le32)__raw_readl(p)); __v; })/* 从I/O内存p读取32位数据 */

#define iowrite8(v,p) __raw_writeb(v, p)   /* 向I/O内存p写入8位数据v */

#define iowrite16(v,p) __raw_writew((__force __u16)cpu_to_le16(v), p) /* 向I/O内存p写入16位数据v */

#define iowrite32(v,p) __raw_writel((__force __u32)cpu_to_le32(v), p) /* 向I/O内存p写入32位数据v */

#define memset_io(c,v,l) _memset_io(__mem_pci(c),(v),(l))    /* 将c开始l个I/O内存设置值v*/

#define memcpy_fromio(a,c,l) _memcpy_fromio((a),__mem_pci(c),(l)) /*从I/O内存c拷贝l个数据到a*/

#define memcpy_toio(c,a,l) _memcpy_toio(__mem_pci(c),(a),(l)) /*从内存a拷贝l个数据到I/O内存c*/

下面是老的内存读写函数,不推荐使用:

#define readb(c) ({ __u8  __v = __raw_readb(__mem_pci(c)); __v; }) /* 写8位数据 */

#define readw(c) ({ __u16 __v = le16_to_cpu((__force __le16) \     /* 写16位数据 */

__raw_readw(__mem_pci(c))); __v; })

#define readl(c) ({ __u32 __v = le32_to_cpu((__force __le32) \       /* 写32位数据 */

__raw_readl(__mem_pci(c))); __v; })

#define writeb(v,c)  __raw_writeb(v,__mem_pci(c))  /* 写8位数据 v到I/O c */

#define writew(v,c)  __raw_writew((__force __u16) \   /* 写16位数据v到I/O c  */

cpu_to_le16(v),__mem_pci(c))

#define writel(v,c)  __raw_writel((__force __u32) \     /* 写32位数据v到I/O c */

cpu_to_le32(v),__mem_pci(c))

void *ioport_map(unsigned long port, unsigned int count); //该函数重新映射count个I/O端口,使其看起来像I/O内存,此后驱动程序可在该函数返回的地址上使用ioread8及其同类函数了,

//需要注意的是在重映射之前,必须通过request_region分配这些端口

void *ioport_unmap(void *addr); //撤销上面的映射

/****************************************************end**********************************************************************/

linux 进城 io字节,(2)linux内核之内存分配与IO口操作相关推荐

  1. Linux内核中内存分配函数

    1.原理说明 Linux内核 中采 用了一种同时适用于32位和64位系统的内 存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系 统中,用到了四级页表,如图2-1所示.四级页表分别为 ...

  2. 深入分析linux内核的内存分配函数devm_kzalloc

    在分析驱动代码的时候,经常会遇到使用devm_kzalloc()为一个设备分配一片内存的情况.devm_kzalloc()是内核用来分配内存的函数,同样可以分配内存的内核函数还有devm_kmallo ...

  3. linux内核dma内存分配,Linux 4.x 内核空间 DMA 虚拟内存地址

    Architecture: i386 32bit Machine Ubuntu 16.04 Linux version: 4.15.0-39-generic 目录 DMA 虚拟内存区 在 IA32 体 ...

  4. linux中的DMA提速1:各种内存分配验证

    前言 驱动需要优化驱动中的DMA速度.在此记录.使用DMA传输1280*800的数据就是1M字节. 使用外设的DMA功能,就需要给外设相应的DMA缓存寄存器设置一个地址,这个地址在内核中叫总线地址,而 ...

  5. ARM Linux (S3C6410架构/2.6.35内核)的内存映射(三)

    这里记录一下Linux内核做二级内存映射的过程,以中断向量表的映射过程为例. 在S3C6410架构下,Linux采用的是粗粒度小页内存管理方式,即内存段(section)的大小为1M,而页(page) ...

  6. linux 进城 管道丢数据,linux – 使用命名管道与bash – 数据丢失的问题

    有人在线搜索,发现简单的"教程"使用命名管道.但是,当我做任何后台工作时,我似乎失去了大量的数据. [[编辑:找到一个更简单的解决方案,看到回复帖子.所以我提出的问题现在是学术性的 ...

  7. linux dev/shm,巧用linux服务器下的/dev/shm/,避开磁盘IO不给力!

    巧用linux服务器的/dev/shm/,如果合理使用,可以避开磁盘IO不给力,提高网站访问速度. 首先让我们认识一下,什么是tmpfs和/dev/shm/? tmpfs是Linux/Unix系统上的 ...

  8. Linux 操作系统原理 — 内存 — 内存分配算法

    目录 文章目录 目录 前文列表 内存碎片 伙伴(Buddy)分配算法 Slab 算法 虚拟内存的分配 内核态内存分配 vmalloc 函数 kmalloc 用户态内存分配 malloc 申请内存 用户 ...

  9. 怎么结束linux里的redis进程,linux 怎么结束redis的monitor命令

    用户:392647181 2016年08月19日 /etc/sysctl.conf 添加 vm.overcommit_memory=1 刷新配置使之生效 sysctl vm.overcommit_me ...

最新文章

  1. R包corrplot绘图相关系数矩阵
  2. 无法完成安装:'unsupported configuration: hda-duplex not supported in this QEMU binary'
  3. android log4,GitHub - oronno/log4android: Log4Android - Simple Logging Wrapper Library for Android
  4. 用python 爬取百度百科内容-使用python爬取小说全部内容
  5. 【Android 逆向】Android 进程注入工具开发 ( 远程进程 注入动态库 文件操作 | Android 进程读取文件所需的权限 | fopen 打开文件标志位 | 验证文件权限 )
  6. vs.net2003无法打开*.xsd文件的解决方法
  7. Oracle 安装报错 [INS-06101] IP address of localhost could not be determined 解决方法
  8. matlab车辆贪心作业调度,贪心算法-区间调度-Interval Scheduling
  9. java 单选下拉_jQuery对单选框radio,复选框checkbox,下拉列表select的操作
  10. UI(1)---2018 UI 设计趋势
  11. android studio上下滚动菜单,Android模仿美团顶部的滑动菜单实例代码
  12. [Luogu P2893][USACO08FEB]修路Making the Grade
  13. 特殊类型窗体制作: 制作字形窗体
  14. Tensorflow实例,拟合二维数据
  15. mvn命令传递参数和pom文件配置的关系
  16. Nginx笔记(一):安装
  17. Illegal use of when-style tag without ...
  18. 基于matlab的车牌识别
  19. FPGA中case语句4选1数据选择器
  20. IDEA 代码格式化插件Save Actions示范

热门文章

  1. selenium 爬虫项目实例 爬取社保五险
  2. VLC可用的模块列表
  3. CRC32原理及实现学习
  4. pandas库读取多个excel文件数据并进行筛选合并处理后导入到新表格中
  5. 通过 Moya + RxSwift + Argo 完成网络请求
  6. MS51替换N76E003注意事项
  7. nfc读卡java开发,分享一段飞天R502读卡器的JAVA读卡代码
  8. CSS属性 – font-size
  9. Android动态换肤实现原理解析,原理+实战+视频+源码
  10. python re re.compile search groupdict 正则多取值