Linux - 内存管理

 引用文章


一. 用户进程内存管理

1.1 进程的虚拟地址空间VMA

1.2 进程的pmap

1.3 VMA与程序的各个段以及库关系

1.4 VSS, RSS, PSS, USS的关系

1.5 page fault的几种可能性

1.6 进程内存泄漏的界定

1.7 内存泄漏的检查

二. 内核内存管理

2.1 内存分布

2.2 内存管理图

 

三. 用户空间和内核空间内存分配关系

3.1 内存总体关系图

3.2 Libc, Slab与buddy

3.3 kmalloc VS vmalloc/ioremap

四. 一个mmap的小实验

4.1 kernel driver

#include <linux/version.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
#include <asm/io.h>
#include <linux/device.h>

/* length of the two memory areas */
#define NPAGES 16

struct mmap_dev_info_st {
    /* char device */
    dev_t mmap_dev;
    struct cdev mmap_cdev;
    
    struct class *mmap_class;
    
    
    /* pointer to the vmalloc'd area - alway page aligned */
    int *vmalloc_area;

    /* pointer to the kmalloc'd area, rounded up to a page boundary */
    int *kmalloc_area;

    /* original pointer for kmalloc'd area as returned by kmalloc */
    void *kmalloc_ptr;
};

static struct mmap_dev_info_st mmap_dev_info;


static int mmap_open(struct inode *inode, struct file *filp)
{
    return 0;
}

static int mmap_release(struct inode *inode, struct file *filp)
{
    return 0;
}

/* mmap's the kmalloc'd area which is physically contiguous */
int mmap_kmem(struct file *filp, struct vm_area_struct *vma)
{
    int ret = 0;
    long length = vma->vm_end - vma->vm_start;
    int *kmalloc_area = mmap_dev_info.kmalloc_area;
    
    printk(KERN_ERR "%s: start\n", __func__);

    /* check length - do not allow larger mappings than the number of
       pages allocated */
    if (length > NPAGES * PAGE_SIZE) {
        ret = -EIO;
        printk(KERN_ERR "%s: length err.\n", __func__);
        goto err;
    }

    /* map the whole physically contiguous area in one piece */
    ret = remap_pfn_range(vma, vma->vm_start,
                    virt_to_phys((void *)kmalloc_area) >> PAGE_SHIFT,
                    length, vma->vm_page_prot);
    if (ret < 0) {
        printk(KERN_ERR "%s: ret:%d err.\n", __func__, ret);
        goto err;
    }
    
    return 0;

err:
    return ret;
}

/* mmap's the vmalloc'd area which is not physically contiguous */
int mmap_vmem(struct file *filp, struct vm_area_struct *vma)
{
    int ret = 0;
    long length = vma->vm_end - vma->vm_start;
    unsigned long start = vma->vm_start;
    char *vmalloc_area_ptr = (char *)mmap_dev_info.vmalloc_area;
    unsigned long pfn = 0;

    printk(KERN_ERR "%s: start\n", __func__);

    /* check length - do not allow larger mappings than the number of
       pages allocated */
    if (length > NPAGES * PAGE_SIZE) {
        ret = -EIO;
        printk(KERN_ERR "%s: length err.\n", __func__);
        goto err;
    }

    /* loop over all pages, map it page individually */
    while (length > 0) {
        pfn = vmalloc_to_pfn(vmalloc_area_ptr);
        ret = remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED);
        if (ret < 0) {
            printk(KERN_ERR "%s: remap_pfn_range err.\n", __func__);
            goto err;
        }

        start += PAGE_SIZE;
        vmalloc_area_ptr += PAGE_SIZE;
        length -= PAGE_SIZE;
    }

    return 0;

err:
    return ret;
}

/* character device mmap method */
static int mmap_mmap(struct file *filp, struct vm_area_struct *vma)
{
    int ret = 0;

    if (vma->vm_pgoff == 0) {
        /* at offset 0 we map the vmalloc'd area */
        ret =  mmap_vmem(filp, vma);  
    } else if (vma->vm_pgoff == NPAGES) {
        /* at offset NPAGES we map the kmalloc'd area */
        ret = mmap_kmem(filp, vma);
    } else {
        /* at any other offset we return an error */
        ret = -EIO;
    }
    
    return ret;
}

static struct file_operations mmap_fops = {
    .open = mmap_open,
    .release = mmap_release,
    .mmap = mmap_mmap,
    .owner = THIS_MODULE,
};

