深入linux内核架构--内存管理

news2024/11/17 7:21:41

【推荐阅读】

代码大佬的【Linux内核开发笔记】分享,前人栽树后人乘凉!

一文了解Linux内核的Oops

一篇长文叙述Linux内核虚拟地址空间的基本概括

路由选择协议——RIP协议

深入理解Intel CPU体系结构【值得收藏!】

内存体系结构

1. UMA VS NUMA

有两种类型的计算机,分别以不同的方式管理物理内存:

1)UMA(uniform memory access)计算机,将内存以连续的方式组织起来。SMP系统中每个处理器都是访问同一块内存。

2)NUMA(non-uniform memory access)计算机,总是多处理器计算机,系统各个CPU有各自本地的内存访问,各个处理器之间的总线是连着的,可以访问其他CPU,但是要慢一些。

UMA总线逻辑更简单,但是访问速度要慢,带宽低;NUMA总线逻辑更复杂,但是访问更高效,带宽高,也更容易伸缩。UMA更适合分时系统,NUMA更适合实时系统,UMA并行能力特别差。下面我们讲解的主要是NUMA系统,因为弄懂了NUMA系统的内存管理,UMA就特别容易理解了。

2. 内核空间-内存组织

  1. 内存划分为节点(pg_data_t),每个节点关联到系统中的一个处理器

每个节点有划分成内存域(zone),目前内存域分为四种:(三个内核内存域,一个用户内存域)ZONE_DMA/ZONE_DMA32,ZONE_NORMAL,ZONE_HIGHMEM。通过划分内存域可以更好的管理,针对不同的场景进行更好的优化。

· ZONE_DMA标记适合DMA的内存域,在IA-32计算机上,一般的现在是16MB,该区域供I/O设备直接访问,不需要通过MMU管理,连续分配,具有更高的性能。

· ZONE_DMA32,标记了使用32位地址可寻址、适合DMA的内存域,显然只有64位系统上,才会有该内存域。

· ZONE_NORMAL,可以直接映射到内核段的普通内存域,这是所有体系机构上保证都会存在的唯一内存域,在IA-32系统上,该域可访问的最大内存不超过896MiB,超过该值的内存只能能通过高端内存寻址访问ZONE_HIGHMEM中的内存。

· ZONE_HIGHMEM,超出了内核段的物理内存。只有在可用物理内存多余可映射的内核内存时,才会访问该域,显然一般只有32位系统上才会有可能有该区域。通过kmap及kunmap将该域内存映射到内核虚拟地址空间。

· ZONE_MOVEABLE,这个区域主要是给用户空间分配使用。

前三个zone主要为内核所用到,最后一个主要被用户空间用到,内核空间内存域具体划分见下图:

内核空间内存域zone划分

由于内核核心不依赖于高端内存,所以一般优先分配高端内存,高端内存分配完毕后才会分配普通内存。

内存组织结构见下图:

NUMA系统中的内存划分

数据结构如下:

 enum zone_type {
#ifdef CONFIG_ZONE_DMA
    ZONE_DMA,
#endif
#ifdef CONFIG_ZONE_DMA32
    ZONE_DMA32,
#endif
    ZONE_NORMAL,
#ifdef CONFIG_HIGHMEM
    ZONE_HIGHMEM,
#endif
    ZONE_MOVABLE,
#ifdef CONFIG_ZONE_DEVICE
    ZONE_DEVICE,
#endif
    __MAX_NR_ZONES
};
typedef struct pglist_data {
    struct zone node_zones[MAX_NR_ZONES]; /*内存域*/
    struct zonelist node_zonelists[MAX_ZONELISTS]; /* 备域(NUMA备用节点) */
    int nr_zones; /* 内存域数目 */
#ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */
    struct page *node_mem_map;  /* 所有内存域中的页 */
#ifdef CONFIG_PAGE_EXTENSION
    struct page_ext *node_page_ext; /* 内存页的更多描述信息*/
#endif
#endif
    unsigned long node_start_pfn; /* 节点起始页帧编号(系统中的所有节点的页帧编号唯一) */
    unsigned long node_present_pages; /* total number of physical pages */
    unsigned long node_spanned_pages; /* total size of physical page
                         range, including holes */
    int node_id;
    wait_queue_head_t kswapd_wait; /* kswapd的等待队列*/
    wait_queue_head_t pfmemalloc_wait;
    struct task_struct *kswapd; /* Protected by
                       mem_hotplug_begin/end() */
    int kswapd_order;
    enum zone_type kswapd_classzone_idx;
 
    int kswapd_failures;        /* Number of 'reclaimed == 0' runs */
...
} pg_data_t;
 
