The Master said, “Sacrificing not his ghost, cadging. See righteousness not for, no courage also.” Analects of Confucius: A political essay

A hundred blog series. This paper is: the v12. Xx HongMeng kernel source code analysis (memory management articles) | what is virtual memory panorama

Memory management:

  • V11. Xx HongMeng kernel source code analysis (memory allocation) | what memory allocation
  • V12. Xx HongMeng kernel source code analysis (memory management) | what is virtual memory panorama
  • V14. Xx HongMeng kernel source code analysis (memory assembly) | who is the foundation of virtual memory implementation
  • V15. Xx HongMeng kernel source code analysis (memory mapping) | virtual memory deficiency in where
  • V16. Xx HongMeng kernel source code analysis rules (memory) | what memory management in the tube
  • V17. Xx HongMeng kernel source code analysis (physical memory) | how to manage physical memory

Initialize the entire memory

You can see from the main() trace that the memory part initialization is done in OsSysMemInit().

UINT32 OsSysMemInit(VOID)
{
    STATUS_T ret;

    OsKSpaceInit(a);// Initialize the kernel space

    ret = OsKHeapInit(OS_KHEAP_BLOCK_SIZE);// Initialize 512K of kernel dynamic memory
    if(ret ! = LOS_OK) {VM_ERR("OsKHeapInit fail");
        return LOS_NOK;
    }

    OsVmPageStartup(a);// page initialization
    OsInitMappingStartUp(a);// Map initialization

    ret = ShmInit(a);// Shared memory is initialized
    if (ret < 0) {
        VM_ERR("ShmInit fail");  
        return LOS_NOK;
    }

    return LOS_OK;
}
Copy the code

Hongmeng virtual memory overall layout

// The HarmonyOS kernel space contains the following sections:
extern CHAR __int_stack_start; // Start address of the system function stack
extern CHAR __rodata_start;  // ROM start address read-only
extern CHAR __rodata_end;  // ROM end address
extern CHAR __bss_start;  // BSS start address
extern CHAR __bss_end;   // BSS end address
extern CHAR __text_start;  // Start address of code area
extern CHAR __text_end;   // End address of code area
extern CHAR __ram_data_start; // RAM start address readable and writable
extern CHAR __ram_data_end;  // RAM end address
extern UINT32 __heap_start;  // Start address of heap area
extern UINT32 __heap_end;  // Heap end address

Copy the code

Once memory starts out as a blank sheet of paper, these extern things are what defines it, what segments it goes from there to there. These values depend on the size of the actual project memory, different memory, the address must be different, so it must be provided externally, the Hongmun kernel uses Linux segment management mode. Understand the position with the illustration above and the explanation below.

A BSS segment is usually an area of memory used to store uninitialized global variables in a program. BSS is short for Block Started by Symbol. The BSS segment is a static memory allocation. This section is used to store uninitialized global variables or global variables initialized to 0 by default. It does not occupy the size of the program file, but occupies the memory space of the program when it is running.

The data segment is used to store initialized global variables. Global variables initialized to 0 are stored in the BSS segment for compilation optimization purposes.

Careful readers may have noticed that almost all global variables in the kernel are not initialized or NULL. These variables are compiled in the BSS section, which takes up memory space at runtime and makes the compiled ELF package smaller.

The.rodata segment, also called the constant area, is used to store constant data. Ro stands for Read Only.

The text segment is used to store program code, determined at compile time, and is read-only. Further, it is the machine instructions that store the processor. When each source file is compiled separately, the object file is generated, the various object files are linked through the connector and the function reference between the various source files is solved. At the same time, the.text segment in all the object files is combined.

The stack segment is used to store parameter variables and local variables as well as the execution of functions.

The heap segment is applied for and released by the user. When applied, virtual storage is allocated at least. When data is actually stored, the corresponding real storage is allocated.

How is kernel space initialized?