static int __init my_mmap_init(void)
{
    int ret = 0;
    int i = 0;

    printk(KERN_ERR "%s: start\n", __func__);
    
    /* allocate a memory area with kmalloc. Will be rounded up to a page boundary */
    if ((mmap_dev_info.kmalloc_ptr = kmalloc((NPAGES + 2) * PAGE_SIZE, GFP_KERNEL)) == NULL) {
        ret = -ENOMEM;
        goto out;
    }

    /* round it up to the page bondary,即找到页对齐的地址 */
    mmap_dev_info.kmalloc_area = (int *)PAGE_ALIGN((unsigned long)mmap_dev_info.kmalloc_ptr);

    /* allocate a memory area with vmalloc. */
    if ((mmap_dev_info.vmalloc_area = (int *)vmalloc(NPAGES * PAGE_SIZE)) == NULL) {
        ret = -ENOMEM;
        goto out_kfree;
    }

    /* get the major number of the character device */
    if ((ret = alloc_chrdev_region(&mmap_dev_info.mmap_dev, 0, 1, "mmap")) < 0) {
        printk(KERN_ERR "could not allocate major number for mmap\n");
        goto out_vfree;
    }

    /* initialize the device structure and register the device with the kernel */
    cdev_init(&mmap_dev_info.mmap_cdev, &mmap_fops);
    if ((ret = cdev_add(&mmap_dev_info.mmap_cdev, mmap_dev_info.mmap_dev, 1)) < 0) {
        printk(KERN_ERR "could not allocate chrdev for mmap\n");
        goto out_unalloc_region;
    }

    /* mark the pages reserved,即让申请的memory不被交换到磁盘。*/
    for (i = 0; i < NPAGES * PAGE_SIZE; i += PAGE_SIZE) {
        //mark_page_reserved(vmalloc_to_page((void *)(((unsigned long)(mmap_dev_info.vmalloc_area)) + i)));
        //mark_page_reserved(virt_to_page(((unsigned long)(mmap_dev_info.kmalloc_area)) + i));
        
        SetPageReserved(vmalloc_to_page((void *)(((unsigned long)(mmap_dev_info.vmalloc_area)) + i)));
        SetPageReserved(virt_to_page(((unsigned long)(mmap_dev_info.kmalloc_area)) + i));
    }

    /* store a pattern in the memory - the test application will check for it */
    for (i = 0; i < (NPAGES * PAGE_SIZE / sizeof(int)); i += 2) {
        mmap_dev_info.vmalloc_area[i] = (0xaffe << 16) + i;
        mmap_dev_info.vmalloc_area[i + 1] = (0xbeef << 16) + i;
        mmap_dev_info.kmalloc_area[i] = (0xdead << 16) + i;
        mmap_dev_info.kmalloc_area[i + 1] = (0xbeef << 16) + i;
    }

    /* create class */
    mmap_dev_info.mmap_class = class_create(THIS_MODULE, "mmap_class");
    if (IS_ERR(mmap_dev_info.mmap_class)) {
        printk(KERN_ERR "class_create failed.\n");
        ret = -1;
        goto out_unalloc_region;
    }
    
    /* create device */
    device_create(mmap_dev_info.mmap_class, NULL, mmap_dev_info.mmap_dev, NULL, "mmap_dev");

    printk(KERN_ERR "%s: end\n", __func__);

    return ret;
    
out_unalloc_region:
    unregister_chrdev_region(mmap_dev_info.mmap_dev, 1);

out_vfree:
    vfree(mmap_dev_info.vmalloc_area);
    mmap_dev_info.vmalloc_area = NULL;

out_kfree:
    kfree(mmap_dev_info.kmalloc_ptr);
    mmap_dev_info.kmalloc_ptr = NULL;

out:
    return ret;
}

static void __exit my_mmap_exit(void)
{
    int i = 0;

    /* unregister device */
    device_destroy(mmap_dev_info.mmap_class, mmap_dev_info.mmap_dev);

    /* remove the character deivce */
    cdev_del(&(mmap_dev_info.mmap_cdev));
    
    class_destroy(mmap_dev_info.mmap_class);
    unregister_chrdev_region(mmap_dev_info.mmap_dev, 1);

    /* unreserve the pages */
    for (i = 0; i < NPAGES * PAGE_SIZE; i += PAGE_SIZE) {
        //free_reserved_page(vmalloc_to_page((void *)(((unsigned long)(mmap_dev_info.vmalloc_area)) + i)));
        //free_reserved_page(virt_to_page(((unsigned long)(mmap_dev_info.kmalloc_area)) + i));
        ClearPageReserved(vmalloc_to_page((void *)(((unsigned long)(mmap_dev_info.vmalloc_area)) + i)));
        ClearPageReserved(virt_to_page(((unsigned long)(mmap_dev_info.kmalloc_area)) + i));
        
    }
    
    /* free the memory areas */
    if (mmap_dev_info.vmalloc_area) {
        vfree(mmap_dev_info.vmalloc_area);
        mmap_dev_info.vmalloc_area = NULL;
    }
    
    if (mmap_dev_info.kmalloc_ptr) {
        kfree(mmap_dev_info.kmalloc_ptr);
        mmap_dev_info.kmalloc_ptr = NULL;
    }
}

module_init(my_mmap_init);
module_exit(my_mmap_exit);

MODULE_DESCRIPTION("mmap demo driver");
MODULE_AUTHOR("vector");
MODULE_LICENSE("GPL");
ifneq ($(KERNELRELEASE),)                                                                                                                                                                               
    obj-m := map_driver.o
else
    PWD  := $(shell pwd)
KDIR := /lib/modules/$(shell uname -r)/build

all:
    $(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
    rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.order Module.symvers
endif

4.2 userspace test app

#include <stdio.h>                                                                                                                                                                                      
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>

#define LEN (16*4096)

int main(int argc, void **argv)
{
    int i = 0;
    int fd;
    unsigned int *vadr;

    fd = open("/dev/mmap_dev", O_RDWR);
    if (fd < 0) {
        perror("open");
        return -1;
    }

    vadr = mmap(0, LEN, PROT_READ, MAP_SHARED, fd, 0);

    if (vadr == MAP_FAILED) {
        perror("mmap");
        return -1;
    }

    printf("------------------------------------------------");
    for (i = 0; i < (LEN / 4); i++) {
        printf("0x%08x,", *(vadr + i));
        if ((i % 10) == 0)
            printf("\n");
    }

    close(fd);

    return(0);
}

4.3 测试结果

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值