struct zone {
    /* zone watermarks, access with *_wmark_pages(zone) macros */
    unsigned long _watermark[NR_WMARK]; /* min, low, high,不同内存水位代表内存有不同的压力,后续会详细介绍kswapd*/
    unsigned long watermark_boost;
    unsigned long nr_reserved_highatomic;
    long lowmem_reserve[MAX_NR_ZONES]; /* 指定数量的内存页,用于无论如何都不能失败的关键性内存分配,不同的域有不同的计算方式*/
#ifdef CONFIG_NUMA
    int node; /* 代表第n个节点*/
#endif
    struct pglist_data  *zone_pgdat; /* 所在的节点 */
    struct per_cpu_pageset __percpu *pageset; /* 热/冷页帧列表,详情见下面的描述及slab分配器 */
#ifndef CONFIG_SPARSEMEM
    ... /* 高级新特性,后续研究完了再补一下*/
#endif
        ...
#ifdef CONFIG_MEMORY_ISOLATION
    ... /* 高级新特性,后续研究完了再补一下*/
#endif
#ifdef CONFIG_MEMORY_HOTPLUG
    ... /* 高级新特性,后续研究完了再补一下*/
#endif
    int initialized;
    /* Write-intensive fields used from the page allocator */
    ZONE_PADDING(_pad1_) /* CPU高速缓存行,缓存填充,确保自旋锁处于自身的缓存行中*/
    /* free areas of different sizes */
    struct free_area    free_area[MAX_ORDER]; /* 伙伴系统,负责实际页帧的分配 */
    /* zone flags, see below */
    unsigned long       flags;
    /* Primarily protects free_area */
    spinlock_t      lock;
    /* Write-intensive fields used by compaction and vmstats. */
    ZONE_PADDING(_pad2_)
    unsigned long percpu_drift_mark;
        ...
    ZONE_PADDING(_pad3_)
    /* Zone statistics */
    atomic_long_t       vm_stat[NR_VM_ZONE_STAT_ITEMS];
    atomic_long_t       vm_numa_stat[NR_VM_NUMA_STAT_ITEMS];
} ____cacheline_internodealigned_in_smp /*最优高速缓存行对齐*/;

2.1 冷热页

冷热页主要是针对于CPU缓存的,热页就是已经加载入CPU缓存,相反冷页则说明页不在CPU高速缓存中,关于CPU高速缓存将在slab内存管理中统一描述。

struct per_cpu_pageset {
    struct per_cpu_pages pcp;
        ...
};
struct per_cpu_pages {
    int count;  /* number of pages in the list */
    int high;       /* high watermark, emptying needed */
    int batch;  /* chunk size for buddy add/remove */
    /* Lists of pages, one per migrate type stored on the pcp-lists */
    struct list_head lists[MIGRATE_PCPTYPES];
};

2.2 页帧