LosMux g_vmSpaceListMux;// Virtual space mutex, usually used with g_vmSpaceList
LOS_DL_LIST_HEAD(g_vmSpaceList);G_vmSpaceList hangs all virtual Spaces together,
LosVmSpace g_kVmSpace;    // Kernel space address
LosVmSpace g_vMallocSpace;// Assign virtual space address

// The kernel process space and the kernel dynamic allocation space share the same L1 page table
VOID OsKSpaceInit(VOID)
{
    OsVmMapInit(a);// Initialize the mutex
    OsKernVmSpaceInit(& g_kVmSpace,OsGFirstTableGet());// initialize the kernel virtual space with OsGFirstTableGet as L1 base address
    OsVMallocSpaceInit(& g_vMallocSpace,OsGFirstTableGet());OsGFirstTableGet specifies the base address of the L1 table
}G_kVmSpace g_vMallocSpace shares one L1 page table
// Initialize the kernel heap space
STATUS_T OsKHeapInit(size_t size)
{
    STATUS_T ret;
    VOID *ptr = NULL;
    /* * roundup to MB aligned in order to set kernel attributes. Kernel text/code/data attributes * should page mapping,  remaining region should section mapping. so the boundary should be * MB aligned. */
     // Round up to MB alignment to set kernel properties. Kernel text/code/data attributes should be page maps and the rest of the area should be segment maps, so the boundaries should be aligned.
    UINTPTR end = ROUNDUP(g_vmBootMemBase + size, MB);// M is used because section mapping is used in kernel source code analysis
    size = end - g_vmBootMemBase;
    //ROUNDUP(0x00000200+ 512,1024) = 1024 ROUNDUP(0x00000201+ 512,1024) = 2048
    ptr = OsVmBootMemAlloc(size);// The boot allocator is used for allocation
    if(! ptr) {PRINT_ERR("vmm_kheap_init boot_alloc_mem failed! %d\n", size);return - 1;
    }

    m_aucSysMem0 = m_aucSysMem1 = ptr;Auc = auC; // AuC = auC;
    ret = LOS_MemInit(m_aucSysMem0, size);// Initialize the memory pool
    if(ret ! = LOS_OK) {PRINT_ERR("vmm_kheap_init LOS_MemInit failed! \n");
        g_vmBootMemBase -= size;G_vmBootMemBase returns size if the assignment fails
        return ret;
    }
    LOS_MemExpandEnable(OS_SYS_MEM_ADDR);// The address is extensible
    return LOS_OK;
}

Copy the code

Kernel space uses three global variables, one of which is mutually exclusive LosMux, which will be covered in the IPC section but not expanded here. More interesting is LOS_DL_LIST_HEAD, look at the kernel source process often nod praise for such code, knowing a smile. Thumb up!

#defineLOS_DL_LIST_HEAD(list) LOS_DL_LIST list = {&(list), &(list)}
Copy the code

How is the Page initialized?

Page is the smallest unit of mapping and the basis of the data structure of physical address mapping

