文章来源:http://gliethttp.cublog.cn
在分析request_standard_resources前先来看linux对挂接在4G总线空间上的设备实体的管理方式-resource结构体
  一个独立的挂接在cpu总线上的设备单元,一般都需要一段线性的地址空间来描述设备自身,linux是怎么管理所有的这些外部"物理地址范围段",进而给用户和linux自身一个比较好的观察4G总线上挂接的一个个设备实体的简洁、统一级联视图的呢?
  linux采用struct resource结构体来描述一个挂接在cpu总线上的设备实体(32位cpu的总线地址范围是0~4G)
struct resource结构体定义在include/linux/ioport.h
struct resource {
    resource_size_t start;
    resource_size_t end;
    const char *name;
    unsigned long flags;
    struct resource *parent, *sibling, *child;
};
include/linux/types.h
typedef u32 phys_addr_t;
typedef phys_addr_t resource_size_t;

resource->name描述这个设备实体的名称,这个名字开发人员可以随意起,但最好贴切;
resource->start描述设备实体在cpu总线上的线性起始物理地址;
resource->end描述设备实体在cpu总线上的线性结尾物理地址;
resource->flag描述这个设备实体的一些共性和特性的标志位;
只需要了解一个设备实体的以上4项,linux就能够知晓这个挂接在cpu总线的上的设备实体的基本使用情况,也就是[resource->start,resource->end]这段物理地址现在是空闲着呢,还是被什么设备占用着呢?
  linux会坚决避免将一个已经被一个设备实体使用的总线物理地址区间段[resource->start,resource->end],再分配给另一个后来的也需要这个区间段或者区间段内部分地址的设备实体,进而避免设备之间出现对同一总线物理地址段的重复引用,而造成对唯一物理地址的设备实体二义性.
  以上的4个属性仅仅用来描述一个设备实体自身,或者是设备实体可以用来自治的单元,但是这不是linux所想的,linux需要管理4G物理总线的所有空间,所以挂接到总线上的形形色色的各种设备实体,这就需要链在一起,因此resource结构体提供了另外3个成员,
resource->parent描述管理本设备实体的父类resource指针
resource->sibling描述比本设备实体物理地址大的下一个设备实体的resource指针[这是一个单向链表]
resource->child描述本设备实体可能还要更详细的描述本设备实体内部的一些物理地址区段,来让linux能够更清楚本设备实体的内部的级联关系,甚至可以将本设备实体内部的具有特色的区段的给它七个名字标示一下
有这样一个保证:由来request_resource()函数提供这个保证的实现
resource->child是所有resource->sibling里边地址最小的,resource->sibling大之,resource->sibling大大之,以此类推,所以resource->sibling永远链接着比resource自己的resource->end地址值大的下一个设备实体.
比如:request_resource(&iomem_resource, res);将res设备实体挂接到iomem_resource设备实体上,request_resource()函数会会在[iomem_resource.start,iomem_resource.end]之间找到[res->.start,res->end](对于request_resource()函数的具体实现,我们后面分析)区间设备实体res是否可以占用,如果可以,那么将把res顺序加入到单向链表sibling的相应位置,如果已经被某个设备实体占用了,那么request_resource()将返回正在使用[res->.start,res->end]该段区间或部分区间的设备实体B的resource指针,进而res可以决定是否把自己作为B的孩子插到B的线性空间视图中.
  通过request_resource()函数,我们可以将打算占用[start,end]总线地址空间的设备实体,登记到供linux内核观察4G总线上设备占用情况的单向观察链表resource中,进而避免对已经被占用的总线地址空间段,再分配给另一个新来的设备实体.
  Linux把基于I/O映射方式的I/O端口和基于内存映射方式的I/O端口资源统称为“I/O区域”(I/O Region)。I/O Region仍然是一种I/O资源,因此它仍然可以用resource结构类型来描述。Linux是以一种倒置的树形结构来管理每一类I/O资源(如:I/O端口、外设内存、DMA和IRQ)的。每一类I/O资源都对应有一颗倒置的资源树,树中的每一个节点都是一个resource结构,而树的根结点root则描述了该类资源的整个资源空间。