页帧代表系统物理内存映射的最小单位,对应于struct page,IA-32系统中标准页的长度为4KiB。页帧被伙伴系统管理着,所以当一个或多个page被分配出来时,由伙伴系统管理page对应的元信息,page中的数据可能是匿名映射首页、slab缓存首页、伙伴页或者页表页。
具体数据结构如下:

 struct page {
    unsigned long flags;        /* Atomic flags, some possibly
                     * updated asynchronously */
    /*
     * Five words (20/40 bytes) are available in this union.
     * WARNING: bit 0 of the first word is used for PageTail(). That
     * means the other users of this union MUST NOT use the bit to
     * avoid collision and false-positive PageTail().
     */
    union {
        struct {    /* Page cache and anonymous pages */
            /**
             * @lru: Pageout list, eg. active_list protected by
             * zone_lru_lock.  Sometimes used as a generic list
             * by the page owner.
             */
            struct list_head lru;
            /* See page-flags.h for PAGE_MAPPING_FLAGS */
            struct address_space *mapping;
            pgoff_t index;      /* Our offset within mapping. */
            /**
             * @private: Mapping-private opaque data.
             * Usually used for buffer_heads if PagePrivate.
             * Used for swp_entry_t if PageSwapCache.
             * Indicates order in the buddy system if PageBuddy.
             */
            unsigned long private;
        };
        struct {    /* slab, slob and slub */
            union {
                struct list_head slab_list; /* uses lru */
                struct {    /* Partial pages */
                    struct page *next;
#ifdef CONFIG_64BIT
                    int pages;  /* Nr of pages left */
                    int pobjects;   /* Approximate count */
#else
                    short int pages;
                    short int pobjects;
#endif
                };
            };
            struct kmem_cache *slab_cache; /* not slob */
            /* Double-word boundary */
            void *freelist;     /* first free object */
            union {
                void *s_mem;    /* slab: first object */
                unsigned long counters;     /* SLUB */
                struct {            /* SLUB */
                    unsigned inuse:16;
                    unsigned objects:15;
                    unsigned frozen:1;
                };
            };
        };
        struct {    /* Tail pages of compound page */
            unsigned long compound_head;    /* Bit zero is set */
 
            /* First tail page only */
            unsigned char compound_dtor;
            unsigned char compound_order;
            atomic_t compound_mapcount;
        };
        struct {    /* Second tail page of compound page */
            unsigned long _compound_pad_1;  /* compound_head */
            unsigned long _compound_pad_2;
            struct list_head deferred_list;
        };
        struct {    /* Page table pages */
            unsigned long _pt_pad_1;    /* compound_head */
            pgtable_t pmd_huge_pte; /* protected by page->ptl */
            unsigned long _pt_pad_2;    /* mapping */
            union {
                struct mm_struct *pt_mm; /* x86 pgds only */
                atomic_t pt_frag_refcount; /* powerpc */
            };
#if ALLOC_SPLIT_PTLOCKS
            spinlock_t *ptl;
#else
            spinlock_t ptl;
#endif
        };
        struct {    /* ZONE_DEVICE pages */
            /** @pgmap: Points to the hosting device page map. */
            struct dev_pagemap *pgmap;
            unsigned long hmm_data;
            unsigned long _zd_pad_1;    /* uses mapping */
        };
 
        /** @rcu_head: You can use this to free a page by RCU. */
        struct rcu_head rcu_head;
    };
 
    union {     /* This union is 4 bytes in size. */
        /*
         * If the page can be mapped to userspace, encodes the number
         * of times this page is referenced by a page table.
         */
        atomic_t _mapcount;
 
        /*
         * If the page is neither PageSlab nor mappable to userspace,
         * the value stored here may help determine what this page
         * is used for.  See page-flags.h for a list of page types
         * which are currently stored here.
         */
        unsigned int page_type;
 
        unsigned int active;        /* SLAB */
        int units;          /* SLOB */
    };
 
    /* Usage count. *DO NOT USE DIRECTLY*. See page_ref.h */
    atomic_t _refcount;
#ifdef CONFIG_MEMCG
    struct mem_cgroup *mem_cgroup;
#endif
    /*
     * On machines where all RAM is mapped into kernel address space,
     * we can simply calculate the virtual address. On machines with
     * highmem some memory is mapped into kernel virtual memory
     * dynamically, so we need a place to store that address.
     * Note that this field could be 16 bits on x86 ... ;)
     *
     * Architectures with slow multiplication can define
     * WANT_PAGE_VIRTUAL in asm/page.h
     */
#if defined(WANT_PAGE_VIRTUAL)
    void *virtual;          /* Kernel virtual address (NULL if
                       not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */
 
#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
    int _last_cpupid;
#endif
} _struct_page_alignment;

2.3 页表

页表建立了虚拟地址到物理地址的映射关联。其通过radix tree来快速查找虚拟地址对应的物理地址,内核空间的映射基本是一致性映射。内核内存管理总是假定使用四级页表,但有些体系结构并不是采用四级页表,比如IA-32系统,只有2级页表,所以体系结构需要对于对于第三、第四级页表进行模拟处理。这里面比较复杂,先只考虑四级页表的情况。

内存地址分解

分解虚拟内存地址

地址空间包括5个部分,前四个部分用于选择页,最后一个部分用于选择页内位置,BITS_PER_LONG用于表示地址空间长度,PGD:全局目录项;PUD:上层目录项;PMD:中间页目录项;PTE:直接页表项。

3. 初始化内存管理

内核内存初始化

setup_arch:初始化自举分配器;
setup_per_cpu_areas:在SMP系统中为每个CPU分配一些PER_CPU的静态宏变量的副本;
build_all_zonelists:构建各个节点和内存域,在NUMA计算机中,还会建立节点的备用节点;
mem_init: 特定于体系结构,将内存分配器从bootmem迁移到真实的内存分配函数。
setup_per_cpu_pageset:初始化高速缓存及slab分配器。
这里需要特殊说明的就是build_all_zonelists中的备用节点的建立,在UMA中will do nothing,但是在NUMA会建立起相关zonelist结构
arch_call_rest_init:做一些扫尾工作,其中最重要的就是会初始化各个pages并分配到伙伴系统中,具体到在伙伴系统相关知识中详细介绍。

