Linux驱动学习——内存管理
环境:
Linux arch-dev 6.18.9-arch1-2 #1 SMP PREEMPT_DYNAMIC Mon, 09 Feb 2026 17:16:33 +0000 x86_64 GNU/Linux
获取系统的物理内存信息
Linux 内核对每个物理页面都采用struct page数据结构来描述,内核为每一个物理页面都分配了这样一个struct page数据结构,并且存储到一个全局的数组mem_map[]中。它们之间一一映射,数组的第i个元素指向页帧号为i的物理页面的struct page数据结构。 基于此,我们可以编写一个内核模块,通过遍历这个mem_map[]`数组来统计当前系统有多少个空闲页面、保留页面、swapcache页面、slab页面、脏页面、活跃页面、正在回写的页面等。
代码
mem_info.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 folio *folio;
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++;
folio = pfn_folio(pfn);
// page_count == 0 is a free page
if (!folio_ref_count(folio)) {
free ++;
continue;
}
if (folio_zone(folio)) {
locked++;
}
if (PageReserved(&folio->page)) {
reserved++;
}
if (folio_test_swapbacked(folio)) {
swapcache++;
}
if (folio_test_referenced(folio)) {
referenced++;
}
if (PageSlab(&folio->page)) {
slab++;
}
if (PagePrivate(&folio->page)) {
private++;
}
if (PageUptodate(&folio->page)) {
uptodate++;
}
if(PageDirty(&folio->page)) {
dirty++;
}
if (folio_test_active(folio)) {
active++;
}
if (PageWriteback(&folio->page)) {
writeback++;
}
if (folio_test_mappedtodisk(folio)) {
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
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
测试
$ sudo insmod showmeminfo.ko
dmesg 输出
[31733.735244] showmeminfo: loading out-of-tree module taints kernel.
[31733.735249] showmeminfo: module verification failed: signature and/or required key missing - tainting kernel
[31733.745763]
Examining 1043558 pages(num_phys_pages) = (efault) MB
[31733.745767] Pages with valid PFN's = 524287, = 2047 MB
[31733.745767]
Pages KB MB
[31733.745768] free = 119434 477736 466
[31733.745769] locked = 404853 1619412 1581
[31733.745770] reserved = 21587 86348 84
[31733.745771] swapcache = 133955 535820 523
[31733.745772] referenced = 209721 838884 819
[31733.745773] slab = 0 0 0
[31733.745774] private = 46758 187032 182
[31733.745774] uptodate = 375524 1502096 1466
[31733.745775] dirty = 35479 141916 138
[31733.745776] active = 0 0 0
[31733.745776] writeback = 0 0 0
[31733.745777] mappedtodisk = 274058 1096232 1070
分配内存
在Linux内核中,可以通过alloc_page分配一个物理页面,然后输出该物理页面的物理地址,并输出该物理页面在内核空间的虚拟地址。
可以通过__get_free_pages来分配物理页面,也可以通过kmalloc直接分配物理内存。
代码
mem_alloc.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 < NR_PAGE_ORDERS; 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 < NR_PAGE_ORDERS; 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
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
测试
$ 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
// 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
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
测试
$ 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!!!
VMA
编写一个内存模块,遍历一个用户进程中所有的VMA.
mem_vma.c
// mem_slab.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/mm.h>
#include <linux/mm_types.h>
// 指定license版本
MODULE_LICENSE("GPL");
// 作者信息
MODULE_AUTHOR("Marvin");
// 模块描述
MODULE_DESCRIPTION("memmory vma");
// 提供别名
MODULE_ALIAS("memvma");
static int pid;
module_param(pid, int, S_IRUGO);
static void printit(struct task_struct *task) {
int j = 0;
unsigned long start, end, length;
struct mm_struct *mm = task->mm;
struct vm_area_struct *vma;
VMA_ITERATOR(vmi, mm, 0); // 从地址0开始
if (!mm)
return;
// 需要持有mm锁
mmap_read_lock(mm);
pr_info("vmas: vma start end length\n");
// 使用VMA迭代器(Linux 6.x推荐方式)
for_each_vma(vmi, vma) {
// vma_iter_addr(vmi) 获取当前地址
j++;
start = vma->vm_start;
end = vma->vm_end;
length = end - start;
pr_info("vma %d: %p 0x%08lx 0x%08lx 0x%08lx\n", j, vma, start, end, length);
// printk("VMA at %px: 0x%lx - 0x%lx\n",
// vma, vma->vm_start, vma->vm_end);
}
mmap_read_unlock(mm);
}
// 设置初始化入口函数
static int __init mem_slab_init(void) {
struct task_struct *task;
if (pid == 0) {
pr_info("pid is 0, use current process\n");
task = current;
pid = task->pid;
pr_info("current process pid is %d\n", pid);
} else {
task = pid_task(find_vpid(pid), PIDTYPE_PID);
if (task == NULL) {
pr_info("cannot find task with pid %d\n", pid);
return -ESRCH;
}
pr_info("find task with pid %d, command = %s\n", pid, task->comm);
}
printit(task);
return 0;
}
// 设置出口函数
static void __exit mem_slab_exit(void) { pr_info("goodbye!!!\n"); }
// 将上述定义的init()和exit()函数定义为模块入口/出口函数
module_init(mem_slab_init);
module_exit(mem_slab_exit);
// sudo insmod memslab.ko
Makefile
ifneq ($(KERNELRELEASE),)
MODULE_NAME = memvma
$(MODULE_NAME)-objs := mem_vma.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
测试
$ sudo insmod memvma.ko
$ sudo rmmod memvma.ko
dmesg 信息
[ 1892.957910] pid is 0, use current process
[ 1892.957921] current process pid is 4538
[ 1892.957921] vmas: vma start end length
[ 1892.957922] vma 1: 00000000969d1748 0x56297e57e000 0x56297e581000 0x00003000
[ 1892.957924] vma 2: 000000003ac0dac5 0x56297e581000 0x56297e59c000 0x0001b000
[ 1892.957925] vma 3: 0000000098dc766f 0x56297e59c000 0x56297e5a4000 0x00008000
[ 1892.957926] vma 4: 000000001f6755c6 0x56297e5a4000 0x56297e5a6000 0x00002000
[ 1892.957926] vma 5: 000000006687b8c1 0x56297e5a6000 0x56297e5a7000 0x00001000
[ 1892.957927] vma 6: 0000000048871c79 0x562994cc3000 0x562994ce4000 0x00021000
[ 1892.957928] vma 7: 00000000cbbeb8ad 0x7fc88fbe2000 0x7fc88fc06000 0x00024000
[ 1892.957929] vma 8: 00000000329b2c33 0x7fc88fc06000 0x7fc88fd77000 0x00171000
[ 1892.957929] vma 9: 000000002c0e897d 0x7fc88fd77000 0x7fc88fdc5000 0x0004e000
[ 1892.957930] vma 10: 0000000033af3caf 0x7fc88fdc5000 0x7fc88fdc9000 0x00004000
[ 1892.957931] vma 11: 00000000df370602 0x7fc88fdc9000 0x7fc88fdcb000 0x00002000
[ 1892.957932] vma 12: 00000000d5b1e752 0x7fc88fdcb000 0x7fc88fdd3000 0x00008000
[ 1892.957933] vma 13: 000000006cb15d1f 0x7fc88fdd3000 0x7fc88fdd7000 0x00004000
[ 1892.957933] vma 14: 00000000f74787ba 0x7fc88fdd7000 0x7fc88fdfa000 0x00023000
[ 1892.957934] vma 15: 000000002c3c3958 0x7fc88fdfa000 0x7fc88fdfe000 0x00004000
[ 1892.957935] vma 16: 000000000ab00e5c 0x7fc88fdfe000 0x7fc88fdff000 0x00001000
[ 1892.957936] vma 17: 00000000d41c5c7f 0x7fc88fdff000 0x7fc88fe00000 0x00001000
[ 1892.957936] vma 18: 000000004eab2890 0x7fc88fe00000 0x7fc88fe52000 0x00052000
[ 1892.957937] vma 19: 000000003ece5d0f 0x7fc88fe52000 0x7fc8901de000 0x0038c000
[ 1892.957938] vma 20: 00000000ec38eb56 0x7fc8901de000 0x7fc8902f1000 0x00113000
[ 1892.957938] vma 21: 00000000ecec6c79 0x7fc8902f1000 0x7fc89036f000 0x0007e000
[ 1892.957939] vma 22: 00000000df9c77a9 0x7fc89036f000 0x7fc890372000 0x00003000
[ 1892.957940] vma 23: 0000000030ab3df1 0x7fc890372000 0x7fc890375000 0x00003000
[ 1892.957941] vma 24: 00000000c6ae42b7 0x7fc890377000 0x7fc89037c000 0x00005000
[ 1892.957941] vma 25: 000000003a32a804 0x7fc89037c000 0x7fc89037f000 0x00003000
[ 1892.957942] vma 26: 000000009cfcc92b 0x7fc89037f000 0x7fc89038f000 0x00010000
[ 1892.957943] vma 27: 000000003813c7a0 0x7fc89038f000 0x7fc890395000 0x00006000
[ 1892.957943] vma 28: 00000000228bdc99 0x7fc890395000 0x7fc890396000 0x00001000
[ 1892.957944] vma 29: 000000002bbf7407 0x7fc890396000 0x7fc890397000 0x00001000
[ 1892.957945] vma 30: 00000000b72e7ffe 0x7fc890397000 0x7fc89039b000 0x00004000
[ 1892.957946] vma 31: 00000000d084fab3 0x7fc89039b000 0x7fc8903bf000 0x00024000
[ 1892.957946] vma 32: 0000000060f1a54d 0x7fc8903bf000 0x7fc8903ca000 0x0000b000
[ 1892.957947] vma 33: 00000000827a2d87 0x7fc8903ca000 0x7fc8903cb000 0x00001000
[ 1892.957948] vma 34: 0000000098f924bf 0x7fc8903cb000 0x7fc8903cc000 0x00001000
[ 1892.957948] vma 35: 00000000a7e3d6e2 0x7fc8903cc000 0x7fc8903d8000 0x0000c000
[ 1892.957949] vma 36: 00000000fb3e0ce6 0x7fc8903d8000 0x7fc89049f000 0x000c7000
[ 1892.957950] vma 37: 00000000f174d217 0x7fc89049f000 0x7fc8904b0000 0x00011000
[ 1892.957951] vma 38: 00000000f6d3bcfb 0x7fc8904b0000 0x7fc8904b1000 0x00001000
[ 1892.957951] vma 39: 0000000062ec0d9d 0x7fc8904b1000 0x7fc8904b2000 0x00001000
[ 1892.957952] vma 40: 000000002529164a 0x7fc8904b2000 0x7fc8904b4000 0x00002000
[ 1892.957953] vma 41: 000000001a3d0012 0x7fc8904c4000 0x7fc8904c8000 0x00004000
[ 1892.957953] vma 42: 0000000043af3ebf 0x7fc8904c8000 0x7fc8904ca000 0x00002000
[ 1892.957954] vma 43: 00000000793b21f4 0x7fc8904ca000 0x7fc8904cc000 0x00002000
[ 1892.957955] vma 44: 000000007cd82d8c 0x7fc8904cc000 0x7fc8904cd000 0x00001000
[ 1892.957956] vma 45: 00000000e0951d2b 0x7fc8904cd000 0x7fc8904f7000 0x0002a000
[ 1892.957956] vma 46: 000000004b38866f 0x7fc8904f7000 0x7fc890502000 0x0000b000
[ 1892.957957] vma 47: 00000000fd5a9d96 0x7fc890502000 0x7fc890504000 0x00002000
[ 1892.957958] vma 48: 000000007236734e 0x7fc890504000 0x7fc890505000 0x00001000
[ 1892.957958] vma 49: 00000000d4317ef2 0x7fc890505000 0x7fc890506000 0x00001000
[ 1892.957959] vma 50: 000000000d369998 0x7ffedab77000 0x7ffedab98000 0x00021000
[ 1894.636183] goodbye!!!
注意
内存管理的内核接口从4.x到5.x再到6.x修改了很多,一些传统的API都已经被废弃了。

浙公网安备 33010602011771号