// page initialization
VOID OsVmPageStartup(VOID)
{
    struct VmPhysSeg *seg = NULL;
    LosVmPage *page = NULL;
    paddr_t pa;
    UINT32 nPage;
    INT32 segID;

    OsVmPhysAreaSizeAdjust(ROUNDUP((g_vmBootMemBase - KERNEL_ASPACE_BASE), PAGE_SIZE));// Calibrate g_physArea size

    nPage = OsVmPhysPageNumGet(a);// Get the total pages of g_physArea
    g_vmPageArraySize = nPage * sizeof(LosVmPage);// Total page table size
    g_vmPageArray = (LosVmPage *)OsVmBootMemAlloc(g_vmPageArraySize);// Apply page table storage area

    OsVmPhysAreaSizeAdjust(ROUNDUP(g_vmPageArraySize PAGE_SIZE));/ / g_physArea smaller

    OsVmPhysSegAdd(a);// Segment page binding
    OsVmPhysInit(a);// Add the free list and set the replacement algorithm, LRU(most recently unused) algorithm

    for (segID = 0; segID < g_vmPhysSegNum; segID++) {
        seg = &g_vmPhysSeg[segID];
        nPage = seg->size >> PAGE_SHIFT;
        for (page = seg->pageBase, pa = seg->start; page <= seg->pageBase + nPage;
             page++, pa += PAGE_SIZE) {
            OsVmPageInit(page, pa, segID);/ / page initialization
        }
        OsVmPageOrderListInit(seg - > pageBase nPage);// Sort the page allocation}}Copy the code

How does a process allocate memory?

The main body of a process is allocated from the process pool, and the process pool is uniformly allocated. How to create a process pool is described in this series. Therefore, when creating a process, you only need to allocate virtual memory LosVmSpace.

// Initializes user space or kernel space for the process
// Initialize the PCB block
STATIC UINT32 OsInitPCB(LosProcessCB *processCB, UINT32 Mode, UINT16 priority, UINT16 policy,const CHAR *name)
{
    UINT32 count;
    LosVmSpace *space = NULL;
    LosVmPage *vmPage = NULL;
    status_t status;
    BOOL retVal = FALSE;

    processCB->processMode = mode;// User process or kernel process
    processCB->processStatus = OS_PROCESS_STATUS_INIT;// The initial state of the process
    processCB->parentProcessID = OS_INVALID_VALUE;// Dad process, externally specified
    processCB->threadGroupID = OS_INVALID_VALUE;// Belongs to a thread group
    processCB->priority = priority;/ / priority
    processCB->policy = policy;// Scheduling algorithm LOS_SCHED_RR
    processCB->umask = OS_PROCESS_DEFAULT_UMASK;/ / mask
    processCB->timerID = (timer_t)(UINTPTR)MAX_INVALID_TIMER_VID;

    LOS_ListInit(&processCB->threadSiblingList);// Initializes the task/thread list
    LOS_ListInit(&processCB->childrenList);        // Initialize the child list
    LOS_ListInit(&processCB->exitChildList);    // Initializes the linked list of which children have dropped out
    LOS_ListInit(&(processCB->waitList));        // Initialize the wait list

    for (count = 0; count < OS_PRIORITY_QUEUE_NUM; ++count) { // Create queues based on the number of priorities
        LOS_ListInit(&processCB->threadPriQueueList[count]);  
    }

    if (OsProcessIsUserMode(processCB)) {// Whether the process is in user mode
        space = LOS_MemAlloc(m_aucSysMem0,sizeof(LosVmSpace));
        if (space == NULL) {
            PRINT_ERR("%s %d, alloc space failed\n", __FUNCTION__ __LINE__);return LOS_ENOMEM;
        }
        VADDR_T *ttb = LOS_PhysPagesAllocContiguous(1);// Allocate a physical page to store the L1 table.
        if (ttb == NULL) {// Get the physical page TTB directly
            PRINT_ERR("%s %d, alloc ttb or space failed\n", __FUNCTION__ __LINE__); (VOID)LOS_MemFree(m_aucSysMem0, space);
            return LOS_ENOMEM;
        }
        (VOID)memset_s(TTB PAGE_SIZE,0The PAGE_SIZE); retVal =OsUserVmSpaceInit(space, ttb);// Initialize the virtual space and the mmU process
        vmPage = OsVmVaddrToPage(ttb);// Get the page from the virtual address
        if ((retVal == FALSE) || (vmPage == NULL)) {// Exception handling
            PRINT_ERR("create space failed! ret: %d, vmPage: %#x\n", retVal vmPage); processCB->processStatus = OS_PROCESS_FLAG_UNUSED;// The process is unused and clean
            (VOID)LOS_MemFree(m_aucSysMem0, space);// Free virtual space
            LOS_PhysPagesFreeContiguous(TTB,1);// Free physical page, 4K
            return LOS_EAGAIN;
        }
        processCB->vmSpace = space;// Set it to process virtual space
        LOS_ListAdd(& processCB - > vmSpace - > archMmu. PtList, & (vmPage - > node));// Attach the space mapping page table to the mmU L1 page table of the space. L1 is the header of the table
    } else {
        processCB->vmSpace = LOS_GetKVmSpace(a);// The kernel shares a virtual space, and kernel processes reside in memory
    }

#ifdef LOSCFG_SECURITY_VID
    status = VidMapListInit(processCB);
    if(status ! = LOS_OK) {PRINT_ERR("VidMapListInit failed! \n");
        return LOS_ENOMEM;
    }
#endif
#ifdef LOSCFG_SECURITY_CAPABILITY
    OsInitCapability(processCB);
#endif

    if (OsSetProcessName(processCB, name) != LOS_OK) {
        return LOS_ENOMEM;
    }

    return LOS_OK;
}
LosVmSpace *LOS_GetKVmSpace(VOID)
{
    return &g_kVmSpace;
}
Copy the code

As you can see from the code, the kernel space is fixed at only one g_kVmSpace, while the virtual memory space for each user process is independent. Please fine product!

How does a task allocate memory?

The main body of a task is from the process pool, and the task pool is uniformly assigned. The OsUserInitProcess function is used to see how the main() of the application is created and executed by the kernel.

// All user processes use the same user code segment descriptor and user data segment descriptor, which are __USER_CS and __USER_DS. That is, each process in user mode has the same value in its CS register and DS register. When any process or interrupt exception enters the kernel, the same kernel code segment descriptors and kernel data segment descriptors are used, which are __KERNEL_CS and __KERNEL_DS. It is important to remember that the kernel data segment is actually the kernel stack segment.
LITE_OS_SEC_TEXT_INIT UINT32 OsUserInitProcess(VOID)
{
    INT32 ret;
    UINT32 size;
    TSK_INIT_PARAM_S param = { 0 };
    VOID *stack = NULL;
    VOID *userText = NULL;
    CHAR *userInitTextStart = (CHAR *)&__user_init_entry;// Start position of code area, all processes
    CHAR *userInitBssStart = (CHAR *)&__user_init_bss;// Uninitialized data area (BSS). Change its value at run time
    CHAR *userInitEnd = (CHAR *)&__user_init_end;// End address
    UINT32 initBssSize = userInitEnd - userInitBssStart;
    UINT32 initSize = userInitEnd - userInitTextStart;

    LosProcessCB *processCB = OS_PCB_FROM_PID(g_userInitProcess);
    ret = OsProcessCreateInit(processCB OS_USER_MODE,"Init", OS_PROCESS_USERINIT_PRIORITY);// Initializes the user process, which will be the parent process of all applications
    if(ret ! = LOS_OK) {return ret;
    }

    userText = LOS_PhysPagesAllocContiguous(initSize >> PAGE_SHIFT);// Allocate contiguous physical pages
    if (userText == NULL) {
        ret = LOS_NOK;
        goto ERROR;
    }

    (VOID)memcpy_s(userText, initSize, (VOID *)&__user_init_load_addr, initSize);__user_init_load_addr -> userText
    ret = LOS_VaddrToPaddrMmap(processCB - > vmSpace, (VADDR_T) (UINTPTR) userInitTextStart,LOS_PaddrQuery(userText), initSize,  VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE | VM_MAP_REGION_FLAG_PERM_EXECUTE | VM_MAP_REGION_FLAG_PERM_USER);// Mapping between virtual addresses and physical addresses
    if (ret < 0) {
        goto ERROR;
    }

    (VOID)memset_s(VOID *)((UINTPTR)userText + userInitBssStart - userInitTextStart), initBssSize,0, initBssSize);// Clear all but the code segment

    stack = OsUserInitStackAlloc(g_userInitProcess, & size);// Initialize the stack area
    if (stack == NULL) {
        PRINTK("user init process malloc user stack failed! \n");
        ret = LOS_NOK;
        goto ERROR;
    }

    param.pfnTaskEntry = (TSK_ENTRY_FUNC)userInitTextStart;// Execution starts in the code area, where the application main function is located
    param.userParam.userSP = (UINTPTR)stack + size;// point to the bottom of the stack
    param.userParam.userMapBase = (UINTPTR)stack;/ / stack
    param.userParam.userMapSize = size;/ / stack size
    param.uwResved = OS_TASK_FLAG_PTHREAD_JOIN;// Joinable can be reclaimed and killed by other threads
    ret = OsUserInitProcessStart(g_userInitProcess, & param);// Create a task to run main
    if(ret ! = LOS_OK) { (VOID)OsUnMMap(processCB - > vmSpace, param. UserParam. UserMapBase, param. UserParam. UserMapSize);goto ERROR;
    }

    return LOS_OK;

ERROR:
    (VOID)LOS_PhysPagesFreeContiguous(userText, initSize >> PAGE_SHIFT);// Free physical memory blocks
    OsDeInitPCB(processCB);// Delete the PCB block
    return ret;
}
Copy the code

All user processes are forked by the init process. You can see that a task is created at the same time as the process. The entry function is the first instruction in the code area, which is the application main function. There are three stack sizes in hongmeng kernel, as follows

#define LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE SIZE(0x800)// A kernel process running in 2K kernel space
#define OS_USER_TASK_SYSCALL_SATCK_SIZE 0x3000 // User processes, tasks created by system calls running in 12K of kernel space
#define OS_USER_TASK_STACK_SIZE         0x100000// The user process is running in user space at 1M
Copy the code

Intensive reading of the kernel source code

Four code stores synchronous annotation kernel source code, >> view the Gitee repository

Analysis of 100 blogs. Dig deep into the core

Add comments to hongmeng kernel source code process, sort out the following article. Content based on the source code, often in life scene analogy as much as possible into the kernel knowledge of a scene, with a pictorial sense, easy to understand memory. It’s important to speak in a way that others can understand! The 100 blogs are by no means a bunch of ridiculously difficult concepts being put forward by Baidu. That’s not interesting. More hope to make the kernel become lifelike, feel more intimate. It’s hard, it’s hard, but there’s no turning back. 😛 and code bugs need to be constantly debug, there will be many mistakes and omissions in the article and annotation content, please forgive, but will be repeatedly amended, continuous update. Xx represents the number of modifications, refined, concise and comprehensive, and strive to create high-quality content.

Compile build The fundamental tools Loading operation Process management
Compile environment

The build process

Environment script

Build tools

Designed.the gn application

Ninja ninja

Two-way linked list

Bitmap management

In the stack way

The timer

Atomic operation

Time management

The ELF format

The ELF parsing

Static link

relocation

Process image

Process management

Process concept

Fork

Special process

Process recycling

Signal production

Signal consumption

Shell editor

Shell parsing

Process of communication Memory management Ins and outs Task management
spinlocks

The mutex

Process of communication

A semaphore

Incident control

The message queue

Memory allocation

Memory management

Memory assembly

The memory mapping

Rules of memory

Physical memory

Total directory

Scheduling the story

Main memory slave

The source code comments

Source structure

Static site

The clock task

Task scheduling

Task management

The scheduling queue

Scheduling mechanism

Thread concept

Concurrent parallel

The system calls

Task switching

The file system Hardware architecture
File concept

The file system

The index node

Mount the directory

Root file system

Character device

VFS

File handle

Pipeline file

Compilation basis

Assembly and the cords

Working mode

register

Anomaly over

Assembly summary

Interrupt switch

Interrupt concept

Interrupt management

HongMeng station | into a little bit every day, the original is not easy, welcome to reprint, please indicate the source.