0 00:00:00,000 --> 00:00:06,520 1 00:00:06,560 --> 00:00:08,920 最后我们讲一下关于ucore编程里面 2 00:00:09,000 --> 00:00:11,320 常用的一些编程的方法 3 00:00:11,360 --> 00:00:15,160 让大家能够熟悉ucore编程的技巧 4 00:00:15,200 --> 00:00:16,800 这里面主要强调一些 5 00:00:16,840 --> 00:00:20,200 关于面向对象编程和通用数据结构的设计 6 00:00:20,240 --> 00:00:23,320 那么ucore虽然是基于C语言开发的 7 00:00:23,360 --> 00:00:25,400 但是它确实也产生了一些 8 00:00:25,440 --> 00:00:28,440 面向对象一些编程手段 比如说在这里面 9 00:00:28,480 --> 00:00:32,320 我们建立一个所谓物理内存的管理器 10 00:00:32,360 --> 00:00:33,800 那叫pmm_manager 11 00:00:33,840 --> 00:00:37,120 这个管理器它有很多一些函数的实现 12 00:00:37,160 --> 00:00:40,080 它的表示是用函数指针的方式来实现 13 00:00:40,120 --> 00:00:44,040 实际上对外暴露给需要访问物理内存管理器这些 14 00:00:44,080 --> 00:00:48,160 调动者一个统一的接口 这个接口不会改变 15 00:00:48,200 --> 00:00:51,400 但是你如果有不同的物理内存管理方法 16 00:00:51,440 --> 00:00:54,480 比如说有不同连续内存的分配方法 17 00:00:54,520 --> 00:00:57,960 那我们可以保持同样接口 但是它实现不一样 18 00:00:58,000 --> 00:01:01,360 这个带来所谓统一的interface 19 00:01:01,400 --> 00:01:03,680 但是它Details的实现不一样 20 00:01:03,720 --> 00:01:04,880 面向对象设计的原则 21 00:01:04,920 --> 00:01:06,840 我们来看看一般应用程序 22 00:01:06,880 --> 00:01:09,160 怎么来用双向循环链表的 23 00:01:09,200 --> 00:01:10,360 比如这么一个数据结构 24 00:01:10,400 --> 00:01:14,080 这个数据结构需要通过双向链表形式 25 00:01:14,120 --> 00:01:17,160 把它链接在一起 它有一个元素data 26 00:01:17,200 --> 00:01:21,160 同时它也有一个prev前一个指针和next下一个指针 27 00:01:21,200 --> 00:01:23,920 那么就形成了这么一个双向循环的链表 28 00:01:23,960 --> 00:01:27,440 当然这种方式确实在应用程序开发里面常见 29 00:01:27,480 --> 00:01:29,880 我们需要针对每一种特定的数据结构 30 00:01:29,920 --> 00:01:32,560 都要建立类似的数据结构 31 00:01:32,600 --> 00:01:36,120 这使得建立起来的数据结构不具有通用性 32 00:01:36,160 --> 00:01:38,280 我们有没有更好的办法 33 00:01:38,320 --> 00:01:42,080 我们以ucore双向链表结构来展示一下 34 00:01:42,120 --> 00:01:45,160 我们定义一个通用的双向链表比如叫list_entry 35 00:01:45,200 --> 00:01:49,400 这个也一样 你看到刚刚才类似Prev和next 36 00:01:49,440 --> 00:01:54,240 但是这个Prev和next并不是特定结构的指针 37 00:01:54,280 --> 00:02:00,240 它是一个通用结构指针 都是list_entry 38 00:02:00,280 --> 00:02:03,080 假设我们想建立这么一个双向链表结构 39 00:02:03,120 --> 00:02:08,840 它包含了free_area和page两种不同类型的strust 40 00:02:08,880 --> 00:02:12,800 那我怎么来用这种方式来建立呢 41 00:02:12,840 --> 00:02:18,080 我们先看free_area结构 free_area里面包含了两块 42 00:02:18,120 --> 00:02:22,080 一块是free_list这free_list就是一个list_entry 43 00:02:22,120 --> 00:02:24,840 第二个它中间的元素叫nr_free 44 00:02:24,880 --> 00:02:26,760 这是一个特定一个它的元素 45 00:02:26,800 --> 00:02:29,040 那么free_list就是这么一个数据结构 46 00:02:29,080 --> 00:02:31,720 就是list_entry_t这么一个结构 47 00:02:31,760 --> 00:02:34,480 你可以看到这里面就包含了两个指针 48 00:02:34,520 --> 00:02:39,640 那么怎么跟page连接在一起呢 我们来看看 49 00:02:39,680 --> 00:02:42,280 我们把page这个结构也是这么定义的 50 00:02:42,320 --> 00:02:45,480 它自己一些比如reference 51 00:02:45,520 --> 00:02:49,560 其它一些自己特定域在里面 成员变量在里面 52 00:02:49,600 --> 00:02:52,040 但是它最后一个page_link 53 00:02:52,080 --> 00:02:55,440 page_link也是一样也是list_entry数据结构 54 00:02:55,480 --> 00:02:58,280 有了这两个之后 这个page_link 55 00:02:58,320 --> 00:03:02,760 和free_list可以建立相应的链接关系 56 00:03:02,800 --> 00:03:07,480 就通过prev和next来建立双向链接关系 57 00:03:07,520 --> 00:03:11,920 这里面即满足了不同类型数据结构的 58 00:03:11,960 --> 00:03:16,200 它们特定一些成员变量一些表示 59 00:03:16,240 --> 00:03:19,800 而且在设计链接关系的时候 60 00:03:19,840 --> 00:03:22,200 用的是一种通用的一种结构来表示 61 00:03:22,240 --> 00:03:24,840 这是通用的双向链表的表示方式 62 00:03:24,880 --> 00:03:27,600 应该说在我们ucore里面大量存在 63 00:03:27,640 --> 00:03:29,080 这种方式大量存在 64 00:03:29,120 --> 00:03:30,920 有了这种方式之后我们可以更灵活 65 00:03:30,960 --> 00:03:34,040 更简洁来表示不同的资源 66 00:03:34,080 --> 00:03:35,400 以及资源之间的关系 67 00:03:35,440 --> 00:03:37,360 有了这个链表数据结构之后 68 00:03:37,400 --> 00:03:39,440 我们还有相应对它进行操作的函数 69 00:03:39,480 --> 00:03:42,880 这个函数的操作和我们通常链表操作差不多 70 00:03:42,920 --> 00:03:46,920 有初始化 有增加删除等等 71 00:03:46,960 --> 00:03:49,960 这都是不同函数的表示 72 00:03:50,000 --> 00:03:52,000 其实还需要很重要一点 73 00:03:52,040 --> 00:03:54,800 就是在于你怎么能够根据free_list的 74 00:03:54,840 --> 00:03:57,000 或者说根据刚才说的page_link 75 00:03:57,040 --> 00:03:59,520 它的结构能够找到它对应 76 00:03:59,560 --> 00:04:03,600 整个数据结构起始位置 77 00:04:03,680 --> 00:04:05,760 这个实际上有一定技巧 78 00:04:05,800 --> 00:04:08,920 在这里面我们可以看看一个例子 79 00:04:08,960 --> 00:04:13,760 怎么去访问链表的结点所在处的宿主数据结构 80 00:04:13,800 --> 00:04:17,560 那这里面也有一样有free_area刚才说的变量 81 00:04:17,600 --> 00:04:23,040 我们希望能够去以这个为头节点来查找 82 00:04:23,080 --> 00:04:26,440 所有的free_area管理的page这么一个数据结构 83 00:04:26,480 --> 00:04:27,640 那么它列出它一个方式 84 00:04:27,680 --> 00:04:32,280 在这里面看起来和通常的轮巡方式是差不多 85 00:04:32,320 --> 00:04:34,640 有一点不一样 需要注意在哪 86 00:04:34,680 --> 00:04:40,000 它用到一个特殊的宏叫le2page这么一个结构 87 00:04:40,040 --> 00:04:44,000 通过le2page来找到某一个page结构 88 00:04:44,040 --> 00:04:46,960 变量的头指针在什么地方 89 00:04:47,000 --> 00:04:49,560 这是它一个有特点的地方 90 00:04:49,600 --> 00:04:51,280 那么le2page怎么实现的 91 00:04:51,320 --> 00:04:52,560 这实际上是我们关心的 92 00:04:52,600 --> 00:04:54,400 le2page它里面包含了两块 93 00:04:54,440 --> 00:04:55,680 一个是指针 一个是member 94 00:04:55,720 --> 00:05:00,480 这个指针是链表节点所在数据结构的指针 95 00:05:00,520 --> 00:05:02,560 Member是它的名字 比如说在这里面 96 00:05:02,600 --> 00:05:06,560 page这个结构里面它是叫做pagerank这两块 97 00:05:06,600 --> 00:05:08,920 然后它是由另一个宏来执行的 98 00:05:08,960 --> 00:05:11,440 就是to_struct 里面包含了三个内容 99 00:05:11,480 --> 00:05:13,800 一个是le 一个是page 一个是member 100 00:05:13,840 --> 00:05:17,120 这个page怎么体现 就是这个里面le2page 101 00:05:17,160 --> 00:05:20,040 可以看出来它对一个特定的数据结构 102 00:05:20,080 --> 00:05:24,400 那么它需要一个特定的结构在这里体现 103 00:05:24,440 --> 00:05:26,600 那么这个结构到底怎么用 我们可以看到 104 00:05:26,640 --> 00:05:32,440 那么to_struct又是这三块进一步的展开 105 00:05:32,480 --> 00:05:37,520 这里面是等于这个链表节点的地址 106 00:05:37,560 --> 00:05:39,600 减去一个offset这个offset是什么呢 107 00:05:39,640 --> 00:05:43,080 就是减去它之前以page为例 它处于这个位置 108 00:05:43,120 --> 00:05:45,320 前面的一个偏移值 它减去之后呢 109 00:05:45,360 --> 00:05:48,360 实际上得到了 宿主数据结构头指针的一个信息 110 00:05:48,400 --> 00:05:52,240 这就是offsetof 包含了一个type一个member 111 00:05:52,280 --> 00:05:55,120 Type就是page member就是pagerank 112 00:05:55,160 --> 00:05:57,760 这offsetof又是什么呢 这个表达式比较特殊 113 00:05:57,800 --> 00:06:01,040 可以看出来 type*0 这个0代表什么意思呢 114 00:06:01,080 --> 00:06:03,480 它不代表具体的一个地址 115 00:06:03,520 --> 00:06:06,040 而是和在一起代表这种类型的 116 00:06:06,080 --> 00:06:09,160 一个成员变量一个member成员变量 117 00:06:09,200 --> 00:06:12,080 在这个类型中的一个偏移值 118 00:06:12,120 --> 00:06:14,840 整个代表这么一个含义 119 00:06:14,880 --> 00:06:15,960 那么有了这个含义之后 120 00:06:16,000 --> 00:06:18,280 我就可以把这个offset得到一个偏移值之后呢 121 00:06:18,320 --> 00:06:21,040 把ptr的地址减去那个偏移值 122 00:06:21,080 --> 00:06:24,840 一个指针操作就可以得到这个page 123 00:06:24,880 --> 00:06:29,280 数据结构的一个特定宿主变量它的地址在什么地方 124 00:06:29,320 --> 00:06:31,600 那这个le2page这个宏呢 125 00:06:31,640 --> 00:06:33,360 用到了三部分的内容 第一部分 126 00:06:33,400 --> 00:06:38,640 是宿主数据结构的链表节点的指针就是le 127 00:06:38,680 --> 00:06:42,760 第二个呢这个数据结构本身这个page这是第二块 128 00:06:42,800 --> 00:06:47,240 第三块是这个链表所对应的这个成员变量的 129 00:06:47,280 --> 00:06:50,080 名字叫member 有了这三个信息之后呢 130 00:06:50,120 --> 00:06:53,760 最后通过关键的一种表达式 这么一种表达式 131 00:06:53,800 --> 00:06:57,880 得出这个member 在这个数据结构中的offset 132 00:06:57,920 --> 00:07:01,160 然后把这个节点的这个地址减去offset 133 00:07:01,200 --> 00:07:03,640 就得到了这个宿主数据结构 134 00:07:03,680 --> 00:07:07,600 所对应变量的头指针的地址 135 00:07:07,640 --> 00:07:09,640 这就是它的大致结构 136 00:07:09,680 --> 00:07:12,400 知道了这个之后呢后面就可以 137 00:07:12,440 --> 00:07:19,960 用这种le2page等等 来完成 138 00:07:20,000 --> 00:07:25,480 对特定数据结构的头指针一个寻址的的过程 139 00:07:25,520 --> 00:07:28,560 这实际上是我们说 这个实验环境 140 00:07:28,600 --> 00:07:31,360 重点需要大家去掌握的内容 141 00:07:31,440 --> 00:07:34,240 好这部分内容就到这为止 谢谢大家