typedef struct pglist_data {
        ...
    struct zonelist node_zonelists[MAX_ZONELISTS];
        ...
}
struct zonelist {
    struct zoneref _zonerefs[MAX_ZONES_PER_ZONELIST + 1];
};
struct zoneref {
    struct zone *zone;  /* Pointer to actual zone */
    int zone_idx;       /* zone_idx(zoneref->zone) */
};
void __ref build_all_zonelists(pg_data_t *pgdat)
{
    if (system_state == SYSTEM_BOOTING) {
        build_all_zonelists_init(); /* __build_all_zonelists(NULL); */
    } else {
        __build_all_zonelists(pgdat);
        /* cpuset refresh routine should be here */
    }
    ...
}
static void __build_all_zonelists(void *data)
{
    ...
    if (self && !node_online(self->node_id)) { / * non-booting*/
        build_zonelists(self);
    } else {  / * when booting, init all nodes*/
        for_each_online_node(nid) {
            pg_data_t *pgdat = NODE_DATA(nid); /* 根据node_id 查询pg_data_t*/
            build_zonelists(pgdat);
        }
                ...
    }
        ...
}
static void build_zonelists(pg_data_t *pgdat)
{
    static int node_order[MAX_NUMNODES]; /* 备用节点,按优先级排序,优先级的计算与节点的到当前节点的距离,节点的负载相关; 具体见find_next_best_node */
    int node, load, nr_nodes = 0;
    nodemask_t used_mask;
    int local_node, prev_node;
 
    /* NUMA-aware ordering of nodes */
    local_node = pgdat->node_id;
    load = nr_online_nodes; /* 可用的内存节点数 */
    prev_node = local_node;
    nodes_clear(used_mask);
 
    memset(node_order, 0, sizeof(node_order));
    while ((node = find_next_best_node(local_node, &used_mask)) >= 0) {
        /*
         * We don't want to pressure a particular node.
         * So adding penalty to the first node in same
         * distance group to make it round-robin.
         */
        if (node_distance(local_node, node) !=
            node_distance(local_node, prev_node))
            node_load[node] = load;
 
        node_order[nr_nodes++] = node;
        prev_node = node;
        load--;
    }
    build_zonelists_in_node_order(pgdat, node_order, nr_nodes);
    build_thisnode_zonelists(pgdat);
}
 
static void build_zonelists_in_node_order(pg_data_t *pgdat, int *node_order,
        unsigned nr_nodes)
{
    struct zoneref *zonerefs;
    int i;
    zonerefs = pgdat->node_zonelists[ZONELIST_FALLBACK]._zonerefs;
    for (i = 0; i < nr_nodes; i++) {
        int nr_zones;
        pg_data_t *node = NODE_DATA(node_order[i]);
        nr_zones = build_zonerefs_node(node, zonerefs); // 分配了 nr_zones个zone,zonerefs需要向后移nr_zones
        zonerefs += nr_zones;
    }
    zonerefs->zone = NULL;
    zonerefs->zone_idx = 0;
}
static int build_zonerefs_node(pg_data_t *pgdat, struct zoneref *zonerefs)
{
    struct zone *zone;
    enum zone_type zone_type = MAX_NR_ZONES; // 内存域种类
    int nr_zones = 0;
    do {
        zone_type--; // 内存域分配由廉价到昂贵
        zone = pgdat->node_zones + zone_type;
        if (managed_zone(zone)) { // 确保zone存在
            zoneref_set_zone(zone, &zonerefs[nr_zones++]);
            check_highest_zone(zone_type); // 检查并设置非moveable的最高的zone
        }
    } while (zone_type);
    return nr_zones;
}

假设一个有四个节点的NUMA计算机其最终备用域的分配可能类似于下图:

连续填充备用域示意图

linux内核起始段物理内存布局

linux内核进程起始段物理内存布局

该图给出了内核内存的前几兆字节,这些字节加载了内核进程的代码段,只读数据段,初始化数据段。第一个页帧主要供BIOS使用,后续的640Kib也基本不使用,用于映射各种ROM(系统BIOS和显卡ROM)。
在IA-32系统中一般是从0x100000开始加载内核代码。

内存初始化流程图