如:struct resource iomem_resource = { "PCI mem", 0x00000000, 0xffffffff, IORESOURCE_MEM };
现在我们再来看看request_standard_resources函数,它定义在arch/arm/kernel/setup.c
static void __init request_standard_resources(struct meminfo *mi, struct machine_desc *mdesc)
{
    struct resource *res;
    int i;

kernel_code.start   = virt_to_phys(_text);  内核代码段开始
    kernel_code.end     = virt_to_phys(_etext - 1);
    kernel_data.start   = virt_to_phys(_data);  内核数据段开始
    kernel_data.end     = virt_to_phys(_end - 1);
    _text,_etext,_data,_end参见arch/arm/kernel/vmlinux.lds.S 链接脚本
    for (i = 0; i < mi->nr_banks; i++) {
        if (mi->bank[i].size == 0)
            continue;
         //总线上的所有memory
        res = alloc_bootmem_low(sizeof(*res));  //申请res存储空间
        res->name  = "System RAM";  //该设备实体的名称
        res->start = mi->bank[i].start; //该设备实体地址段
        res->end   = mi->bank[i].start + mi->bank[i].size - 1;
        res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;

request_resource(&iomem_resource, res); //将该设备实体登记注册到总线空间链表

if (kernel_code.start >= res->start &&
            kernel_code.end <= res->end)
//如果kernel_code在这个memory内,那么将kernel_code这个比较有特性的设备实体登记注册到res[该memory实体]设备实体下,来详细描述res内部地址空间中一些比较有特色的设备实体的分布情况,进而使linux能够给用户提供一个更加详细,充实的cpu外围总线设备的级联图解情况.
//res就像一个倒挂的树,res是树根,kernel_code是倒挂在res这个树根下的一根枝儿.
//                树根
//    .............................res..........................   
//    |                          |
//    |                          [..kernel_data..]//又生出来一根枝儿
//    [..kernel_code..]//生出来一根枝儿
            request_resource(res, &kernel_code); //将kernel_code设备实体登记注册到res空间
        if (kernel_data.start >= res->start &&
            kernel_data.end <= res->end)
            request_resource(res, &kernel_data); //将kernel_code设备实体登记注册到res空间
    }
//到这里所有总线上的外部内存都已经登记注册了,这些空间已经被告知linux---占用了.
    if (mdesc->video_start) { //将video,液晶之类的设备实体占用的总线地址空间段,进行登记注册.
        video_ram.start = mdesc->video_start;
        video_ram.end   = mdesc->video_end;
        request_resource(&iomem_resource, &video_ram);
    }

/*
     * Some machines don't have the possibility of ever
     * possessing lp0, lp1 or lp2
     */
//登记注册以下io口,这些io口在linux系统运行起来之后,只能由如下的设备使用,不能他用.
    if (mdesc->reserve_lp0)
        request_resource(&ioport_resource, &lp0);
    if (mdesc->reserve_lp1)
        request_resource(&ioport_resource, &lp1);
    if (mdesc->reserve_lp2)
        request_resource(&ioport_resource, &lp2);
}

request_resource()函数的具体实现,它定义在kernel/resource.c
int request_resource(struct resource *root, struct resource *new)
{
    struct resource *conflict;

conflict = request_resource_conflict(root, new);//调用request_resource_conflict
    return conflict ? -EBUSY : 0;
}
request_resource_conflict实现,同文件下
struct resource *request_resource_conflict(struct resource *root, struct resource *new)
{
    struct resource *conflict;

write_lock(&resource_lock);
    conflict = __request_resource(root, new);调用__request_resource
    write_unlock(&resource_lock);
    return conflict;
}
__request_resource实现,同文件下
static struct resource * __request_resource(struct resource *root, struct resource *new)
{
    resource_size_t start = new->start;
    resource_size_t end = new->end;
    struct resource *tmp, **p;

if (end < start)
        return root;
    if (start < root->start)
        return root;
    if (end > root->end)
        return root;
    p = &root->child;  //root下的第一个链表元素*p.[child链表是以I/O资源物理地址从低到高的顺序排列的]
    for (;;) {
        tmp = *p;
        if (!tmp || tmp->start > end) {
            new->sibling = tmp;
            *p = new;
/*可以从root->child=null开始我们的分析考虑,此时tmp=null,那么第一个申请将以!tmp条件满足而进入,这时root->child的值为new指针,new->sibling = tmp = null;当第二次申请发生时:如果tmp->start > end成立,那么,root->child的值为new指针,new->sibling = tmp;这样就链接上了,空间分布图如:child=[start,end]-->[tmp->start,tmp->end](1);如果条件tmp->start > end不成立,那么只能是!tmp条件进入那么,root->child的值不变,tmp->sibling = new;new->sibling = tmp = null这样就链接上了,空间分布图如:child=[child->start,child->end]-->[start,end](2);当第三次申请发生时:如果start在(2)中的[child->end,end]之间,那么tmp->end < start将成立,继而continue,此时tmp = (2)中的[start,end],因为tmp->start < end,所以继续执行p = &tmp->slibing = null,因为tmp->end > start,所以资源冲突,返回(2)中的[start,end]域综上的两个边界值情况和一个中间值情况的分析,可以知道代码实现了一个从地地址到高地址的顺序链表模型图:childe=[a,b]-->[c,d]-->[e,f],此时有一个[x,y]需要插入进去,tmp作为sibling指针游动tmp指向child=[a,b],tmp指向[a,b],当tmp->start>y时,插入后的链接图为:child=[x,y]-->[a,b]-->[c,d]-->[e,f]-->null;当tmp->end>=x时,冲突返回tmp tmp指向[c,d],当tmp->start>y时,插入后的链接图为:child=[a,b]-->[x,y]-->[c,d]-->[e,f]-->null;当tmp->end>=x时,冲突返回tmp,tmp指向[e,f],当tmp->start>y时,插入后的链接图为:child=[a,b]-->[c,d]-->[x,y]-->[e,f]-->null;当tmp->end>=x时,冲突返回tmp,tmp指向null,插入后的链接图为:child=[a,b]-->[c,d]-->[e,f]-->[x,y]-->null;顺利的达到了检测冲突,顺序链接的目的*/
            new->parent = root;
            return NULL;
        }
        p = &tmp->sibling;
        if (tmp->end < start)
            continue;
        return tmp;
    }
}
————————————————
版权声明:本文为CSDN博主「BoArmy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/boarmy/article/details/8652763

