Linux驱动程序——内存管理

文章目录

获取系统的物理内存信息

Linux 内核对每个物理页面都采用struct page数据结构来描述,内核为每一个物理页面都分配了这样一个struct page数据结构,并且存储到一个全局的数组mem_map[]中。它们之间一一映射,数组的第i个元素指向页帧号为i的物理页面的struct page数据结构。 基于此,我们可以编写一个内核模块,通过遍历这个mem_map[]`数组来统计当前系统有多少个空闲页面、保留页面、swapcache页面、slab页面、脏页面、活跃页面、正在回写的页面等。

代码

mem_info.c

c 复制代码
// mem_info.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/mm.h>

// 指定license版本
MODULE_LICENSE("GPL");
// 作者信息
MODULE_AUTHOR("Marvin");
// 模块描述
MODULE_DESCRIPTION("show memory info");
// 提供别名
MODULE_ALIAS("meminfo");

#define PRT(a, b)                                                              \
  pr_info("%-15s=%10d %10ld %8ld\n", a, b, (PAGE_SIZE * b) / 1024,             \
          (PAGE_SIZE * b) / 1024 / 1024)

//设置初始化入口函数
static int __init mem_info_init(void)
{
    struct page *p;
    int i;
    unsigned long pfn, valid = 0;
    int free = 0, locked = 0, reserved = 0, swapcache = 0, referenced = 0,
        slab = 0, private = 0, uptodate = 0, dirty = 0, active = 0,
        writeback = 0, mappedtodisk = 0;
    unsigned long num_physpages;
    num_physpages = get_num_physpages();

    for (i = 0; i< num_physpages; i++) {
      pfn = i + PREEMPT_OFFSET;
      // skip empty hole
      if (!pfn_valid(pfn)) {
        continue;
      }
      valid++;
      p = pfn_to_page(pfn);
      // page_count == 0 is a free page
      if (!page_count(p)) {
        free ++;
        continue;
      }
      if (PageLocked(p)) {
        locked++;
      }
      if (PageReserved(p)) {
        reserved++;
      }
      if (PageSwapCache(p)) {
        swapcache++;
      }
      if (PageReferenced(p)) {
        referenced++;
      }
      if (PageSlab(p)) {
        slab++;
      }
      if (PagePrivate(p)) {
        private++;
      }
      if (PageUptodate(p)) {
        uptodate++;
      }
      if(PageDirty(p)) {
        dirty++;
      }
      if (PageActive(p)) {
        active++;
      }
      if (PageWriteback(p)) {
        writeback++;
      }
      if (PageMappedToDisk(p)) {
        mappedtodisk++;
      }
    }

    pr_info("\nExamining %ld pages(num_phys_pages) = %ls MB\n", num_physpages,
            num_physpages * PAGE_SIZE / 1024 / 1024);
    pr_info("Pages with valid PFN's = %ld, = %ld MB\n", valid,
            valid * PAGE_SIZE / 1024 / 1024);
    pr_info("\n                      Pages       KB        MB\n\n");

    PRT("free", free);
    PRT("locked", locked);
    PRT("reserved", reserved);
    PRT("swapcache", swapcache);
    PRT("referenced", referenced);
    PRT("slab", slab);
    PRT("private", private);
    PRT("uptodate", uptodate);
    PRT("dirty", dirty);
    PRT("active", active);
    PRT("writeback", writeback);
    PRT("mappedtodisk", mappedtodisk);

    return 0;
}

//设置出口函数
static void __exit mem_info_exit(void)
{
    printk(KERN_DEBUG "goodbye!!!\n");
}

//将上述定义的init()和exit()函数定义为模块入口/出口函数
module_init(mem_info_init);
module_exit(mem_info_exit);

Makefile

makefile 复制代码
ifneq ($(KERNELRELEASE),)
MODULE_NAME = showmeminfo
$(MODULE_NAME)-objs := mem_info.o
obj-m := $(MODULE_NAME).o
else
KERNEL_DIR = /lib/modules/`uname -r`/build
MODULEDIR := $(shell pwd)
 
.PHONY: modules
default: modules
 
modules:
	make -C $(KERNEL_DIR) M=$(MODULEDIR) modules
 
clean distclean:
	make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean
	rm -f *.o *.mod.c .*.*.cmd *.ko
	rm -rf .tmp_versions
endif

测试

bash 复制代码
$ sudo insmod showmeminfo.ko

dmesg 输出

复制代码
[ 1943.870130] 
               Examining 1043558 pages(num_phys_pages) = (efault) MB
[ 1943.870135] Pages with valid PFN's = 524287, = 2047 MB
[ 1943.870136] 
                                     Pages       KB        MB

[ 1943.870136] free           =     43554     174216      170
[ 1943.870138] locked         =         0          0        0
[ 1943.870139] reserved       =     29070     116280      113
[ 1943.870140] swapcache      =         0          0        0
[ 1943.870140] referenced     =    148195     592780      578
[ 1943.870141] slab           =     85462     341848      333
[ 1943.870142] private        =    179942     719768      702
[ 1943.870142] uptodate       =    346732    1386928     1354
[ 1943.870143] dirty          =     14310      57240       55
[ 1943.870144] active         =        25        100        0
[ 1943.870145] writeback      =         0          0        0
[ 1943.870145] mappedtodisk   =     88168     352672      344

分配内存

在Linux内核中,可以通过alloc_page分配一个物理页面,然后输出该物理页面的物理地址,并输出该物理页面在内核空间的虚拟地址。

可以通过__get_free_pages来分配物理页面,也可以通过kmalloc直接分配物理内存。

代码

mem_alloc.c

c 复制代码
// mem_alloc.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/vmalloc.h>

// 指定license版本
MODULE_LICENSE("GPL");
// 作者信息
MODULE_AUTHOR("Marvin");
// 模块描述
MODULE_DESCRIPTION("alloc memmory");
// 提供别名
MODULE_ALIAS("memalloc");

#define PRT(a, b)                                                              \
  pr_info("%-15s=%10d %10ld %8ld\n", a, b, (PAGE_SIZE * b) / 1024,             \
          (PAGE_SIZE * b) / 1024 / 1024)
#define MB (1024*1024)
static int mem = 64;
//设置初始化入口函数
static int __init mem_alloc_init(void)
{
    static char *kbuf, *vm_buff;
    static unsigned long order;
    int size;
    for (size = PAGE_SIZE, order = 0; order < MAX_ORDER; order++, size *= 2) {
        pr_info(" order=%2ld, pages=%5ld, size=%8d ", order, size / PAGE_SIZE, size);
        kbuf = (char *)__get_free_pages(GFP_ATOMIC, order);
        if (!kbuf) {
          pr_err("__get_free_pages failed!");
          break;
        }
        pr_info("__get_free_pages OK");
        free_pages((unsigned long)kbuf, order);
    }

    for (size = PAGE_SIZE, order = 0; order < MAX_ORDER; order++, size *= 2) {
      pr_info(" order=%2ld, pages=%5ld, size=%8d ", order, size / PAGE_SIZE, size);
        kbuf = kmalloc((size_t) size, GFP_ATOMIC);
        if (!kbuf) {
          pr_err("kmalloc failed!");
          break;
        }
        pr_info("kmalloc OK");
        kfree(kbuf);
    }

    for (size = 4 * MB; size <= mem * MB; size += 4 * MB) {
      pr_info(" pages=%6lu, size=%8lu ", size / PAGE_SIZE, size / MB);
      vm_buff = vmalloc(size);
      if (!vm_buff) {
        pr_err("... vmalloc failed\n");
        break;
      }
      pr_info("... vmalloc OK\n");
      vfree(vm_buff);
    }

    return 0;
}

//设置出口函数
static void __exit mem_alloc_exit(void)
{
    printk(KERN_DEBUG "goodbye!!!\n");
}

//将上述定义的init()和exit()函数定义为模块入口/出口函数
module_init(mem_alloc_init);
module_exit(mem_alloc_exit);

Makefile

Makefile 复制代码
ifneq ($(KERNELRELEASE),)
MODULE_NAME = memalloc
$(MODULE_NAME)-objs := mem_alloc.o
obj-m := $(MODULE_NAME).o
else
KERNEL_DIR = /lib/modules/`uname -r`/build
MODULEDIR := $(shell pwd)
 
.PHONY: modules
default: modules
 
modules:
	make -C $(KERNEL_DIR) M=$(MODULEDIR) modules
 
clean distclean:
	make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean
	rm -f *.o *.mod.c .*.*.cmd *.ko
	rm -rf .tmp_versions
endif

测试

bash 复制代码
$ sudo insmod memalloc.ko

dmesg输出:

复制代码
[ 5111.502527]  order= 0, pages=    1, size=    4096 
[ 5111.502531] __get_free_pages OK
[ 5111.502531]  order= 1, pages=    2, size=    8192 
[ 5111.502533] __get_free_pages OK
[ 5111.502534]  order= 2, pages=    4, size=   16384 
[ 5111.502537] __get_free_pages OK
[ 5111.502537]  order= 3, pages=    8, size=   32768 
[ 5111.502540] __get_free_pages OK
[ 5111.502540]  order= 4, pages=   16, size=   65536 
[ 5111.502544] __get_free_pages OK
[ 5111.502545]  order= 5, pages=   32, size=  131072 
[ 5111.502570] __get_free_pages OK
[ 5111.502571]  order= 6, pages=   64, size=  262144 
[ 5111.502583] __get_free_pages OK
[ 5111.502584]  order= 7, pages=  128, size=  524288 
[ 5111.502606] __get_free_pages OK
[ 5111.502607]  order= 8, pages=  256, size= 1048576 
[ 5111.502651] __get_free_pages OK
[ 5111.502652]  order= 9, pages=  512, size= 2097152 
[ 5111.502773] __get_free_pages OK
[ 5111.502782]  order=10, pages= 1024, size= 4194304 
[ 5111.502998] __get_free_pages OK
[ 5111.503002]  order= 0, pages=    1, size=    4096 
[ 5111.503004] kmalloc OK
[ 5111.503005]  order= 1, pages=    2, size=    8192 
[ 5111.503006] kmalloc OK
[ 5111.503006]  order= 2, pages=    4, size=   16384 
[ 5111.503008] kmalloc OK
[ 5111.503009]  order= 3, pages=    8, size=   32768 
[ 5111.503040] kmalloc OK
[ 5111.503041]  order= 4, pages=   16, size=   65536 
[ 5111.503048] kmalloc OK
[ 5111.503049]  order= 5, pages=   32, size=  131072 
[ 5111.503056] kmalloc OK
[ 5111.503057]  order= 6, pages=   64, size=  262144 
[ 5111.503078] kmalloc OK
[ 5111.503079]  order= 7, pages=  128, size=  524288 
[ 5111.503137] kmalloc OK
[ 5111.503138]  order= 8, pages=  256, size= 1048576 
[ 5111.503221] kmalloc OK
[ 5111.503225]  order= 9, pages=  512, size= 2097152 
[ 5111.503363] kmalloc OK
[ 5111.503365]  order=10, pages= 1024, size= 4194304 
[ 5111.503585] kmalloc OK
[ 5111.503588]  pages=  1024, size=       4 
[ 5111.503977] ... vmalloc OK
[ 5111.504094]  pages=  2048, size=       8 
[ 5111.504548] ... vmalloc OK
[ 5111.504946]  pages=  3072, size=      12 
[ 5111.505723] ... vmalloc OK
[ 5111.505995]  pages=  4096, size=      16 
[ 5111.506893] ... vmalloc OK
[ 5111.507246]  pages=  5120, size=      20 
[ 5111.508254] ... vmalloc OK
[ 5111.508652]  pages=  6144, size=      24 
[ 5111.509853] ... vmalloc OK
[ 5111.510383]  pages=  7168, size=      28 
[ 5111.511969] ... vmalloc OK
[ 5111.512642]  pages=  8192, size=      32 
[ 5111.514359] ... vmalloc OK
[ 5111.514982]  pages=  9216, size=      36 
[ 5111.517869] ... vmalloc OK
[ 5111.518718]  pages= 10240, size=      40 
[ 5111.521890] ... vmalloc OK
[ 5111.523108]  pages= 11264, size=      44 
[ 5111.528199] ... vmalloc OK
[ 5111.529169]  pages= 12288, size=      48 
[ 5111.533407] ... vmalloc OK
[ 5111.534582]  pages= 13312, size=      52 
[ 5111.539651] ... vmalloc OK
[ 5111.540847]  pages= 14336, size=      56 
[ 5111.544862] ... vmalloc OK
[ 5111.545983]  pages= 15360, size=      60 
[ 5111.551694] ... vmalloc OK
[ 5111.552957]  pages= 16384, size=      64 
[ 5111.558645] ... vmalloc OK
[ 5119.521126] goodbye!!!

slab 内存分配

mem_slab.c

c 复制代码
// mem_slab.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/vmalloc.h>

// 指定license版本
MODULE_LICENSE("GPL");
// 作者信息
MODULE_AUTHOR("Marvin");
// 模块描述
MODULE_DESCRIPTION("memmory slab");
// 提供别名
MODULE_ALIAS("memslab");

#define PRT(a, b)                                                              \
  pr_info("%-15s=%10d %10ld %8ld\n", a, b, (PAGE_SIZE * b) / 1024,             \
          (PAGE_SIZE * b) / 1024 / 1024)

static char *kbuf;
static int size = 20;
static int align = 8;
static struct kmem_cache *my_cache;
//设置初始化入口函数
static int __init mem_slab_init(void)
{
    // 创建一个kmem_cache
    my_cache = kmem_cache_create("mycache", size, align, 0, NULL);
    if (!my_cache) {
      pr_err("kmem_cache_create failed!");
      return -ENOMEM;
    }
    pr_info("kmem_cache_create OK");

    // 分配一个缓存对象
    kbuf = kmem_cache_alloc(my_cache, GFP_ATOMIC);
    if (!kbuf) {
      pr_err("create a cache object failed!");
      (void)kmem_cache_destroy(my_cache);
      return -1;
    }
    pr_info("create a cache object OK");
    return 0;
}

//设置出口函数
static void __exit mem_slab_exit(void)
{
    kmem_cache_free(my_cache, kbuf);
    pr_info("destroyed memory cache object");
    (void)kmem_cache_destroy(my_cache);
    printk(KERN_DEBUG "goodbye!!!\n");
}

//将上述定义的init()和exit()函数定义为模块入口/出口函数
module_init(mem_slab_init);
module_exit(mem_slab_exit);

// sudo insmod memslab.ko

Makefile

makefile 复制代码
ifneq ($(KERNELRELEASE),)
MODULE_NAME = memslab
$(MODULE_NAME)-objs := mem_slab.o
obj-m := $(MODULE_NAME).o
else
KERNEL_DIR = /lib/modules/`uname -r`/build
MODULEDIR := $(shell pwd)
 
.PHONY: modules
default: modules
 
modules:
	make -C $(KERNEL_DIR) M=$(MODULEDIR) modules
 
clean distclean:
	make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean
	rm -f *.o *.mod.c .*.*.cmd *.ko
	rm -rf .tmp_versions
endif

测试

bash 复制代码
$ sudo insmod memslab.ko
$ sudo rmmod memslab.ko 

dmesg 信息

复制代码
[ 5896.854844] kmem_cache_create OK
[ 5896.854851] create a cache object OK
[ 5904.684947] destroyed memory cache object
[ 5904.684950] goodbye!!!
相关推荐
小白同学_C15 小时前
Lab4-Lab: traps && MIT6.1810操作系统工程【持续更新】 _
linux·c/c++·操作系统os
今天只学一颗糖15 小时前
1、《深入理解计算机系统》--计算机系统介绍
linux·笔记·学习·系统架构
不做无法实现的梦~16 小时前
ros2实现路径规划---nav2部分
linux·stm32·嵌入式硬件·机器人·自动驾驶
默|笙18 小时前
【Linux】fd_重定向本质
linux·运维·服务器
陈苏同学19 小时前
[已解决] Solving environment: failed with repodata from current_repodata.json (python其实已经被AutoDL装好了!)
linux·python·conda
“αβ”19 小时前
网络层协议 -- ICMP协议
linux·服务器·网络·网络协议·icmp·traceroute·ping
不爱学习的老登20 小时前
Windows客户端与Linux服务器配置ssh无密码登录
linux·服务器·windows
小王C语言21 小时前
进程状态和进程优先级
linux·运维·服务器
xlp666hub21 小时前
【字符设备驱动】:从基础到实战(下)
linux·面试
弹幕教练宇宙起源1 天前
cmake文件介绍及用法
android·linux·c++