vm_area_struc 和 vm_struct

本文详细介绍了Linux内核中的vm_struct和vm_area_struct结构,前者用于内核空间的虚拟地址管理,后者则用于用户空间进程的虚拟地址管理。文章深入探讨了这两种结构的定义、初始化过程以及相关的核心函数实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

vm_area_struct 用于表示0~3G的空间中一段连续的虚拟地址空间,是给user space的process使用.
vm_struct 是kernel space 除low memory中用于表示连续的虚拟地址空间,常用于vmalloc/vfree的操作
struct vm_struct {
    struct vm_struct    *next; //表示下一个vm,所有的vm组成一个链表
    void            *addr;//虚拟地址
    unsigned long        size; //大小
    unsigned long        flags;
    struct page        **pages;//vm所映射的page
    unsigned int        nr_pages;//vm 所映射的page 的个数
    phys_addr_t        phys_addr;//对应起始的物理地址和addr相对应
    const void        *caller;//当前调用vm的指针
};
vm_struct的常用操作如下:可以看出这些都返回vm_struct
extern struct vm_struct *get_vm_area(unsigned long size, unsigned long flags);
extern struct vm_struct *get_vm_area_caller(unsigned long size,
                    unsigned long flags, const void *caller);
extern struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
                    unsigned long start, unsigned long end);
extern struct vm_struct *__get_vm_area_caller(unsigned long size,
                    unsigned long flags,
                    unsigned long start, unsigned long end,
                    const void *caller);
extern struct vm_struct *remove_vm_area(const void *addr);
extern struct vm_struct *find_vm_area(const void *addr);
struct vm_struct *get_vm_area(unsigned long size, unsigned long flags)
{
    return __get_vm_area_node(size, 1, flags, VMALLOC_START, VMALLOC_END,
                  NUMA_NO_NODE, GFP_KERNEL,
                  __builtin_return_address(0));
}
可见是从VMALLOC_START 和 VMALLOC_END 之间分配内存,也间接证实了vm_struct 是kernel space管理虚拟内存的。
static struct vm_struct *__get_vm_area_node(unsigned long size,
        unsigned long align, unsigned long flags, unsigned long start,
        unsigned long end, int node, gfp_t gfp_mask, const void *caller)
{
    struct vmap_area *va;
    struct vm_struct *area;

    BUG_ON(in_interrupt());
    size = PAGE_ALIGN(size);
    if (unlikely(!size))
        return NULL;

    if (flags & VM_IOREMAP)
        align = 1ul << clamp_t(int, get_count_order_long(size),
                       PAGE_SHIFT, IOREMAP_MAX_ORDER);

    area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);
    if (unlikely(!area))
        return NULL;

    if (!(flags & VM_NO_GUARD))
        size += PAGE_SIZE;

    va = alloc_vmap_area(size, align, start, end, node, gfp_mask);
    if (IS_ERR(va)) {
        kfree(area);
        return NULL;
    }

    setup_vmalloc_vm(area, va, flags, caller);

    return area;
}
__get_vm_area_node 的实现也很简单
首先申请一个vm_struct area的结构
area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);
然后通过alloc_vmap_area 申请已经映射过的虚拟内存,已经映射的虚拟内存用vmap_area 表示,这段虚拟内存用rb tree表示,是在开机的过程中
vmalloc_init->__insert_vmap_area来插入的
static void __insert_vmap_area(struct vmap_area *va)
{
    struct rb_node **p = &vmap_area_root.rb_node;
    struct rb_node *parent = NULL;
    struct rb_node *tmp;

    while (*p) {
        struct vmap_area *tmp_va;

        parent = *p;
        tmp_va = rb_entry(parent, struct vmap_area, rb_node);
        if (va->va_start < tmp_va->va_end)
            p = &(*p)->rb_left;
        else if (va->va_end > tmp_va->va_start)
            p = &(*p)->rb_right;
        else
            BUG();
    }

    rb_link_node(&va->rb_node, parent, p);
    rb_insert_color(&va->rb_node, &vmap_area_root);

    /* address-sort this list */
    tmp = rb_prev(&va->rb_node);
    if (tmp) {
        struct vmap_area *prev;
        prev = rb_entry(tmp, struct vmap_area, rb_node);
        list_add_rcu(&va->list, &prev->list);
    } else
        list_add_rcu(&va->list, &vmap_area_list);
}
注意这个rb tree的root是vmap_area_root.rb_node。
回到__get_vm_area_node 中通过alloc_vmap_area 申请到虚拟内存后,就调用setup_vmalloc_vm 填充vm_struct
static void setup_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va,
                  unsigned long flags, const void *caller)
{
    spin_lock(&vmap_area_lock);
    vm->flags = flags;
    vm->addr = (void *)va->va_start;
    vm->size = va->va_end - va->va_start;
    vm->caller = caller;
    va->vm = vm;
    va->flags |= VM_VM_AREA;
    spin_unlock(&vmap_area_lock);
}


