• linux源码分析(五)-start_kernel


    前置:这里使用的linux版本是4.8,x86体系。

    local_irq_disable();

    这个函数是做了关闭中断操作。和后面的local_irq_enable相对应。说明启动的下面函数是不允许被中断抢占的。这个函数追下去会发现下面的代码:

    static inline void native_irq_disable(void)
    {
        asm volatile("cli": : :"memory");
    }
    

    这个写法是linux的内联汇编写法。在C语言中写汇编语言。实际上调用的是汇编cli命令。cli命令是禁用中断功能。http://rock3.info/blog/2013/11/24/linux-c中调用汇编用法/

    接着start_kernel,linux关闭完中断之后,还使用了一个变量early_boot_irqs_disabled来标记已经关闭irq了。

    这里稍微说说irq的概念,我们把中断分为两个概念,一个是上半部,一个是下半部,上半部指的是硬件直接要求立即响应的中断。下半部指的是可以在某个特定时间之后执行的。这里的IRQ就是一个上半部概念。每个硬件设备都有一个irq线,通过这个线把中断描述符传递给CPU,CPU获取中断之后立即执行对应已经注册的操作。

    boot_cpu_init()

    这个函数功能是初始化第一个CPU。

    void __init boot_cpu_init(void)
    {
        int cpu = smp_processor_id();
    
        /* Mark the boot cpu "present", "online" etc for SMP and UP case */
        set_cpu_online(cpu, true);
        set_cpu_active(cpu, true);
        set_cpu_present(cpu, true);
        set_cpu_possible(cpu, true);
    }
    

    先获取cpu的id,在smp下,获取第一个处理器ip,非smp,第一个cpu的id为0。后面就是设置cpu的四个标志位。

    page_address_init()

    这个是页地址初始化操作。

    我们先要了解下段式管理和页式管理。我们会有三个地址,逻辑地址,虚拟地址,物理地址。CPU要将一个逻辑地址转换为物理地址,需要两步:首先CPU利用段式内存管理单元,先将逻辑地址转换为线性地址(虚拟地址)。再利用页式管理单元,把虚拟地址,转化为物理地址。

    形象理解,段式管理就是一个大大的内存按照目的分为几段,有的段比较大,有的段比较小。但是呢,每个段的最低地址位都是0,实际的地址位是段的偏移量。所以,这里就存在逻辑地址和虚拟地址的转换了。为什么要进行分段管理呢?进行分段管理,能使得我们有可能对不同的内存段赋予不同的权限管理。

    页式管理是一段内存,按照指定大小划分,每4k为一页。这里就有一个虚拟地址和物理地址的映射关系了。比如在一个三级的页式管理中,一个虚拟地址32位按照10,10,12分为3段,前10位是页目录地址,后10位是页表地址,最后12位是偏移量地址。这里其实有个奇怪的地方了。

    为什么要进行页式管理呢?不管是否有分页管理,一个32位的地址,最多指向的也就是4G内存空间。页式管理其实是为了更好地利用内存。比如假设内存是连续分配的,进程A获取了1~100的内存空间,进程B获取了100~104的内存空间,进程C获取了104~200的内存空间。现在进程B释放空间了,但是只有很小的4。这个时候,如果后续的进程要申请的空间都是大于4的,那么100~104这个内存空间段就永远没有办法被分配。而使用页式管理就有办法避免这个问题。它可以让程序使用的内存在逻辑上是连续的,在物理上是离散的。

    回到linux中,由于仅有一部分体系支持段式管理,基于兼容的原因吧,linux并没有支持段式管理。换句话说,linux把内存块当作是一个段。所以实际上,在linux中,逻辑地址和虚拟地址是一样的。但是linux把页式管理是全盘接受了。

    回到这个函数,page_address_init()

    查找这个函数的定义,你可以看到根据宏不同有两种定义,一种是

    #if !defined(HASHED_PAGE_VIRTUAL) && !defined(WANT_PAGE_VIRTUAL)
    #define page_address(page) lowmem_page_address(page)
    #define set_page_address(page, address)  do { } while(0)
    #define page_address_init()  do { } while(0)
    #endif
    

    一种是

    void __init page_address_init(void)
    {
        int i;
    
        for (i = 0; i < ARRAY_SIZE(page_address_htable); i++) {
            INIT_LIST_HEAD(&page_address_htable[i].lh);
            spin_lock_init(&page_address_htable[i].lock);
        }
    }
    
    #endif    /* defined(CONFIG_HIGHMEM) && !defined(WANT_PAGE_VIRTUAL) */
    

    首先,这里分配的页地址空间指的是内核的页地址空间。linux把4G的虚拟地址空间分为3G用户地址空间+1G内核地址空间。基本上是使用物理地址+3G的方式进行直接映射的。所以一般初始化内核地址空间的时候并不需要做任何操作。但是,当有些系统自身需要的内存大于896M的时候,就出现问题了。首先1G的内核空间,linux内核会把它分为896M的操作系统使用的空间,和128M的IO映射空间。当有些操作系统自身需要的内存大于896M,那么就难免有一些操作系统空间需要使用虚拟地址的形式,实际上是占用了原本用户的系统空间,这一部分空间就叫做high memory。(那么想对,原本已经有的896M的空间叫做low memory)。这一部分的空间映射,是保存在128M的IO映射空间里面的。http://blog.sina.com.cn/s/blog_6488248f0100wu6v.html

  • 相关阅读:
    [BZOJ]1854: [Scoi2010]游戏
    [BZOJ]3531: [Sdoi2014]旅行
    2017-3-30校内训练
    Codeforces Round #407 (Div. 1)
    [BZOJ]1064: [Noi2008]假面舞会
    Educational Codeforces Round 18
    [BZOJ]1503: [NOI2004]郁闷的出纳员
    [BZOJ]1758: [Wc2010]重建计划
    2017-3-26四校联考
    [BZOJ]4644: 经典傻逼题
  • 原文地址:https://www.cnblogs.com/yjf512/p/6008465.html
Copyright © 2020-2023  润新知