-
2022-03-09 11:43:57
写在前面
该篇博客大致梳理了从引导阶段的memblock.memory到预留内存和动态内存的分配,再到分页机制的建立,以及内存节点有效section的初始化和mem_section下每个有效页框的映射关系,最后seciton下页框属性的初始化。
kernel启动阶段的物理内存初始化流程如下:
- 获取线性地址大小,记录物理内存的起始地址,以及通过memblock分配器来初始化系统预留内存。
- 初始化系统分页机制;
- 初始化内存基本数据结构包括内存节点,内存域;
- 初始化zonelist;
- 初始化内存分配器;
一、 memblock简介
在Memblock之前还有一个在内核2.3.23版本引入的bootmem引导内存分配器。随着内存检测从简单的bios询问扩展内存块的大小演变为处理复杂的表,快,库和群集。人们开始使用memblock作为引导内存分配器。为什么我们把memlbock称之为引导内存分配器?很显然这是在kernel启动早期,系统还处于引导阶段。Memblock刚开始被称为LMB,Logical Memory Block。后来Yinghai Lu提了一个patch将其重命名为memblock
更多相关内容 -
DPDK : 解析内存初始化的过程
2019-05-01 17:41:09这一篇文章主要是对DPDK的EAL(Environment Abstraction Layer)中内存的初始化的解析,这是DPDK内存管理的基础。 注:在DPDK中,初始化由primary process完成,而其他process统称为secondary process,其可以通过读取...说明
这一篇文章主要是对DPDK的EAL(Environment Abstraction Layer)中内存的初始化的解析,这是DPDK内存管理的基础
由于个人水平所限,若所写的博文中存在错误,希望大家能帮忙指出。注:
1,在DPDK中,初始化由primary process完成。而其他process统称为secondary process,其可以通过读取一些文件来获取primary process的初始化信息,从而使得自身与primary process保持相同的内存映像。
2, DPDK采用了一种集中式控制的方式,比如在多进程的场景中,若一个secondary process要申请内存,则向primary process发起请求,由primary process完成相应操作后在通知secondary process(DPDK : 进程间通信以及在内存管理的应用)。一,初始化相关的代码入口
从lib/librte_eal/linux/eal/eal.c中的方法int rte_eal_init(int argc,char **argv)开始:
int rte_eal_init(int argc, char **argv){ ······ rte_config_init(); if (internal_config.no_hugetlbfs == 0) { /* rte_config isn't initialized yet */ ret = internal_config.process_type == RTE_PROC_PRIMARY ? eal_hugepage_info_init() : eal_hugepage_info_read(); ······ } ······ if (rte_eal_memzone_init() < 0) { ······ } if (rte_eal_memory_init() < 0) { ······ } if (rte_eal_malloc_heap_init() < 0) { ······ } }
从上面可以看出,内存的初始化过程主要包含5个方面的初始化, rte_config_init(), eal_hugepage_info_init(), rte_eal_memzone_init(), rte_eal_memory_init(), rte_eal_malloc_heap_init(), 下面依次对这五个方面进行解析:
二,相关的结构体的说明
在开始之前,先对一些结构体中关于内存的变量进行一些说明(使用······说明省略了一些变量,不影响理解DPDK内存初始化):
********************rte_eal.h******************** struct rte_config { //运行时环境的配置 ······ /** PA or VA mapping mode */ enum rte_iova_mode iova_mode; //指明了DMA使用虚拟地址(virtual address, 简称VA), 还是物理地址(physical address, 简称PA) /** * Pointer to memory configuration, which may be shared across multiple * DPDK instances */ //这个指针指向的内存空间存放了一个DPDK instance的内存分布情况 //DPDK内存初始化过程主要是初始化struct rte_mem_config中的每一项 struct rte_mem_config *mem_config; } __attribute__((__packed__)); ********************eal_internal_cfg.h******************* struct hugepage_info {//用于存放每一个hugepage的信息 uint64_t hugepage_sz; /**< size of a huge page */ char hugedir[PATH_MAX]; /**< dir where hugetlbfs is mounted */ uint32_t num_pages[RTE_MAX_NUMA_NODES]; /**< number of hugepages of that size on each socket */ int lock_descriptor; /**< file descriptor for hugepage dir */ //lock_descriptor指的是挂载点(即hugedir字段)对应的file descriptor }; /** * internal configuration */ struct internal_config { //DPDK的全局配置信息 volatile size_t memory; /**< amount of asked memory */ //请求分配的内存数量 ······· volatile unsigned no_hugetlbfs; /**< true to disable hugetlbfs */ //是否允许使用hugetlbfs unsigned hugepage_unlink; /**< true to unlink backing files */ //是否删除hugepage文件(DPDK在memalloc时将每一个hugepage当做一个文件处理) ······· volatile unsigned no_shconf; /**< true if there is no shared config */ //是否允许共享,不允许的话primary process不会将初始化信息写入到文件 volatile unsigned in_memory; /**< true if DPDK should operate entirely in-memory and not create any * shared files or runtime data. */ volatile enum rte_proc_type_t process_type; /**< multi-process proc type */ //用于区分primary process, 或者secondary process /** true to try allocating memory on specific sockets */ volatile unsigned force_sockets; //强制在指定的socket上分配内存 volatile uint64_t socket_mem[RTE_MAX_NUMA_NODES]; /**< amount of memory per socket */ //表示每一个socket分配的内存数量 volatile unsigned force_socket_limits; //设置是否限制socket分配的内存 volatile uint64_t socket_limit[RTE_MAX_NUMA_NODES]; /**< limit amount of memory per socket */ //每一个socket分配的内存的上限 uintptr_t base_virtaddr; /**< base address to try and reserve memory from */ volatile unsigned legacy_mem; //指明是legacy mode, 或者dynamic mode /**< true to enable legacy memory behavior (no dynamic allocation, * IOVA-contiguous segments). */ volatile unsigned match_allocations; /**< true to free hugepages exactly as allocated */ volatile unsigned single_file_segments; //指明是single-file-segments mode, 或者 page-per-file mode /**< true if storing all pages within single files (per-page-size, * per-node) non-legacy mode only. */ unsigned num_hugepage_sizes; /**< how many sizes on this system */ struct hugepage_info hugepage_info[MAX_HUGEPAGE_SIZES]; }; ********************rte_eal_memconfig.h******************* struct rte_mem_config { volatile uint32_t magic; /**< Magic number - Sanity check. */ /* memory topology */ uint32_t nchannel; /**< Number of channels (0 if unknown). */ uint32_t nrank; /**< Number of ranks (0 if unknown). */ ······ /* memory segments amemnd zones */ struct rte_fbarray memzones; /**< Memzone descriptors. */ //每一个struct rte_memseg_list中使用<socket id, pagesz>进行标识 //memsegs 可能存在多个具有相同<socket id, page_sz>的struct rte_memseg_list struct rte_memseg_list memsegs[RTE_MAX_MEMSEG_LISTS]; /**< list of dynamic arrays holding memsegs */ ······ /* Heaps of Malloc */ struct malloc_heap malloc_heaps[RTE_MAX_HEAPS]; ······ /* address of mem_config in primary process. used to map shared config into * exact same address the primary process maps it. */ uint64_t mem_cfg_addr; //这个地址等于struct rte_config中的struct rte_mem_config *mem_config /* legacy mem and single file segments options are shared */ uint32_t legacy_mem; //指明内存是legacy mode, 还是dynamic mode uint32_t single_file_segments; // 指明memalloc是single-file-segments mode, 还是page-per-file mode ······ } __attribute__((__packed__)); struct rte_memseg_list {//每一个rte_memseg_list由page_sz和socket_id唯一标识 RTE_STD_C11 union { void *base_va; /**< Base virtual address for this memseg list. */ uint64_t addr_64; /**< Makes sure addr is always 64-bits */ };//指向一块用于存放rte_memseg的内存空间 uint64_t page_sz; /**< Page size for all memsegs in this list. */ int socket_id; /**< Socket ID for all memsegs in this list. */ ······ size_t len; /**< Length of memory area covered by this memseg list. */ //指明具有base_va所指向的内存空间的字节数总量 ······ struct rte_fbarray memseg_arr; //用于管理base_va指向的内存空间,包含rte_memseg相关的元数据 }; ********************rte_memory.h******************* struct rte_memseg { //一个rte_memseg等同于一个hugepage RTE_STD_C11 union { phys_addr_t phys_addr; /**< deprecated - Start physical address. */ rte_iova_t iova; /**< Start IO address. */ }; RTE_STD_C11 union { void *addr; /**< Start virtual address. */ uint64_t addr_64; /**< Makes sure addr is always 64 bits */ }; size_t len; /**< Length of the segment. */ uint64_t hugepage_sz; /**< The pagesize of underlying memory */ int32_t socket_id; /**< NUMA socket ID. */ uint32_t nchannel; /**< Number of channels. */ uint32_t nrank; /**< Number of ranks. */ uint32_t flags; /**< Memseg-specific flags */ } __rte_packed; ********************eal_hugepages.h******************* struct hugepage_file { void *orig_va; /**< virtual addr of first mmap() */ void *final_va; /**< virtual addr of 2nd mmap() */ uint64_t physaddr; /**< physical addr */ size_t size; /**< the page size */ int socket_id; /**< NUMA socket ID */ int file_id; /**< the '%d' in HUGEFILE_FMT */ //这是第file_id个大小为size的hugepage char filepath[MAX_HUGEPAGE_PATH]; /**< path to backing file on filesystem */ //filepath指明hugepage对应的文件 };
三,对于primary process的内存初始化过程:
1, rte_config_init() :
这个函数主要是为struct rte_config中的struct rte_mem_config *mem_config(简称mcfg)申请一块内存空间,并且在运行时目录下创建一个名字为config的文件,并且将mcfg的内容写进此文件。这样,secondary process在初始化时就能通过读取config文件来创建和primary process一样的内存映像。
在这里采用了mmap()的方式将config文件和mcfg进行了映射,所以在后面的初始化操作中,一旦对config进行了写操作,也能够立刻反映到其他的进程中(类似于使用共享内存通信)。
2, eal_hugepage_info_init() : 读取系统中的hugepage的信息。
在linux系统中,会打开系统目录/sys/kernel/mm/hugepages,遍历每一个目录项下获取系统支持的hugepage size。然后从/proc/mounts中根据hugepage size获取对应挂载点(mount point), 然后计算在不同socket中每一种free hugepage的数量,。将每一种大页的相关信息存放在internal_config->hugepage_info中。
然后会在runtime dir下创建一个名字为hugepage_info的文件,将internal_config->hugepage_info写入到该文件。
3,rte_eal_memzone_init()
初始化mcfg->memzones, 申请一块内存空间,用于保存以后内存分配时使用到的struct memzone。(memzone所使用的内存空间也是从rte_heap中分配的,rte_memzone_reserve和rte_malloc之间的区别尚未解决)
4,rte_eal_memory_init() : 这是内存初始化过程的核心,其中包括了memseg_primary_init(), eal_memalloc_init(), rte_eal_hugepage_init(), rte_eal_memdevice_init().
1), memseg_primary_init() :
确定每一种类型(由socket id和page sz确定)的struct rte_memseg_list的数量,及其所包含的mem segment的数量。 然后,根据确定的数量为mcfg->memsegs中的struct rte_memseg_list分配虚拟内存空间。
2), eal_memalloc_init() :
初始化struct fd_list 如果是single-file-segments mode, 则对于一个rte_memseg_list,只使用一个file descriptor(fd_list中的memseg_list_fd) 如果是file-per-page, 则对于一个rte_memseg_list中的每一个mem segment, 都会使用一个file descriptor(fd_list中的fds)
3), rte_eal_hugepage_init()
*如果是legacy mem, 则调用eal_legacy_hugepage_init() 1), 根据internal_config->hugepage_info初始化hugepage_file, 并且将这些hugepage_file, 根据<socket id, pagesz> 对应的rte_memseg_list中的rte_memseg进行映射。 2),然后对hugepage_file排序(使得按照页的size降序排序,同一种size按照物理地址升序排序), 然后根据internal_config->socket_mem计算hugepage在不同socket的分布, 3),之后对所有的hugepage_file进行重映射,使得虚拟内存连续的mem segments在物理内存上也是连续的, 并且同一个rte_memseg_list所有的mem_sgement的虚拟地址和物理地址都是单调递增。 4),接着,设置fd_list中对应的file descriptor。 5),这个方法会将hugepage_file写入到hugepage_data文件。 在实现的过程中采用了read-ahead, 目的是为了保证虚拟内存连续的mem segments在物理内存上也是连续的, 同时也能够提前载入物理页,提高系统的性能。 而对于nohugepage的情况,将其视为legacy, single-file mode,采用的页的大小为4K。 *如果是dynamic mem, 则调用eal_hugepage_init() 根据socket_mem的需求,计算hugepage在不同socket的分布。然后使用了eal_memalloc_alloc_seg_bluk进行分配, 由于这个方法是一个一个mem segment进行分配,所以不能保证分配完成后,虚拟空间上连续的mem segments在物理上也是连续的. 采用了pre-allocate,能够提高系统的性能。
4), rte_eal_memdevice_init().:
设置mcfg->nchannel, mcfg->nrank
5, rte_eal_malloc_heap_init()
初始化mcfg->malloc_heaps;并且注册进程间通信的handle,用于多进程环境下的内存分配;初始化heap的结构。
其中每一个socket会对应一个heap。初始化完成后heap的结构如下(一个例子) :
假设系统支持两种大小的hugepage(2MB, 1GB)
上图的heap包含两个rte_memseg_list, 每一个都包含3个contiguous mem segments(其中可能包含一个或多个hugepage), 总共有6个contiguous mem segments(图中浅黄色的部分). 每一个contigous mem segments都包含一个malloc_elem,用于记录此contiguous mem segments的元数据。每一个struct malloc_heap都会指向第一个malloc_elem和最后一个malloc_elem;并且一个heap中,所有的malloc_elem会组成一个双向链表。四,对于secondary process的内存初始化过程:
1, rte_config_init()
使用mmap()将config文件映射到此进程的mcfg,这样可以直接读取primary process的内存映像.
2, eal_hugepage_info_init() :
读取hugepage文件的内容,并保存在struct internal_config->hugepage_info中。
3,rte_eal_memzone_init()
根据config文件中关于memzones的内容, 创建一个和primary process具有相同内存映像的mcfg->memzones
4,rte_eal_memory_init() : 这是内存初始化过程的核心,其中包括了memseg_secondary_init(), eal_memalloc_init(), rte_eal_hugepage_attach(), rte_eal_memdevice_init().
1), memseg_secondary_init() :
直接根据**config**文件的内容创建和primary process相同的虚拟内存空间视图。
2), eal_memalloc_init() :
对mcfg中的struct rte_memseg_list, 创建一个本地副本(即local_memsegs),用于同步memory hotplug 初始化struct fd_list 如果是single-file-segments mode, 则对于一个rte_memseg_list,只使用一个file descriptor(fd_list中的memseg_list_fd) 如果是file-per-page, 则对于一个rte_memseg_list中的每一个mem segment, 都会使用一个file descriptor(fd_list中的fds)
3)rte_eal_hugepage_attach()
如果是legacy mem, eal_legacy_hugepage_attach() 读取hugepage_data文件,根据文件的内容建立与primary process相应的内存映像,并且设置fd_list中相应的file descriptor. 如果是dynamic mem, eal_hugepage_attach() 调用eal_memalloc_sync_with_primary(), 将primary process的mcfg->memsegs同步到此进程的local_memsegs.
4)rte_eal_memdevice_init() :
不做任何操作.
5, rte_eal_malloc_heap_init()
初始化mcfg->malloc_heaps;并且注册进程间通信的handle,用于多进程环境下的内存分配;初始化heap的结构。
五,总结
1,如果没有采用hugetlbfs,则默认采用系统页(大小为4K)
2, DPDK有两种内存模式 :legacy mode : 保证虚拟空间连续的contiguous mem segments在物理空间上也是连续的 dynamic mode : 分配hugepage时是一个一个分配的,不能和legacy mode有一样的保证
3, DPDK在memalloc时有两种模式single-file-segments, page-per-file, 每一种都在hugetlbfs的挂载点上有相应的文件形式(即存在于内存中的文件),这样在内存分配时可以使用对file descriptor操作的系统调用对内存进行操作。
4,每一个socket有一个heap, 每一个heap包含若干个rte_memseg_list, 每一个rte_memseg_list包含若干rte_memseg, 一个rte_memseg对应于一个memory page
5, 在分配内存时,采用了read-ahead, pre-allocated等方法,能够减少由于页错误而阻塞的情况,提高系统的性能。 -
C/C++中的分配内存初始化(new和malloc)
2019-08-14 14:07:101、new和delete以及new[]和delete[] #include<iostream> using namespace std; int main() { int* p1 = new int;...//动态分配一个4字节(一个int)的空间,并初始化为10。 int* p3 = new i...1、new和delete以及new[]和delete[]
#include<iostream> using namespace std; int main() { int* p1 = new int;//动态分配一个4字节(int)空间 int* p2 = new int(10);//动态分配一个4字节(一个int)的空间,并初始化为10。 int* p3 = new int[10];//动态分配40个字节(10个int)的空间。 delete p1; delete p2; delete[] p3; return 0; }
通过上述的代码,就是说明了C++在使用new时常用的三种方式, //2020.7.14
int *p = new int //new 数据类型
int *p = new int (10);//new 数据类型(初始值)
int *p = new int [100]; //new 数据类型[常量表达式]
2、malloc和free的使用
首先使用malloc函数的函数的时候需要加上头文件#include <malloc.h>
#include <iostream> #include<malloc.h> using namespace std; int main() { //[1]给指针申请内存 char *p = (char *)malloc(100);//申请一块内存,然后再释放 free(p); //[2]较多的用法 int *p1 = (int*)malloc(sizeof(int)); *p1 = 10; printf("%d\n", *p1); //[3]申请int型的指针变量内存 int* b = (int*)malloc(sizeof(int)*4); return 0; }
参考博客:C++ 动态内存管理(new /delete-new[]/delete[]-N次释放)_porryCn的博客-CSDN博客_new 什么时候释放
参考博客:C语言指针之二malloc的用法及详解_修炼之路-CSDN博客_malloc函数用法
c++学习之概述--从C到C++_OrangeRen-CSDN博客
注意:关于malloc的增加内容//2022.2
关于malloc的内存申请上述分配内容是有问题的,具体可以参考博客:c语言之内存的申请malloc() 和释放free() - stevenwuzheng - 博客园
指出两点问题:
1、malloc申请内存有可能不成功,所以需要增加判断 ,必须使用if(NULL!=p)语句来验证内存确实分配成功了
2、当free掉p之后,变成野指针的p,需要将它设置成p = NULL,这样才能防止野指针带来的问题。
-
c++结构体数组内存初始化
2017-04-14 02:23:45memset能够对char数组进行内存初始化,但是怎样对结构体中有int 和double 型数据的数组进行初始化呢?有没有比用for循环效率更高的初始化方法呢? 例如这个结构体 typedef struct QueeNode{ int a; int b; int c;... -
内存的初始化
2017-03-29 10:23:13内存的初始化内存的分类
内存由于具备访问速度快,访问方式简单等优点,成为了PC或者是嵌入式硬件平台上不可或缺的元件。在开始学习如何使用内存之前,非常有必要先了解一下内存的分类:
DRAM:它的基本原件是小电容,电容可以在两个极板上保留电荷,但是需要定期的充电(刷新),否则数据会丢失。缺点:由于要定期刷新存储介质,存取速度较慢。
SRAM:它是一种具有静止存取功能的内存,不需要定期刷新电路就能保存它内部存储的数据。其优点:存取速度快; 但是缺点是:功耗大,成本高。常用作存储容量不高,但存取速度快的场合,比如stepping stone。
在嵌入式硬件体系中,除了CPU内部的“垫脚石”采用SRAM外,板载内存一般会采用DRAM,而DRAM又可以分为SDRAM,DDR,DDR2等。
SDRAM(Synchronous Dynamic Random AccessMemory):同步动态随机存储器:
同步: 内存工作需要有同步时钟,内部的命令的发送与数据的传输都以该时钟为基准。
动态:存储阵列需要不断的刷新来保证数据不丢失。
随机:是指数据不是线性依次存储,而是自由指定地址进行数据读写。DDR (Double Data Rate SDRAM),即“双倍速率同步动态随机存储器”。与早期的SDRAM相比,DDR 除了可以在时钟脉冲的上升沿传输数据,还可以在下降沿传输信号,这意味着在相同的工作频率下,DDR 的理论传输速率为SDRAM的两倍。DDR2 则在DDR 的基础上再次进行了改进,使得数据传输速率在DDR 的基础上再次翻倍。
内存的内部结构
表结构:
内存的内部如同表格,数据就存放在每个单元格中。数
据读写时,先指定行号(行地址),再指定列号(列地
址) ,我们就可以准确地找到所需要的单元格。而这张表格的称为:Logical Bank(L-Bank)。由于技术、成本等原因,一块内存不可能把所有的单元格都做到一个L-Bank,现在内存内部基本都会分割成4个L-Bank。
需要向芯片提供以下3个信息来做到寻址:
1、L-Bank选择信号
2、行地址
3、列地址OK6410开发板上的内存资源:
128M字节Mobile DDR内存。
2片KSX51163PC芯片:
每一片为:32M*16,总共128M上面的16有两重含义:1、每个内存单元的大小为16位; 2、内存芯片的数据宽度为16位。ARM芯片的数据线为32位,剩下的16位连入另一个内存芯片。
4 * 2^13 * 2^10 = 32M2440内存初始化
2440开发板配置的是SDRAM内存。
S3c2440芯片对外提供的引脚上,只给出了27根地址线Addr[0:26]。单靠芯片上的 27 根引脚,它只能访问128M 的外设空间。
为了扩大外设的访问范围,S3c2440芯片又提供了8个片选信号nGCS0~nGCS7。当某个片选信号nGCSx有效时,则可以通过27根地址线去访问对应这个片选的128MB空间。由于有8个片选,所以2440芯片能访问的外设空间总共为8*128MB=1GB。而1G (0x40000000)以上的空间,则安排给了2440内部的寄存器,访问这些内部的寄存器,则是通过32位的处理器内部总线来完成的。
一般把内存放置在片选6和片选7的位置,因此编写程序时,内存的起始地址为0x30000000。
对于某一个地址,首先有存储控制器进行分解,然后对相应的存储设备进行操作。因此,对内存的初始化,其实就是对存储控制器的初始化。
初始化存储寄存器:(mini2440开发板)
#define mem_contrl 0x48000000 init_sdram: ldr r0, =mem_contrl add r3, r0, #4*13 @最后一个地址 adrl r1, mem_data 0: ldr r2, [r1], #4 @加载并且更新地址 str r2, [r0], #4 @存储并且更新地址 cmp r0, r3 bne 0b @不等于就跳到分支 mov pc, lr mem_data: .long 0x22000000 .long 0x00000700 .long 0x00000700 .long 0x00000700 .long 0x00000700 .long 0x00000700 .long 0x00000700 .long 0x00018001 .long 0x00018001 .long 0x008c04f5 .long 0x000000b1 .long 0x00000030 .long 0x00000030
6410内存初始化
6410开发板配置的是DDR内存。
S3C6410处理器拥32位地址总线,其寻址空间为4GB。其中高2GB为保留区,低2GB区域又可划分为两部分:主存储区和外设区。
保留区没有使用。外设区放置6410芯片中的寄存器,访问这些内部寄存器就通过这些地址。主存储区
主存储区的划分:
boot启动镜像区:
这个区域的作用正如它的名字所述,是用来启动ARM系统的。但是这个区域并没有固定的存储 介质与之对应。而是通过修改启动选项,把不同的启动介质映射到该区域。比如说选择了IROM 启动方式后,就把IROM映射到该区域。内部存储区:
这个区域对应着内部的内存地址,iROM和SRAM都是分布在这个区间。0x08000000~0x0bffffff对应着内部ROM,但是IROM实际只有32KB,选择从IROM启动的时候,首先运行就是这里面的程序BL0,BL0这部分代码由三星固化。0x0c000000~0x0fffffff对应内部SRAM,实际就是8KB的Steppingstone。静态存储区:
这个区域用于访问挂在外部总线上的设备,比如说NOR flash、oneNand等。这个区域被分割为6个bank,每个bank为128MB,数据宽度最大支持16bit,每个bank由片选Xm0CS[0]~Xm0CS[5] 选中。动态存储区:
该区域从0x50000000~0x6fffffff,又分为2个区间,分别占256MB,可以片选Xm1CS[0]~Xm1CS[1]来进行着2个区间的选择。我们6410开发板上256MB的DDR内存就安排在这个区域,这也就是为什么6410的内存地址是从0x50000000开始的原因。内存芯片的连接方式:
6410芯片手册中的存储控制器
芯片手册第5章,DRAM CONTROLLER5.4节介绍了内存控制器初始化的流程。
uboot代码中的cpu\s3c64xx\s3c6410文件下的cpu_init.S文件下的标号mem _ ctrl _ asm _ init下的代码就是对存储控制器进行初始化的代码。
代码举例:(OK6410开发板)
.text .global mem_init mem_init: ldr r0, =0x7e00f120 @设置数据线 mov r1, #0x8 str r1, [r0] ldr r0, =0x7e001004 @make DRAM Controller enter ‘Config’ state mov r1, #0x4 str r1, [r0] ldr r0, =0x7e001010 @鍒锋柊瀵勫瓨鍣ㄥ湴鍧€ ldr r1, =( ( 7800 / ( 1000000000/133000000 ) + 1 ) ) @璁剧疆鍒锋柊鏃堕棿 str r1, [r0] ldr r0, =0x7e001014 @CAS latency瀵勫瓨鍣? mov r1, #(3 << 1) str r1, [r0] ldr r0, =0x7e001018 @t_DQSS瀵勫瓨鍣? mov r1, #0x1 str r1, [r0] ldr r0, =0x7e00101c @T_MRD瀵勫瓨鍣? mov r1, #0x2 str r1, [r0] ldr r0, =0x7e001020 @t_RAS瀵勫瓨鍣? ldr r1, =( ( 45 / ( 1000000000 / 133000000 ) + 1 ) ) str r1, [r0] ldr r0, =0x7e001024 @t_RC瀵勫瓨鍣? ldr r1, =( ( 68 / ( 1000000000 / 133000000 ) + 1 ) ) str r1, [r0] ldr r0, =0x7e001028 @t_RCD瀵勫瓨鍣? ldr r1, =( ( 23 / ( 1000000000 / 133000000 ) + 1 ) ) str r1, [r0] ldr r0, =0x7e00102c @t_RFC瀵勫瓨鍣? ldr r1, =( ( 80 / ( 1000000000 / 133000000 ) + 1 ) ) str r1, [r0] ldr r0, =0x7e001030 @t_RP瀵勫瓨鍣? ldr r1, =( ( 23 / ( 1000000000 / 133000000 ) + 1 ) ) str r1, [r0] ldr r0, =0x7e001034 @t_rrd瀵勫瓨鍣? ldr r1, =( ( 15 / ( 1000000000 / 133000000 ) + 1 ) ) str r1, [r0] ldr r0, =0x7e001038 @t_wr瀵勫瓨鍣? ldr r1, =( ( 15 / ( 1000000000 / 133000000 ) + 1 ) ) @ ldr r2, [r0] str r1, [r0] ldr r0, =0x7e00103c @t_wtr瀵勫瓨鍣? mov r1, #0x07 str r1, [r0] ldr r0, =0x7e001040 @t_xp瀵勫瓨鍣? mov r1, #0x02 str r1, [r0] ldr r0, =0x7e001044 @t_xsr瀵勫瓨鍣? ldr r1, =( ( 120 / ( 1000000000 / 133000000 ) + 1 ) ) str r1, [r0] ldr r0, =0x7e001048 @t_esr瀵勫瓨鍣? ldr r1, =( ( 120 / ( 1000000000 / 133000000 ) + 1 ) ) str r1, [r0] ldr r0, =0x7e00100c @鍐呭瓨鎺у埗閰嶇疆瀵勫瓨鍣? ldr r1, =0x00010012 @閰嶇疆鎺у埗鍣? str r1, [r0] ldr r0, =0x7e00104c @32浣岲RAM閰嶇疆鎺у埗瀵勫瓨鍣? ldr r1, =0x0b45 str r1, [r0] ldr r0, =0x7e001200 @鐗囬€夊瘎瀛樺櫒 ldr r1, =0x150f8 str r1, [r0] ldr r0, =0x7e001304 @鐢ㄦ埛閰嶇疆瀵勫瓨鍣? mov r1, #0x0 str r1, [r0] ldr r0, =0x7e001008 @从这一步开始,初始化内存,设置mem_cmd为nop ldr r1, =0x000c0000 str r1, [r0] ldr r1, =0x00000000 @‘Prechargeall’ str r1, [r0] ldr r1, =0x00040000 @‘Autorefresh’2次 str r1, [r0] str r1, [r0] ldr r1, =0x000a0000 @MRS str r1, [r0] ldr r1, =0x00080032 @Mode Reg str r1, [r0] ldr r0, =0x7e001004 @memc_cmd‘3’b000’ mov r1, #0x0 @DRAM Controller enter ‘Ready’ state str r1, [r0] check_dmc1_ready: @检查内存状态 ‘2’b01’, which means'Ready’ ldr r0, =0x7e001000 ldr r1, [r0] mov r2, #0x3 and r1, r1, r2 cmp r1, #0x1 bne check_dmc1_ready nop mov pc, lr
210内存初始化
210开发板配置的是DDR2内存。
内存起始地址为0x20000000
210开发板的内存通常采用8片或者4片128M*8bit芯片级联的办法。
代码举例(OK210开发板)
#define DMC_PHYCONTROL0 0xf0000018 #define DMC_PHYCONTROL1 0xf000001c #define DMC_CONCONTROL 0xf0000000 #define DMC_MEMCONTROL 0xf0000004 #define DMC_MEMCONFIG0 0xf0000008 #define DMC_MEMCONFIG1 0xf000000c #define DMC_PRECHCONFIG 0xf0000014 #define DMC_TIMINGAREF 0xf0000030 #define DMC_TIMINGROW 0xf0000034 #define DMC_TIMINGDATA 0xf0000038 #define DMC_TIMINGPOWER 0xf000003c #define DMC_PHYSTATUS 0xf0000040 #define DMC_DIRECTCMD 0xf0000010 #define DMC_PWRDNCONFIG 0xf0000028 #define DMC0_MEMCONTROL 0x00202400 #define DMC0_MEMCONFIG_0 0x20F00313 #define DMC0_MEMCONFIG_1 0x00F00313 #define DMC0_TIMINGA_REF 0x00000618 #define DMC0_TIMING_ROW 0x2B34438A #define DMC0_TIMING_DATA 0x24240000 #define DMC0_TIMING_PWR 0x0BDC0343 .globl mem_init mem_init: @ step 2.1 ldr r0, =DMC_PHYCONTROL0 ldr r1, =0x00101000 str r1, [r0] @ step 2.2 ldr r0, =DMC_PHYCONTROL0 ldr r1, =0x00101002 str r1, [r0] @ step 4 ldr r0, =DMC_PHYCONTROL0 ldr r1, =0x00101003 str r1, [r0] @ step 5 ldr r0, =DMC_CONCONTROL ldr r1, =0x0FFF1350 str r1, [r0] @ step 6 ldr r0, =DMC_MEMCONTROL ldr r1, =DMC0_MEMCONTROL str r1, [r0] @ step 7 ldr r0, =DMC_MEMCONFIG0 ldr r1, =DMC0_MEMCONFIG_0 str r1, [r0] @ step 8 ldr r0, =DMC_PRECHCONFIG ldr r1, =0xFF000000 str r1, [r0] @ step 9.1 ldr r0, =DMC_TIMINGAREF ldr r1, =DMC0_TIMINGA_REF str r1, [r0] @ step 9.2 ldr r0, =DMC_TIMINGROW ldr r1, =DMC0_TIMING_ROW str r1, [r0] @ step 9.3 ldr r0, =DMC_TIMINGDATA ldr r1, =DMC0_TIMING_DATA str r1, [r0] @ step 9.4 ldr r0, =DMC_TIMINGPOWER ldr r1, =DMC0_TIMING_PWR str r1, [r0] @ step 11 wait_lock: ldr r0, =DMC_PHYSTATUS ldr r1, [r0] and r2, r1, #0x4 cmp r2, #0x4 bne wait_lock @ step 14 ldr r0, =DMC_DIRECTCMD ldr r1, =0x07000000 str r1, [r0] @ step 16 ldr r1, =0x01000000 str r1, [r0] @ step 17 ldr r1, =0x00020000 str r1, [r0] @ step 18 ldr r1, =0x00030000 str r1, [r0] @ step 19 ldr r1, =0x00010400 str r1, [r0] @ step 20 ldr r1, =0x00000542 str r1, [r0] @ step 21 ldr r1, =0x01000000 str r1, [r0] @ step 22.1 ldr r1, =0x05000000 str r1, [r0] @ step 22.2 ldr r1, =0x05000000 str r1, [r0] @ step 23 ldr r1, =0x00000442 str r1, [r0] @ step 25.1 ldr r1, =0x00010780 str r1, [r0] @ step 25.2 ldr r1, =0x00010400 str r1, [r0] @ step 26, repeat step14~step25 ldr r1, =0x07100000 str r1, [r0] ldr r1, =0x01100000 str r1, [r0] ldr r1, =0x00120000 str r1, [r0] ldr r1, =0x00130000 str r1, [r0] ldr r1, =0x00110400 str r1, [r0] ldr r1, =0x00100542 str r1, [r0] ldr r1, =0x01100000 str r1, [r0] ldr r1, =0x05100000 str r1, [r0] ldr r1, =0x05100000 str r1, [r0] ldr r1, =0x00100442 str r1, [r0] ldr r1, =0x00110780 str r1, [r0] ldr r1, =0x00110400 str r1, [r0] @ step 27 ldr r0, =DMC_CONCONTROL ldr r1, =0x0FF02030 str r1, [r0] ldr r0, =DMC_PWRDNCONFIG ldr r1, =0xFFFF00FF str r1, [r0] ldr r0, =DMC_CONCONTROL ldr r1, =0x00202400 str r1, [r0] mov pc, lr
-
关于C++的new是否会对内存初始化的问题
2017-02-13 10:09:40...C++在new时的初始化的规律可能为:对于有构造函数的类,不论有没有括号,都用...如果没有构造函数,则不加括号的new只分配内存空间,不进行内存的初始化,而加了括号的new会在分配内存的同时初始化为0。 -
用MATLAB生成*.mif(QUARTUS II)内存初始化文件
2016-12-16 11:04:02特别对于ROM,内存的初始化就显得比较重要。当然你完全可以手工在QUARTUS II打开mif文件的表格里或是在EXCEL中逐个输入,几十项(字)或是近百项(字)你还可以接受,如果上千项或是更多呢?估计能累的人吐血! ... -
Uboot 内存初始化(2440)
2014-03-13 21:49:46Uboot 内存初始化(2440) 内存分类,DRAM:需要定期充电(刷新),否则数据会丢失,存取速度慢。SRAM:具有静止存储功能的内存,不需要定期刷新操作就能保存它内部存储的数据,存取速度快,CPU内部的steppingstone... -
【UCOSIII操作系统】系统初始化篇(2)CPU,SysTick,内存初始化
2020-03-29 19:57:17UCOSIII操作系统UCOSIII操作系统——系统初始化篇(2)CPU,SysTick,内存初始化 UCOSIII其他内容导航不迷路 UCOSIII操作系统-简介 UCOSIII操作系统——任务篇(1)创建任务 UCOSIII操作系统——任务篇(2)相关API... -
初始化内存函数:memset()
2019-05-26 08:19:16每种类型的变量都各自的初始化方法,memset()函数可以说是初始化内存空间的“万能函数”,通常为新申请的内存进行初始化工作,他是直接操作内存空间的,mem即是“内存”的意思,该函数的原型: #include<string.h... -
Linux内存初始化:bootmem到buddy的过渡
2014-02-19 13:04:04Linux的内存管理是一个Masterpiece,想把它完全彻底的搞懂真的不容易,今天主要讲一下从bootmem到buddy的过渡。 众所周知,Linux内存管理的核心是伙伴系统(buddy system)。其实在linux启动的那一刻,内存管理就... -
启动期间的内存管理之初始化过程概述----Linux内存管理(九)
2016-09-01 20:27:53日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 ...在内存管理的上下文中, 初始化(initialization)可以有多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换 -
C++ new 动态内存 对象初始化
2018-08-21 16:40:57User a[2]=User(23,24);这种写法对数组的每一个对象调用有参数的构造函数,是功能实现最完备的形式。 User a(23,24);这种写法可以调用有参数的构造函数。...这种写法不初始化,仅分配内存,里面是乱码。 int a=... -
关于STM32使用LWIP协议栈二次初始化时无法成功初始化TCP服务器----内存碎片化问题以及解决方法
2022-03-27 21:33:51关于STM32使用LWIP协议栈二次初始化时无法成功初始化TCP服务器----内存碎片化问题以及解决方法 关于LWIP协议栈的话后期再出一个相关的系列文章吧,关于使用LAN8720芯片断网线重连的问题可以参考:我的这篇博客 这里... -
C语言数组初始化及malloc初始化
2021-05-06 20:39:20数组赋值初始化的三个方式 1、{0} 初始化 例如: int nums[3] = {0}; 但是经常出现的是给定长度(变量)然后需要进行初始化,如果写成下式: int main(){ int numsSize=0; scanf("%d",&numsSize); int ... -
动态内存分配——new的初始化
2019-11-22 15:27:09最近在C++用new进行动态内存分配的时候,发现在new一个内置类型时,new是不会对其进行初始化的。可以总结一下new一个内置类型时,可以采用的初始化方法。 单变量初始化 可以采用以下形式: int*p = new int();//此时... -
C++学习之内存的分配和初始化
2018-10-06 13:46:501. 使用new动态分配和初始化对象 在自由空间分配的内存是无名的,new返回一个指向分配的对象的指针。 int *pi = new int; // pi指向一个动态分配的、未初始化的无名对象 默认情况下,动态分配的对象是默认初始... -
C语言内存的初始化
2016-05-08 02:17:47我们编写C语言的时候需要给变量申请一块内存区域,当我们创建一个内存区域的时候,内存中的数据十有八九是乱七八糟的(因为其他代码用过后遗留的数据并没有及时清掉) 例如: int main() { char str[10];//分配的... -
Java获取的最大堆内存、Jvm初始化总内存与设置的大小不一致问题
2020-11-26 19:30:02通过下述代码:可以分别输出:最大堆内存和Jvm初始化总内存(单位是M) public static void main(String[] args) { System.out.println(Runtime.getRuntime().maxMemory()>>20); System.out.println(Runtime... -
动态分配内存的初始化
2016-08-12 17:09:00在内存的动态分配区域中分配一个长度为size的连续空间,如果分配成功,则返回所分配内存空间的首地址,否则返回NULL,申请的内存不会进行初始化。 2)calloc 函数: void *calloc(unsigned int num, unsigned int ... -
关于C初始化内存
2015-04-23 09:23:12关于C初始化内存 -
c语言结构体学习整理(结构体初始化,结构体指针)
2018-11-01 20:22:12请注意,在我们进行数组初始化的时候如果定义的数组过长,而我们只初始化了一部分数据,对于未初始化的数据如果是数值型,则会自动赋值为0,对于字符型,会自动赋初值为NULL,即‘\0’ 即不足的元素补以默认值 这里... -
6.堆(动态内存 heap)的初始化和使用
2018-06-14 16:36:15堆:先进先出 FIFO:First in first out 手动分配、释放栈:后进先出 FILO:First in last out 自动分配释放裸机情况下使用动态内存heap:在启动文件(startup_stm32f103xe.s)中调整:Heap_Size EQU 0x00000200然后... -
嵌入式系统初始化过程
2018-05-21 16:32:29系统初始化过程可以分为 3 个主要环节,按照自底向上、从硬件到软件的次序依次为:片级初始化、板级初始化和系统级初始化。1.片级初始化 完成嵌入式微处理器的初始化,包括设置嵌入式微处理器的核心寄存器和控制... -
vs使用未初始化的内存怎么解决_遇到C语言内存错误怎么办?一定要找准这六个原因...
2020-11-22 06:32:38一、没有为指针分配内存定义了指针变量,但是没有为指针分配...1、结构体成员指针未初始化struct student { char *name; int score; }stu,*pstu; int main() { strcpy(stu.name,"Jimy"); stu.score = 99; return ... -
使用了未初始化的内存
2020-02-06 13:35:16明明已经定义了due,为什么还会报警说“使用了未初始化的内存due?” ``` #include main() { float money,due; char ontime; printf("How much do you owe?"); scanf_s("%f", &money); printf("On ... -
C语言calloc()函数:分配内存空间并初始化
2017-07-13 08:54:30// 分配100个字节的内存空间下面的两种写法是等价的:// calloc() 分配内存空间并初始化 char *str1 = (char *)calloc(10, 2); // malloc() 分配内存空间并用 memset() 初始化 char *str2 = (char *)malloc(20 -
malloc到未初始化的内存
2016-09-20 09:41:23在最近开发过程中发现一个malloc到未初始化内存的错误. 在使用CLISH(一款命令行开源软件)的时候,每次这个程序退出的时候都会发生core dump,错误为invalid pointer: 0x00007ffff02b35d8 . 但是这个错误只在某一台...