而vm_area_struct 是给进程用的,常用find_vma 来找到虚拟地址所在的vm_area_struct
struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
{
    struct rb_node *rb_node;
    struct vm_area_struct *vma;

    /* Check the cache first. */
    vma = vmacache_find(mm, addr);
    if (likely(vma))
        return vma;

    rb_node = mm->mm_rb.rb_node;

    while (rb_node) {
        struct vm_area_struct *tmp;

        tmp = rb_entry(rb_node, struct vm_area_struct, vm_rb);

        if (tmp->vm_end > addr) {
            vma = tmp;
            if (tmp->vm_start <= addr)
                break;
            rb_node = rb_node->rb_left;
        } else
            rb_node = rb_node->rb_right;
    }

    if (vma)
        vmacache_update(addr, vma);
    return vma;
}
可见也是用rb tree表示,但是这个rb tree的根节点是在自身mm中保存,每个进程都有自己的mm。如果能找到就返回vm_area_struct。负责就发生page fault。
从find_vma 中可以看到查找的时候会首先从vm cache中也就是vmacache_find 中查找,最后将查找的结构更新到vmacache_update 中.

### learn_struct_EM 函数详解 #### 函数概述 `learn_struct_EM` 是用于贝叶斯网络结构学习的一种重要函数,在存在数据缺失的情况下尤为有用。该函数采用期望最大化(EM)算法来估计模型参数,同时推断最可能的网络结构[^3]。 #### 参数说明 - `data`: 输入的数据矩阵,每一列代表一个变量,每一行表示一次观测。 - `node_sizes`: 向量,指定每个节点的状态数量。 - `max_iter`: 整数,默认值为100,指定了EM算法的最大迭代次数。 - `threshold`: 浮点数,默认值为0.01,收敛阈值,当似然增加小于这个值时停止迭代。 - `init_struc`: 初始网络结构,可以为空或预定义的结构。 ```matlab [bnet, lltrace] = learn_struct_EM(data, node_sizes, 'MaxIter', max_iter, ... 'Threshold', threshold, 'InitStruc', init_struc); ``` #### 示例代码 下面是一个简单的例子展示如何使用此函数: ```matlab % 加载示例数据集 load('alarm.mat'); % alarm.mat 文件应包含 data node_sizes 变量 % 设置 EM 算法参数 max_iter = 200; threshold = 0.001; % 调用 learn_struct_EM 进行结构学习 [bnet, lltrace] = learn_struct_EM(alarm_data, node_sizes, 'MaxIter', max_iter,... 'Threshold', threshold); % 显示结果 disp(bnet); plot(lltrace); title('Log-Likelihood Trace'); xlabel('Iteration Number'); ylabel('Log Likelihood'); ``` 上述代码片段展示了如何加载数据、配置必要的输入参数以及调用 `learn_struct_EM` 来执行贝叶斯网路的学习过程,并最终绘制了对数似然变化曲线图以便观察收敛情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值