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!!!
相关推荐
HLC++9 小时前
Linux的进程间通信
android·linux·服务器
华清远见IT开放实验室10 小时前
实验室建设案例 | 石家庄科技信息职业学院嵌入式实验室——从底层硬件到系统应用,一所应用型高校的嵌入式人才培养这样落地
linux·arm开发·stm32·嵌入式硬件·高校·实验室建设
groundhappy13 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕13 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫14 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生15 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.1116 小时前
linux基础IO
linux·运维·服务器
BelongPanda18 小时前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯18 小时前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算
2301_7779983418 小时前
Linux中断机制:操作系统如何高效运行
linux·运维·服务器