该图给出了系统加载内核内存管理模块的流程。
paging_init负责初始化内核页表并启用内存分页;
最终分配完后的内核内存布局如图:

内核内存地址空间
内核的普通内存(normalmem)大部分内存是直接直接映射的。前面说到内核地址只能通过直接映射使用前896MiB的数据,后续的128M被叫做高端内存(highmem),而高端内存有三种访问方式,vmalloc:可以在这个区域分配不连续的内存;持久映射:用于将高端内存中的非持久页映射到内核中;固定映射:与固定的内核物理地址关联,但具体关联的页帧可以自由选择。

虚拟地址和物理地址的管理

内核初始化完成后,物理内存管理的管理基本是由伙伴系统承担,伙伴系统以一种非常简单而高效的方式伴随了linux40多年,结合了优秀内存分配器的特点:速度与效率。而内核还为内核提供了非连续分配内存及管理缓存及小额内存分配的slab,slub及slob,这些将后续一一描述。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/30264.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

银行测试人员谈测试需求

今天呢&#xff0c;想用故事说话&#xff0c;先看看啥叫用户需求挖掘。其实看完故事之后&#xff0c;我自己颇为震撼&#xff0c;请看。 故事一&#xff1a; 100多年前&#xff0c;福特公司的创始人亨利福特先生到处跑去问客户&#xff1a;“您需要一个什么样的更好的交通工具…

loganalyzer 展示数据库中的日志

1 实验目标&#xff1a; 利用rsyslog日志服务&#xff0c;将收集的日志记录于MySQL中&#xff0c;通过loganalyzer 展示数据库中的日志 2 环境准备 三台主机&#xff1a; 一台日志服务器&#xff0c;利用上一个案例实现&#xff0c;IP&#xff1a;192.168.100.100一台数据库…

【Java八股文总结】之数据结构

文章目录数据结构一、概念1、时间复杂度与空间复杂度2、常见算法时间复杂度3、Comparable二、常见的排序算法1、直接插入排序2、希尔排序3、选择排序4、堆排序5、冒泡排序6、快速排序7、归并排序8、二分查找算法Q&#xff1a;什么时候需要结束呢&#xff1f;三、线性表1、概念2…

使用 Footprint Analytics, 快速搭建区块链数据应用

Nov 2022, danielfootprint.network 如果你有一个处理 NFTs 或区块链的网站或应用程序&#xff0c;你可以在你的平台上直接向用户展示数据&#xff0c;以保持他们在网站或者应用内的参与&#xff0c;而不是链接以及跳出到其他网站。 对于任何区块链应用或者媒体、信息网站来说…

秦皇岛科学选育新品种 国稻种芯·中国水稻节:河北秸秆变肥料

秦皇岛科学选育新品种 国稻种芯中国水稻节&#xff1a;河北秸秆变肥料 秦皇岛新闻网 记者李妍 冀时客户端报道&#xff08;河北台 张志刚 米弘钊 赵永鑫 通讯员 赵力楠&#xff09; 新闻中国采编网 中国新闻采编网 谋定研究中国智库网 中国农民丰收节国际贸易促进会 国稻种芯…

无线通信技术概览

电生磁&#xff0c;磁生电 电场和磁场的关系&#xff0c;简而言之就是&#xff1a;变化的电场产生磁场&#xff0c;变化的磁场产生电场。 电荷的定向移动产生电流&#xff0c;电荷本身产生电场。电流是移动的电场。静止的电荷产生静止的电场&#xff0c;运动的电荷产生运动的电…

java实现阿里云rocketMQ消息的发送与消费(http协议sdk)

目录一、准备工作二、代码实现1.添加依赖2.创建一个常量类存放公共参数3.调用HTTP协议的SDK 发送普通消息4.调用HTTP协议的SDK 订阅普通消息三、配置main的日志输出级别四、测试效果五、完成代码一、准备工作 登录阿里云官网&#xff0c;先申请rocketMQ&#xff0c;再申请Topi…

一文带你了解PCB设计中的常用基本概念

本文将从初学者的角度出发&#xff0c;一文带你快速了解PCB设计中的常用基本概念&#xff1a;一、FR4板材FR-4就是玻璃纤维环氧树脂覆铜板&#xff0c;线路板中的一种基材&#xff0c;可以分为一般FR4板材和高TG FR4板材&#xff0c;Tg是玻璃转化温度&#xff0c;即熔点。电路板…

分享好玩的h5小游戏制作_为什么要做h5微信小游戏呢

