精华内容
下载资源
问答
  • 提示:本文基于开源鸿蒙内核分析,官方源码【kernel_liteos_a】...有了上篇鸿蒙内核源码分析(内存概念篇)的基础,本篇讲内存管理部分,内核空间,用户空间划分,初始化等等,源码较多,关键处都加了注释。 目录 ...

    百万汉字注解 >> 精读鸿蒙源码,中文注解分析, 深挖地基工程,大脑永久记忆,四大码仓每日同步更新< gitee | github | csdn | coding >

    百篇博客分析 >> 故事说内核,问答式导读,生活式比喻,表格化说明,图形化展示,主流站点定期更新中< osc | 51cto | csdn | harmony >

    本文分析虚拟内存模块源码 详见:../kernel/base/vm 本篇源码超级多,很烧脑,但笔者关键处都加了注释。废话不多说,开始吧。

    初始化整个内存

    从main()跟踪可看内存部分初始化是在OsSysMemInit()中完成的。

    UINT32 OsSysMemInit(VOID)
    {
        STATUS_T ret;
    
        OsKSpaceInit();//内核空间初始化
    
        ret = OsKHeapInit(OS_KHEAP_BLOCK_SIZE);// 内核动态内存初始化 512K   
        if (ret != LOS_OK) {
            VM_ERR("OsKHeapInit fail");
            return LOS_NOK;
        }
    
        OsVmPageStartup();// page初始化
        OsInitMappingStartUp();// 映射初始化
    
        ret = ShmInit();// 共享内存初始化
        if (ret < 0) {
            VM_ERR("ShmInit fail");  
            return LOS_NOK;
        }
    
        return LOS_OK;
    }
    

    鸿蒙虚拟内存整体布局图

     

    // HarmonyOS 内核空间包含以下各段:
    extern CHAR __int_stack_start; // 运行系统函数栈的开始地址
    extern CHAR __rodata_start;  // ROM开始地址 只读
    extern CHAR __rodata_end;  // ROM结束地址
    extern CHAR __bss_start;  // bss开始地址
    extern CHAR __bss_end;   // bss结束地址
    extern CHAR __text_start;  // 代码区开始地址
    extern CHAR __text_end;   // 代码区结束地址
    extern CHAR __ram_data_start; // RAM开始地址 可读可写
    extern CHAR __ram_data_end;  // RAM结束地址
    extern UINT32 __heap_start;  // 堆区开始地址
    extern UINT32 __heap_end;  // 堆区结束地址
    

    内存一开始一张白纸,这些extern就是给它画大界线的,从哪到哪是属于什么段。这些值大小取决实际项目内存条的大小,不同的内存条,地址肯定会不一样,所以必须由外部提供,鸿蒙内核采用了Linux的段管理方式。结合上图对比以下的解释自行理解下位置。

    BSS段 (bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。该段用于存储未初始化的全局变量或者是默认初始化为0的全局变量,它不占用程序文件的大小,但是占用程序运行时的内存空间。

    data段 该段用于存储初始化的全局变量,初始化为0的全局变量出于编译优化的策略还是被保存在BSS段。

    细心的读者可能发现了,鸿蒙内核几乎所有的全局变量都没有赋初始化值或NULL,这些变量经过编译后是放在了BSS段的,运行时占用内存空间,如此编译出来的ELF包就变小了。

    .rodata段,该段也叫常量区,用于存放常量数据,ro就是Read Only之意。

    text段 是用于存放程序代码的,编译时确定,只读。更进一步讲是存放处理器的机器指令,当各个源文件单独编译之后生成目标文件,经连接器链接各个目标文件并解决各个源文件之间函数的引用,与此同时,还得将所有目标文件中的.text段合在一起。

    stack栈段,是由系统负责申请释放,用于存储参数变量及局部变量以及函数的执行。

    heap段 它由用户申请和释放,申请时至少分配虚存,当真正存储数据时才分配相应的实存,释放时也并非立即释放实存,而是可能被重复利用。

    内核空间是怎么初始化的?

    LosMux g_vmSpaceListMux;//虚拟空间互斥锁,一般和g_vmSpaceList配套使用
    LOS_DL_LIST_HEAD(g_vmSpaceList);//g_vmSpaceList把所有虚拟空间挂在一起,
    LosVmSpace g_kVmSpace;    //内核空间地址
    LosVmSpace g_vMallocSpace;//虚拟分配空间地址
    
    //鸿蒙内核空间有两个(内核进程空间和内核动态分配空间),共用一张L1页表
    VOID OsKSpaceInit(VOID)
    {
        OsVmMapInit();// 初始化互斥量
        OsKernVmSpaceInit(&g_kVmSpace, OsGFirstTableGet());// 初始化内核虚拟空间,OsGFirstTableGet 为L1表基地址
        OsVMallocSpaceInit(&g_vMallocSpace, OsGFirstTableGet());// 初始化动态分配区虚拟空间,OsGFirstTableGet 为L1表基地址
    }//g_kVmSpace g_vMallocSpace 共用一个L1页表
    //初始化内核堆空间
    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.
         */
         //向上舍入到MB对齐是为了设置内核属性。内核文本/代码/数据属性应该是页映射,其余区域应该是段映射,所以边界应该对齐。
        UINTPTR end = ROUNDUP(g_vmBootMemBase + size, MB);//用M是因为采用section mapping 鸿蒙内核源码分析(内存映射篇)有阐述
        size = end - g_vmBootMemBase;
        //ROUNDUP(0x00000200+512,1024) = 1024  ROUNDUP(0x00000201+512,1024) = 2048 此处需细品! 
        ptr = OsVmBootMemAlloc(size);//因刚开机,使用引导分配器分配
        if (!ptr) {
            PRINT_ERR("vmm_kheap_init boot_alloc_mem failed! %d\n", size);
            return -1;
        }
    
        m_aucSysMem0 = m_aucSysMem1 = ptr;//内存池基地址,取名auc还用0和1来标识有何深意,一直没整明白, 哪位大神能告诉下?
        ret = LOS_MemInit(m_aucSysMem0, size);//初始化内存池
        if (ret != LOS_OK) {
            PRINT_ERR("vmm_kheap_init LOS_MemInit failed!\n");
            g_vmBootMemBase -= size;//分配失败时需归还size, g_vmBootMemBase是很野蛮粗暴的
            return ret;
        }
        LOS_MemExpandEnable(OS_SYS_MEM_ADDR);//地址可扩展
        return LOS_OK;
    }
    

    内核空间用了三个全局变量,其中一个是互斥LosMux,IPC部分会详细讲,这里先不展开。 比较有意思的是LOS_DL_LIST_HEAD,看内核源码过程中经常会为这样的代码点头称赞,会心一笑。点赞!

    #define LOS_DL_LIST_HEAD(list) LOS_DL_LIST list = { &(list), &(list) }
    

    Page是如何初始化的?

    page是映射的最小单位,是物理地址<--->虚拟地址映射的数据结构的基础

    // page初始化
    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));//校正 g_physArea size
    
        nPage = OsVmPhysPageNumGet();//得到 g_physArea 总页数
        g_vmPageArraySize = nPage * sizeof(LosVmPage);//页表总大小
        g_vmPageArray = (LosVmPage *)OsVmBootMemAlloc(g_vmPageArraySize);//申请页表存放区域
    
        OsVmPhysAreaSizeAdjust(ROUNDUP(g_vmPageArraySize, PAGE_SIZE));// g_physArea 变小
    
        OsVmPhysSegAdd();// 段页绑定
        OsVmPhysInit();// 加入空闲链表和设置置换算法,LRU(最近最久未使用)算法
    
        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初始化
            }
            OsVmPageOrderListInit(seg->pageBase, nPage);// 页面分配的排序
        }
    }
    

    进程是如何申请内存的?

    进程的主体是来自进程池,进程池是统一分配的,怎么创建进程池的去翻系列篇里的文章,所以创建一个进程的时候只需要分配虚拟内存LosVmSpace,这里要分内核模式和用户模式下的申请。

    //初始化进程的 用户空间 或 内核空间
    //初始化PCB块
    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;//用户态进程还是内核态进程
        processCB->processStatus = OS_PROCESS_STATUS_INIT;//进程初始状态
        processCB->parentProcessID = OS_INVALID_VALUE;//爸爸进程,外面指定
        processCB->threadGroupID = OS_INVALID_VALUE;//所属线程组
        processCB->priority = priority;//优先级
        processCB->policy = policy;//调度算法 LOS_SCHED_RR
        processCB->umask = OS_PROCESS_DEFAULT_UMASK;//掩码
        processCB->timerID = (timer_t)(UINTPTR)MAX_INVALID_TIMER_VID;
    
        LOS_ListInit(&processCB->threadSiblingList);//初始化任务/线程链表
        LOS_ListInit(&processCB->childrenList);        //初始化孩子链表
        LOS_ListInit(&processCB->exitChildList);    //初始化记录哪些孩子退出了的链表    
        LOS_ListInit(&(processCB->waitList));        //初始化等待链表
    
        for (count = 0; count < OS_PRIORITY_QUEUE_NUM; ++count) { //根据 priority数 创建对应个数的队列
            LOS_ListInit(&processCB->threadPriQueueList[count]);  
        }
    
        if (OsProcessIsUserMode(processCB)) {// 是否为用户态进程
            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);//分配一个物理页用于存储L1页表 4G虚拟内存分成 (4096*1M)
            if (ttb == NULL) {//这里直接获取物理页ttb
                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, 0, PAGE_SIZE);
            retVal = OsUserVmSpaceInit(space, ttb);//初始化虚拟空间和本进程 mmu
            vmPage = OsVmVaddrToPage(ttb);//通过虚拟地址拿到page
            if ((retVal == FALSE) || (vmPage == NULL)) {//异常处理
                PRINT_ERR("create space failed! ret: %d, vmPage: %#x\n", retVal, vmPage);
                processCB->processStatus = OS_PROCESS_FLAG_UNUSED;//进程未使用,干净
                (VOID)LOS_MemFree(m_aucSysMem0, space);//释放虚拟空间
                LOS_PhysPagesFreeContiguous(ttb, 1);//释放物理页,4K
                return LOS_EAGAIN;
            }
            processCB->vmSpace = space;//设为进程虚拟空间
            LOS_ListAdd(&processCB->vmSpace->archMmu.ptList, &(vmPage->node));//将空间映射页表挂在 空间的mmu L1页表, L1为表头
        } else {
            processCB->vmSpace = LOS_GetKVmSpace();//内核共用一个虚拟空间,内核进程 常驻内存
        }
    
    #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;
    }
    

    从代码可以看出,内核空间固定只有一个g_kVmSpace,而每个用户进程的虚拟内存空间都是独立的。请细品!

    task是如何申请内存的?

    task的主体是来自进程池,task池是统一分配的,怎么创建task池的去翻系列篇里的文章。这里task只需要申请stack空间,还是直接上看源码吧,用OsUserInitProcess函数看应用程序的main() 是如何被内核创建任务和运行的。

    //所有的用户进程都是使用同一个用户代码段描述符和用户数据段描述符,它们是__USER_CS和__USER_DS,也就是每个进程处于用户态时,它们的CS寄存器和DS寄存器中的值是相同的。当任何进程或者中断异常进入内核后,都是使用相同的内核代码段描述符和内核数据段描述符,它们是__KERNEL_CS和__KERNEL_DS。这里要明确记得,内核数据段实际上就是内核态堆栈段。
    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;//代码区开始位置 ,所有进程
        CHAR *userInitBssStart = (CHAR *)&__user_init_bss;// 未初始化数据区(BSS)。在运行时改变其值
        CHAR *userInitEnd = (CHAR *)&__user_init_end;// 结束地址
        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);// 初始化用户进程,它将是所有应用程序的父进程
        if (ret != LOS_OK) {
            return ret;
        }
    
        userText = LOS_PhysPagesAllocContiguous(initSize >> PAGE_SHIFT);// 分配连续的物理页
        if (userText == NULL) {
            ret = LOS_NOK;
            goto ERROR;
        }
    
        (VOID)memcpy_s(userText, initSize, (VOID *)&__user_init_load_addr, initSize);// 安全copy 经加载器load的结果 __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);// 虚拟地址与物理地址的映射
        if (ret < 0) {
            goto ERROR;
        }
    
        (VOID)memset_s((VOID *)((UINTPTR)userText + userInitBssStart - userInitTextStart), initBssSize, 0, initBssSize);// 除了代码段,其余都清0
    
        stack = OsUserInitStackAlloc(g_userInitProcess, &size);// 初始化堆栈区
        if (stack == NULL) {
            PRINTK("user init process malloc user stack failed!\n");
            ret = LOS_NOK;
            goto ERROR;
        }
    
        param.pfnTaskEntry = (TSK_ENTRY_FUNC)userInitTextStart;// 从代码区开始执行,也就是应用程序main 函数的位置
        param.userParam.userSP = (UINTPTR)stack + size;// 指向栈底
        param.userParam.userMapBase = (UINTPTR)stack;// 栈顶
        param.userParam.userMapSize = size;// 栈大小
        param.uwResved = OS_TASK_FLAG_PTHREAD_JOIN;// 可结合的(joinable)能够被其他线程收回其资源和杀死
        ret = OsUserInitProcessStart(g_userInitProcess, &param);// 创建一个任务,来运行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);//释放物理内存块
        OsDeInitPCB(processCB);//删除PCB块
        return ret;
    }
    

    所有的用户进程都是通过init进程 fork来的, 可以看到创建进程的同时创建了一个task, 入口函数就是代码区的第一条指令,也就是应用程序 main函数。这里再说下stack的大小,不同空间下的task栈空间是不一样的,鸿蒙内核中有三种栈空间size,如下

    #define LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE SIZE(0x800)//内核进程,运行在内核空间2K
    #define OS_USER_TASK_SYSCALL_SATCK_SIZE 0x3000 //用户进程,通过系统调用创建的task运行在内核空间的 12K
    #define OS_USER_TASK_STACK_SIZE         0x100000//用户进程运行在用户空间的1M
    

    鸿蒙源码百篇博客 往期回顾

    在给 鸿蒙内核源码加中文注释 过程中,整理出以下文章.内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感.百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思.更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了.😛

    写文章比写代码累多了,越深入研究,越觉得没写好,所以文章和注解会反复修正, .xx代表修改的次数, 将持续完善源码注解和文档内容,精雕细琢,言简意赅, 尽全力打磨精品内容.

    进入 >> osc | csdn | 51cto | 掘金 | 公众号 | 头条号 | gitee | github

    各大站点搜 "鸿蒙内核源码分析" .原创不易,欢迎转载,但请注明出处.

    热爱是所有的理由和答案 - turing

    展开全文
  • cell的循环利用

    2016-03-22 15:46:02
    // 离开屏幕的cell会怎样/** * 每当有一个cell进入视野范围内,就会调用 */ - (UITableViewCell )tableView:(UITableView )tableView ... // static修饰局部变量:可以保证局部变量只分配一次存储空间(只初始化一次

    这里写图片描述
    // 离开屏幕的cell会怎样

    /**
    * 每当有一个cell进入视野范围内,就会调用
    */
    - (UITableViewCell )tableView:(UITableView )tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    // static修饰局部变量:可以保证局部变量只分配一次存储空间(只初始化一次)
    static NSString *ID = @”hero”;

    // 1.通过一个标识去缓存池中寻找可循环利用的cell
    // dequeue : 出列 (查找)
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    
    // 2.如果没有可循环利用的cell
    if (cell == nil){
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
    

    // NSLog(@”——缓存池找不到cell–%d”, indexPath.row);
    }

    // 3.给cell设置新的数据
    // 取出模型
    MJHero *hero = self.heros[indexPath.row];
    
    // 设置cell的数据
    cell.textLabel.text = hero.name;
    cell.detailTextLabel.text = hero.intro;
    cell.imageView.image = [UIImage imageNamed:hero.icon];
    
    return cell;
    

    }

    展开全文
  • 操作系统想要允许程序时,最先要做的事情就是将程序的代码段和数据段(比如初始化过的变量)载入进程的内存和地址空间。程序最初是以可执行格式存储在磁盘(或者在现代操作系统中还有基于闪存的固态硬盘)上的。因此...

    4.3进程创建:更多的细节

    首先我们要揭秘的是,程序是怎样转换为进程的。尤其是,操作系统怎么得到一个程序并将之运行?进程创建实际上是怎么作用的?

    操作系统想要允许程序时,最先要做的事情就是将程序的代码段和数据段(比如初始化过的变量)载入进程的内存和地址空间。程序最初是以可执行格式存储在磁盘(或者在现代操作系统中还有基于闪存的固态硬盘)上的。因此,载入程序和静态数据进入内存的进程需要操作系统从磁盘读取数据并将它们存放在内存中的某处(见图4.1,图被本宝放上一节去了,大家可以顺着本宝宝的文章找过去,不过不看也没事,这张图百度哪哪都有)。

    在早期简单的操作系统中,装载进程很紧急,即在程序运行之前一次性全部完成;现代操作系统执行该进程就很慵懒了,在程序执行过程中只载入需要用的代码片段和数据。为了真正了解载入代码和数据片段有多懒,你需要了解机器中的分页机制和调度机制,这个专题我们将放在在后续有关内存虚拟化的讨论中。

    现在只要记得,在运行任何程序之前,操作系统必须做一些工作来从磁盘获取重要的程序数据放入内存。

          一旦代码段和静态数据被装入内存以后,操作系统在启动进程之前还需要做一些其他的事情。一部分的内存空间需要分配给程序的运行时堆栈(或者也就是堆栈)。或许你应该已经知道,C语言程序将堆栈用来存放局部变量,功能参数以及返回地址;操作系统将内存进行划分并将之交给进程。操作系统似乎也初始化了带参的堆栈;尤其是它会将这些参数传入main()函数中,即argc和argv数组。

    {

    ……常见int main(int argc,char **argv);

    这是本宝宝的常用写法,

    argc表示参数个数,

    argv数组中存放的是参数的具体内容 ,

    argc可用在main函数中作为参数个数的检查哦。

    }   

          操作系统也会分配一些内存空间给程序的堆,在C语言中,堆空间用于需求明确的动态分配空间的数据;程序通过调用malloc()函数来申请一块大小明确的空间,然后通过调用free()函数再准确的释放掉该空间。堆空间通常被一些数据结构所使用,如链表,哈希表,树和其他一些很有趣的数据结构。首先堆空间会变小;随着程序的运行和malloc()库函数对空间的分配,操作系统可能会介入并分配更多的内存空间给进程来满足这些需求。

          操作系统还会做其他一些初始化的工作,尤其是I/O(input/output输入输出流,用于数据读取写入等)相关的 。比如,在UNIX系统中,每个默认进程都拥有三个文件描述符,标准输入(stdin),标准输出(stdout)和标准错误(stderr)。这些描述符使得程序轻松的从终端读取输入的数据并将输出结果打印到显示器。我们将在本书的第三部分,持续性的部分学习I/O,文件描述符等。

          通过载入代码和静态数据进入内存,通过创建和初始化栈,通过执行与I/O相关的操作这些步骤,操作系统最终为程序的执行做好铺垫了。至此,它还有最后一个任务,在程序入口处启动程序,也就是main()函数处。通过跳转至main()函数的路线(通过一个特殊的机制,将在下一章讨论 ),操作系统将对CPU的控制权交给一个新建的进程,因此,程序开始执行。                 

    (下一节,4.4进程状态,跪求大家多多指教,虽然操作系统相关的数据看了不少,但是本宝宝也是第一次翻译全英文的,不管有没有中文版)                                      

    展开全文
  • 进入BIOS的时候会有一个约定,就是我们读取第一段指令的地址是哪里,约定 PC = 16 * CS + IP,又因为是在此时系统处于实模式下,只有20位的地址空间,完成整个初始化后,BIOS会从磁盘的引导扇区中读取加载程序,这...

    前言

    前段时间,在忙学校的Zigbee作业,所以就没用继续操作系统的学习。

    操作系统的启动

    操作系统的启动流程是怎样的?BIOS是什么?

    启动流程

    在CPU上电的时候,CPU会读取内存中只读存储区中的程序,一般也称BIOS启动固件。
    在进入BIOS的时候会有一个约定,就是我们读取第一段指令的地址是哪里,约定 PC = 16 * CS + IP,又因为是在此时系统处于实模式下,只有20位的地址空间,完成整个初始化后,BIOS会从磁盘的引导扇区中读取加载程序,这个程序512字节,控制权到加载程序,加载程序将操作系统内核映像读入内存,交移控制权

    总结来说,CPU首先初始化,然后BIOS初始化,最后加载程序加载内核映像

    中断、异常、系统调用

    中断可以分为软中断和硬中断,软中断包括由异常或系统调用引起的中断,硬中断是又硬件引起的中断。
    异常是由指令错误或访问错误地址引起的,内核会同步处理异常。
    系统调用一般是由函数库调用。

    展开全文
  • (首页的SkyDriver公开文件夹中,可能需要用代理才能正常访问该空间——空间绝对稳定,不会丢失文件!) (最近工作重心不在SIP开发,SO本文档也没有机会更新,有技术问题也请尽量咨询他人,本人不一定能及时回复...
  • 进入工作拷贝的管理区 条目文件 原始拷贝和属性文件 使用API Apache可移植运行库 URL 和路径需求 使用 C 和 C++ 以外的语言 代码样例 9. Subversion 完全参考 Subversion 命令行客户端:svn svn选项 svn子命令 ...
  • SVN使用手册中文版.chm

    热门讨论 2011-02-28 10:38:55
    进入工作拷贝的管理区 条目文件 原始拷贝和属性文件 WebDAV 使用内存池编程 为Subversion做贡献 加入社区 取得源代码 开始熟悉社区政策 作出修改并测试 贡献你的修改 9. Subversion完全参考 Subversion命令行客户端...
  • 1.2.5 初始化参数选择设定 10 1.2.6 文件存储及创建选项 11 1.2.7 告警日志及跟踪文件 12 1.3 数据库创建的脚本 13 1.3.1 数据库创建脚本 13 1.3.2 创建的起点 14 1.3.3 ORADIM工具的使用 14 1.3.4 ...
  • 易学C++,C++入门

    2009-12-06 14:30:11
    书中常以形象的比喻来解释程序设计中的概念,通俗易懂,令读者印象深刻,更快地进入C++程序设计的大门。本书的内容涵盖了绝大部分常用的C++知识,可以作为大学计算机专业或非计算机专业的程序设计入门教材,也可供...
  • 这本入门书可以告诉这些初学者,整个C++世界有哪些内容构成,应该按照怎样的路径进行学习探索,用C++进行程序设计应该具有怎样的设计思想,同时,又如何在实践中运用这些学到的知识。可以说,这本书基本上囊括了每个...
  • Windows系统进程详解

    2010-06-21 12:18:32
    如果授权是成功的,lsass就会产生用户的进入令牌,令牌别使用启动初始的shell。其他的由用户初始化的进程会继承这个令牌的。 svchost.exe 包含很多系统服务 !!!->eventsystem,(SPOOLSV.EXE 将文件加载到内存中以便...
  •  26.4 按成员赋值(memberwise assignment)和初始化 303  26.5 异常处理 305  26.6 小结 306  第五篇 技术  第27章 自己跟踪自己的类 309  27.1 设计一个跟踪类 309  27.2 创建死代码 312  27.3...
  • 26.4 按成员赋值(memberwise assignment)和初始化 303 26.5 异常处理 305 26.6 小结 306 第五篇 技术 第27章 自己跟踪自己的类 309 27.1 设计一个跟踪类 309 27.2 创建死代码 312 27.3 生成对象的审计...
  • C#微软培训教材(高清PDF)

    千次下载 热门讨论 2009-07-30 08:51:17
    附录 C .Net 名字空间成员速查.269 参 考 资 料 .300 <<page 4>> page begin==================== 第一部分 C#语言概述 第一章 第一章第一章 第一章 .NET 编程语 编程编程 编程 言 语言语言...
  • C#微软培训资料

    2014-01-22 14:10:17
    附录 C .Net 名字空间成员速查.269 参 考 资 料 .300 <<page 4>> page begin==================== 第一部分 C#语言概述 第一章 第一章第一章 第一章 .NET 编程语 编程编程 编程 言 语言语言...
  • LINUX 24学时教程

    2011-10-21 18:33:38
    1.7 怎样计划安装过程 7 1.7.1 从CD - ROM光盘启动安装 8 1.7.2 从软盘启动安装 8 1.7.3 其他方法 9 1.8 计划开机启动过程 9 1.9 准备硬盘驱动器 10 1.10 课时小结 13 1.11 专家答疑 13 1.12 练习题 13 第2学时 安装...
  • C#浏览器编程,学习使用

    热门讨论 2010-08-04 10:04:05
    浏览器代码设计主要是确定对上述的设计的功能在代码上怎样实现,包括选用的程序语言和编程平台选择、程序代码的编写、程序代码的调试、程序功能的测试等等。 4.4 浏览器编程设计平台(老师讲解2学时,学生理解4学时...
  • windowsnt 技术内幕

    2014-04-09 20:47:17
    Domain Admins(域管理员)组的详细说明 赋予拨号进入权限 理解用户配置文件(User Profile) 为Windows用户创建并使用登录脚本文件(Logon Script) 创建漫游式用户配置文件(Roaming User Profile) 创建强制性用户配置...
  • Tcl_TK编程权威指南pdf

    热门讨论 2011-03-25 09:30:55
    不管怎样,现在发现包含几千行Tcl脚本的应用程序并不稀奇。 我编写这本书的原因就是,虽然自己觉得使用Tcl与Tk既有乐趣又高效,但是也有令人头痛的时候。此外,在Xerox PARC工作,那里有许多语言和系统上的专家,我...
  • PT80-NEAT开发指南v1.1

    2014-06-24 18:38:34
    V 1.0 初始版本。 2012-04-12 V1.1 修改前三章内容 2012-09-25 目录 第一章 关于本手册..........................................................................................................................
  • 101数据空间的三种不同使用方法和 管理方法217 1011静态存储分配218 1012动态存储分配219 1013栈式动态存储分配219 1014堆式动态存储分配219 102栈式存储分配的实现220 1021简单的栈式存储...
  • c语言编写单片机技巧

    2009-04-19 12:15:17
    MCU来说,其内部ROM、RAM、STACK等资源都有限,如果使用C语言编写,一条C语言指令编译后,会变成很多条机器码,很容易出现ROM空间不够、堆栈溢出等问题。而且一些单片机厂家也不一定能提供C编译器。而汇编语言,一...
  • Reversing:逆向工程揭密

    热门讨论 2010-06-21 17:27:22
    3.2.4 内核内存空间 75 3.2.5 区段对象 77 3.2.6 VAD树 78 3.2.7 用户模式的内存分配 78 3.2.8 内存管理API 79 3.3 对象与句柄 80 命名对象 81 3.4 进程与线程 83 3.4.1 进程 84 3.4.2 线程 84 3.4.3 运行状态切换 ...

空空如也

空空如也

1 2 3
收藏数 41
精华内容 16
关键字:

怎样进入初始空间