request_standard_resources相关推荐

  1. uboot linux内核传递参数,Uboot与Linux之间的参数传递详解

    原标题:Uboot与Linux之间的参数传递详解 U-boot会给Linux Kernel传递很多参数,如:串口,RAM,videofb等.而Linux kernel也会读取和处理这些参数.两者之间通 ...

  2. ARM linux的启动部分源代码简略分析

    ARM linux的启动部分源代码简略分析 以友善之臂的mini2440开发板为平台,以较新的内核linux-2.6.32.7版本为例,仅作说明之用. 当内核映像被加载到RAM之后,Bootloade ...

  3. 详解Linux2.6内核中基于platform机制的驱动模型 (经典)

    [摘要]本文以Linux 2.6.25 内核为例,分析了基于platform总线的驱动模型.首先介绍了Platform总线的基本概念,接着介绍了platform device和platform dri ...

  4. ARM的嵌入式Linux移植体验之操作系统

    转自:[url]http://dev.yesky.com/153/2527653.shtml[/url] 06-08-13 08:00 作者: 宋宝华 出处: 天极开发 责任编辑:方舟 在笔者撰写的& ...

  5. 详解Linux2.6内核中基于platform机制的驱动模型

    原文地址:详解Linux2.6内核中基于platform机制的驱动模型 作者:nacichan [摘要]本文以Linux 2.6.25 内核为例,分析了基于platform总线的驱动模型.首先介绍了P ...

  6. linux内存管理分析 二,linux内存管理分析【二】

    为建立内存管理系统,在内核初始化过程中调用了下面几个函数: init/main.c asmlinkage void __init start_kernel(void) { ...... 初始化持久映射 ...

  7. U-boot给kernel传参数和kernel读取参数—struct tag

    U-boot 会给 Linux Kernel 传递很多参数,如:串口, RAM , videofb 等. 而 Linux kernel 也会读取和处理这些参数.两者之间 通过 struct tag 来 ...

  8. Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】...

    原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.c ...

  9. Exynos4412 内核移植(六)—— 设备树解析

    一.描述 ARM Device Tree起源于OpenFirmware (OF),在过去的Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,相 ...

最新文章

  1. windows 生成 deploy key_推荐一个免费生成点线/方格/横线纸张的网站
  2. KubeSphere 使用外置 Jenkins
  3. Python-100 | 练习题 01 列表推导式
  4. sqoop导出数据单mysql_sqoop导出hive表数据到mysql
  5. 渗透测试入门25之一次完整的渗透测试实验
  6. STM32F429i-DISCO FreeRTOS keil STM32CubeMX
  7. java 睡眠_Java 线程和多线程执行过程分析
  8. 一名高校老师的观点:高考志愿该怎么填
  9. 制作自己的Maven镜像,上传Harbor镜像仓库
  10. 在ArrayLIst和LinkedList尾部加元素,谁的效率高
  11. 新西兰 计算机 转专业,新西兰留学后如何转学转专业?
  12. 一款基于tampermonkey的浏览器插件:聚合搜索
  13. 一文搞定细菌基因组De Novo测序分析
  14. 30、【backtrader股票策略】《151 trading strategies》中的支撑与阻力策略(support and resistance)
  15. 数码相机闪光灯存储卡挑选篇
  16. 如何开高效的需求评审会?
  17. iphone4安装使用微信
  18. Android常见的错误及解决
  19. SCOUT 薄膜分析软件
  20. 程序员需要知道一些网站

热门文章

  1. Linux直接在通过终端打开图片文件
  2. 最小二乘支持向量机(lssvm)回归预测(matlab)
  3. 英读廊——为什么说密码中加入特殊字符会更安全?
  4. Linux分区命令-parted
  5. adb 卸载android系统程序
  6. 朱会灿:搜索引擎演变史 视频及PPT放出 - 讲堂活动 - 腾讯大讲堂
  7. FastDB 很难得的一篇分析
  8. 软件工程-体系结构设计
  9. python数据可视化字段,Python数据可视化
  10. Jenkins查看凭据的密码