近年来&#xff0c;市面上一直流行各种h5游戏&#xff0c;例如投票、答题、刮刮乐、大转盘等等等等&#xff0c;而且我在各种营销场景下经常看到它们的身影&#xff0c;是做促销&#xff0c;引流和宣传的神器之一&#xff01; 那么&#xff0c;怎么做好玩的h5游戏&#xff1f;还…

DIXml v5.21.0 for Delphi 11

DIXml v5.21.0 for Delphi 11 DIXml是一个嵌入式XML、XSLT&#xff0c;也是Delphi的EXSLT处理库(Embarcadero//CodeGear/Borland)。它构建在libxml2、libxslt和libexslt库上&#xff0c;但不需要更多的DLL或其他外部文件。 DIXml很容易成为Delphi中功能最齐全的XML和XSLT替代品…

实战讲解MyBatis缓存:一级缓存和二级缓存(图+文+源码)

1 缘起 回顾SpringBoot如何进行事务管理相关知识的时&#xff0c; 发现使用Spring的注解Transational即可实现事务管理&#xff0c;完成回滚操作&#xff0c; 然而SpringBoot中使用MyBatis这个ORM框架操作数据库&#xff0c;实现CURD&#xff0c; 这两者有什么关系呢&#xff…

集合类不安全

ArryList集合 多线程下不安全&#xff1b;可能会报错&#xff1a;java.util.ConcurrentModificationException&#xff08;并发修改异常&#xff09; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList;//java.util.ConcurrentModificationException 并发…

每日刷题3——牛客,算术转换和二级指针

更新不易&#xff0c;麻烦多多点赞&#xff0c;欢迎你的提问&#xff0c;感谢你的转发&#xff0c; 最后的最后&#xff0c;关注我&#xff0c;关注我&#xff0c;关注我&#xff0c;你会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我…

FFmpeg入门 - 视频播放

音视频最好从能够直接看到东西,也更加贴近用户的播放开始学起. 音视频编解码基础 我们可以通过http、rtmp或者本地的视频文件去播放视频。这里的"视频"实际上指的是mp4、avi这种既有音频也有视频的文件格式。 这样的视频文件可能会有多条轨道例如视频轨道、音频轨道…

(200,10)和(10,)的ndarray数组怎么计算内积,得到的是什么维度?

今天在定义一个内积运算的时候&#xff0c;发现了一个模糊的问题&#xff0c;两个变量&#xff0c;都是ndarray数组&#xff0c;一个维度是(200,10)&#xff0c;一个维度是(10,)&#xff0c;两者求内积后得到的新数组的维度是(200,)&#xff0c;该如何理解&#xff1f; 一、数…

你给我解释解释,什么TMD叫TMD attention(持续更新ing...)

诸神缄默不语-个人CSDN博文目录 开宗明义&#xff1a;attention就是一个加权机制&#xff0c;但是权重需要经过训练得到。 本文介绍一些我自己整理的常见attention类型。 本文不关注别的博文里已经写烂了的attention机制的重要性和直觉解释&#xff08;又不是写论文还要写int…

SpringBoot项目调用Matlab方法

SpringBoot项目调用Matlab方法需求环境准备步骤Matlab方面java方面结果需求 java调用matlab的函数 环境准备 Matlab&#xff0c;jdk&#xff0c;maven管理的springboot项目 步骤 Matlab方面 准备一个简单函数 function [x,y,z] jarDemo(arg) disp("hello world fr…

JVM运行数据区深度解析

运行数据区 字节码只是一个二进制文件存放在那里。要想在jvm里跑起来&#xff0c;先得有个运行的内存环境。 也就是我们所说的jvm运行时数据区。 1&#xff09;运行时数据区的位置 运行时数据区是jvm中最为重要的部分&#xff0c;执行引擎频繁操作的就是它。类的初始化&…

IPD-产品需求管理过程(2)

1、需求分析方法 需求分析分为需求解释、需求过滤、需求分类、需求排序四步。 SEG&#xff1a;system engineering group,意为系统工程组 其中&#xff0c;需求解释环节要完成客户需求语言向内部规范描述语言的转换&#xff1b;需求过滤环节要去伪存真、去粗取精&#x…

C++11 异步操作future和aysnc

目录 C11异步操作的4个接口 1. std::aysnc和std::future std::future和std::aysnc的使用Demo 2. std::packaged_task std::packaged_task的使用Demo 3. std::promise std::promise的使用Demo 总结 C/CLinux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂 C1…