操作系统内存相关:首次适应算法实现主存分配和回收。
问题描述
- 可变分区方式是按作业需要的主存空间大小来分割分区的。当要装入一个作业时,根据作业需要的主存容量查看是否有足够的空闲空间,若有,则按需分配,否则,作业无法装入。
- 假定内存大小为128K,空闲区说明表格式为:
- 分区号(表示是第几个空闲分区)
- 起始地址(指出空闲区的起始地址)
- 长度(一个连续空闲区的长度)
- 采用首次适应算法分配回收内存空间。运行时,输入一系列分配请求和回收请求。
要求
要求能接受来自键盘的空间申请及释放请求,能显示分区分配及回收后的内存布局情况。
代码实现
方法一:使用二叉树存储空闲区说明表
- 节点结构为:
address
:首地址length
:分区长度state
:状态(F:空闲,B:忙碌,P:左右孩子节点的父节点)homework
:作业号(-1表示没有作业装入)*lchild
:左孩子(Busy状态)*rchild
:右孩子(Free状态)
- 最开始只有一个根节点
- 每次作业装入都是将一个节点划分为左右孩子,左孩子装入作业,右孩子为剩余空间
- 每次卸载作业只需找到该作业将其
state
改为F
- 每次卸载作业成功后合并相邻空闲分区
- 打印空闲区说明表时只打印度为0的节点,这才是真正的表项
- 待优化:因为二叉树结构的特殊性,本程序无法合并不同深度的相邻空闲节点
1 |
|
2 | using namespace std; |
3 | |
4 | int flag = 0; // 装入一个作业时赋值为1,用来判断是否成功装入作业 |
5 | int temp = 0; // 卸载一个作业时赋值为1,用来判断是否成功卸载作业 |
6 | // 空闲区说明表结构体 |
7 | typedef struct Node { |
8 | int address; // 首地址 |
9 | int length; // 长度 |
10 | char state; // 状态(F:空闲,B:忙碌,P:左右孩子节点的父节点) |
11 | int homework; // 作业号(-1表示没有作业装入) |
12 | struct Node *lchild; // 左孩子(Busy状态) |
13 | struct Node *rchild; // 右孩子(Free状态) |
14 | }TNode; // 空闲区说明表节点 |
15 | |
16 | // 初始化空闲区说明表 |
17 | void init_table(TNode *p) { |
18 | p->address = 0; // 从0开始 |
19 | p->length = 128; // 大小为128K |
20 | p->state = 'F'; // 空闲 |
21 | p->homework = -1; // 没有作业装入 |
22 | p->lchild = NULL; |
23 | p->rchild = NULL; |
24 | cout << "*首地址为0,大小为128K的内存空间已初始化完成*" << endl; |
25 | } |
26 | |
27 | // 打印空闲区说明表 |
28 | void show_item(TNode *p) { |
29 | cout << p->address << "\t" << p->length; |
30 | if(p->state == 'F') cout << "\tFree"; |
31 | else cout << "\tBusy"; |
32 | if(p->homework != -1) cout << "\t" << p->homework; |
33 | else cout << "\tnull"; |
34 | cout << endl; |
35 | } |
36 | |
37 | // 采用二叉树的先序遍历打印空闲区说明表 |
38 | void show_table(TNode *p) { |
39 | if(p) { |
40 | if(!p->lchild && !p->rchild && p->length!=0) |
41 | show_item(p); // 度为0的节点才是一个表项 |
42 | show_table(p->lchild); |
43 | show_table(p->rchild); |
44 | } |
45 | } |
46 | |
47 | // 采用二叉树的后序遍历合并相邻空闲区 |
48 | // 因为二叉树结构的特殊性,只能合并处于同一深度的空闲分区 |
49 | void merge_table(TNode *p) { |
50 | if(p) { |
51 | merge_table(p->lchild); |
52 | merge_table(p->rchild); |
53 | if(p->lchild && p->rchild && p->lchild->state=='F' && p->rchild->state=='F') { |
54 | p->state = 'F'; |
55 | p->homework = -1; |
56 | p->address = p->lchild->address; |
57 | p->length = p->lchild->length + p->rchild->length; |
58 | p->lchild = NULL; |
59 | p->rchild = NULL; |
60 | } |
61 | } |
62 | } |
63 | |
64 | // 装入新作业 |
65 | // 参数:空闲区说明表,作业号,作业长度 |
66 | void insert_homework(TNode *p, int work, int len) { |
67 | if(p) { |
68 | // 当改节点:空闲 + 足够大 + 是一个表项 时,才能装入新作业 |
69 | if(p->state=='F' && len<=p->length && !p->lchild && !p->rchild && flag==0) { |
70 | TNode *left = new TNode; // 左孩子装入作业 |
71 | TNode *right = new TNode; // 右孩子为剩余空间 |
72 | // 设置左孩子节点 |
73 | left->homework = work; |
74 | left->address = p->address; |
75 | left->length = len; |
76 | left->state = 'B'; |
77 | left->lchild = NULL; |
78 | left->rchild = NULL; |
79 | // 设置右孩子节点 |
80 | right->homework = -1; |
81 | right->address = left->address + len; |
82 | right->length = p->length - left->length; |
83 | right->state = 'F'; |
84 | right->lchild = NULL; |
85 | right->rchild = NULL; |
86 | // 设置p节点 |
87 | p->state = 'P'; |
88 | p->lchild = left; |
89 | p->rchild = right; |
90 | flag = 1; |
91 | }else { |
92 | insert_homework(p->lchild, work, len); |
93 | insert_homework(p->rchild, work, len); |
94 | } |
95 | } |
96 | } |
97 | |
98 | // 卸载作业 |
99 | // 参数:空闲区说明表,作业号 |
100 | void delete_homework(TNode *p, int work) { |
101 | if(p) { |
102 | if(p->homework == work) { |
103 | p->homework = -1; |
104 | p->state = 'F'; |
105 | temp = 1; |
106 | }else { |
107 | delete_homework(p->lchild, work); |
108 | delete_homework(p->rchild, work); |
109 | } |
110 | } |
111 | } |
112 | |
113 | // 可变分区管理方式下采用首次适应算法实现主存分配和回收 |
114 | void First_Fit() { |
115 | // 初始化空闲区说明表 |
116 | TNode *p = new TNode; |
117 | init_table(p); |
118 | // 打印初始空闲区说明表 |
119 | cout << "*初始化空闲区说明表*" << endl; |
120 | cout << "首地址\t长度\t状态\t作业号" << endl; |
121 | show_table(p); |
122 | // 作业信息 |
123 | char how; // 作业装入或卸载命令 |
124 | int no; // 作业号 |
125 | int len; // 作业长度 |
126 | // 响应来自键盘的作业装入和卸载请求 |
127 | cout << "*指令格式:装入/卸载 作业号 [作业长度](如:n 1 20 / d 1),输入'#'结束*" << endl; |
128 | cout << "*请输入:"; |
129 | cin >> how; |
130 | while(how != '#') { |
131 | cin >> no; |
132 | if(how == 'n') { |
133 | flag = 0; |
134 | cin >> len; |
135 | if(len > 0) { |
136 | insert_homework(p, no, len); |
137 | if(flag == 1) { |
138 | cout << "装入作业" << no << endl; |
139 | cout << "首地址\t长度\t状态\t作业号" << endl; |
140 | show_table(p); |
141 | }else cout << "空间不足,装入失败!" << endl; |
142 | }else cout << "作业所需空间大小需为正数!" << endl; |
143 | } |
144 | else if(how == 'd') { |
145 | temp = 0; |
146 | delete_homework(p, no); |
147 | if(temp == 1) { |
148 | merge_table(p); // 合并空闲分区 |
149 | cout << "卸载作业" << no << endl; |
150 | cout << "首地址\t长度\t状态\t作业号" << endl; |
151 | show_table(p); |
152 | }else cout << "作业不存在,卸载失败!" << endl; |
153 | } |
154 | else |
155 | cout << "指令格式错误,请重新输入!" << endl; |
156 | cout << "*请输入:"; |
157 | cin >> how; |
158 | } |
159 | } |
160 | |
161 | int main() { |
162 | cout << "**可变分区管理方式下采用首次适应算法实现主存分配和回收**" << endl; |
163 | First_Fit(); |
164 | |
165 | return 0; |
166 | } |
方法二:使用单链表存储空闲区说明表
- 节点结构为:
address
:首地址length
:分区长度state
:状态(F:空闲,B:忙碌)homework
:作业号(-1表示没有作业装入)*next
:后继节点
- 每次装入作业时:
- 该空闲区大小和作业大小相等,直接将作业信息赋给当前分区节点
- 否则,装入作业当前节点,新建一个节点作为剩余空间节点
- 每次卸载作业只需找到该作业将其
state
改为F
- 每次卸载作业成功后合并相邻空闲分区
- 本方法克服了方法一的不足,能够实现任意相邻空闲分区的合并
1 |
|
2 | using namespace std; |
3 | |
4 | // 空闲区说明表结构体 |
5 | typedef struct Node { |
6 | int address; // 首地址 |
7 | int length; // 长度 |
8 | char state; // 状态(F:空闲,B:忙碌) |
9 | int homework; // 作业号(-1表示没有作业装入) |
10 | struct Node *next; // 后继节点 |
11 | }TNode; // 空闲区说明表节点 |
12 | |
13 | // 初始化空闲区说明表 |
14 | void init_table(TNode *p) { |
15 | p->address = 0; // 从0开始 |
16 | p->length = 128; // 大小为128K |
17 | p->state = 'F'; // 空闲 |
18 | p->homework = -1; // 没有作业装入 |
19 | p->next = NULL; |
20 | cout << "*首地址为0,大小为128K的内存空间已初始化完成*" << endl; |
21 | } |
22 | |
23 | // 打印空闲区说明表节点 |
24 | void show_item(TNode *p) { |
25 | cout << p->address << "\t" << p->length; |
26 | if(p->state == 'F') cout << "\tFree"; |
27 | else cout << "\tBusy"; |
28 | if(p->homework != -1) cout << "\t" << p->homework; |
29 | else cout << "\tnull"; |
30 | cout << endl; |
31 | } |
32 | |
33 | // 打印空闲区说明表 |
34 | void show_table(TNode *p) { |
35 | while(p) { |
36 | show_item(p); |
37 | p = p->next; |
38 | } |
39 | } |
40 | |
41 | // 装入新作业 |
42 | // 参数:空闲区说明表,作业号,作业长度 |
43 | // 返回值:true装入成功,false装入失败 |
44 | bool insert_homework(TNode *p, int work, int len) { |
45 | while(p) { |
46 | // 当改节点:空闲 + 足够大 + 是一个表项 时,才能装入新作业 |
47 | if(p->state=='F' && len<=p->length) { |
48 | if(len == p->length) { // 作业完全填充该表项,没有剩余空间 |
49 | p->homework = work; |
50 | p->state = 'B'; |
51 | }else { |
52 | // 新表项表示装入作业后的剩余空间 |
53 | TNode *q = new TNode; |
54 | q->address = p->address + len; |
55 | q->state = 'F'; |
56 | q->homework = -1; |
57 | q->length = p->length - len; |
58 | // 作业装入当前表项的前len长度的空间 |
59 | p->state = 'B'; |
60 | p->homework = work; |
61 | p->length = len; |
62 | // 链接 |
63 | q->next = p->next; |
64 | p->next = q; |
65 | } |
66 | return true; |
67 | } |
68 | p = p->next; |
69 | } |
70 | return false; |
71 | } |
72 | |
73 | // 合并相邻空闲区 |
74 | void merge_table(TNode *p) { |
75 | while(p) { |
76 | if(p->state == 'F') { |
77 | if(p->next && p->next->state=='F') { |
78 | TNode *q = p->next; |
79 | p->length += q->length; |
80 | p->next = q->next; |
81 | free(q); |
82 | } |
83 | else if(p->next && p->next->state=='B') |
84 | p = p->next->next; |
85 | else if(p->next == NULL) |
86 | return; |
87 | }else p = p->next; |
88 | } |
89 | } |
90 | |
91 | // 卸载作业 |
92 | // 参数:空闲区说明表,作业号 |
93 | // 返回值:true卸载成功,false卸载失败 |
94 | bool delete_homework(TNode *p, int work) { |
95 | while(p) { |
96 | if(p->homework == work) { |
97 | p->homework = -1; |
98 | p->state = 'F'; |
99 | return true; |
100 | } |
101 | p = p->next; |
102 | } |
103 | return false; |
104 | } |
105 | |
106 | // 可变分区管理方式下采用首次适应算法实现主存分配和回收 |
107 | void First_Fit() { |
108 | // 初始化空闲区说明表 |
109 | TNode *p = new TNode; |
110 | init_table(p); |
111 | // 打印初始空闲区说明表 |
112 | cout << "*初始化空闲区说明表*" << endl; |
113 | cout << "首地址\t长度\t状态\t作业号" << endl; |
114 | show_table(p); |
115 | // 作业信息 |
116 | char how; // 作业装入或卸载命令 |
117 | int no; // 作业号 |
118 | int len; // 作业长度 |
119 | // 响应来自键盘的作业装入和卸载请求 |
120 | cout << "*指令格式:装入/卸载 作业号 [作业长度](如:n 1 20 / d 1),输入'#'结束*" << endl; |
121 | cout << "*请输入:"; |
122 | cin >> how; |
123 | while(how != '#') { |
124 | cin >> no; |
125 | if(how == 'n') { |
126 | cin >> len; |
127 | if(len > 0) { |
128 | bool flag = insert_homework(p, no, len); |
129 | if(flag == true) { |
130 | cout << "装入作业" << no << endl; |
131 | cout << "首地址\t长度\t状态\t作业号" << endl; |
132 | show_table(p); |
133 | }else cout << "空间不足,装入失败" << endl; |
134 | }else cout << "作业所需空间大小需为正数!" << endl; |
135 | } |
136 | else if(how == 'd') { |
137 | bool flag = delete_homework(p, no); |
138 | if(flag == true) { |
139 | merge_table(p); // 合并空闲分区 |
140 | cout << "卸载作业" << no << endl; |
141 | cout << "首地址\t长度\t状态\t作业号" << endl; |
142 | show_table(p); |
143 | }else cout << "作业不存在,卸载失败!" << endl; |
144 | } |
145 | else |
146 | cout << "指令格式错误,请重新输入!" << endl; |
147 | cout << "*请输入:"; |
148 | cin >> how; |
149 | } |
150 | } |
151 | |
152 | int main() { |
153 | cout << "**可变分区管理方式下采用首次适应算法实现主存分配和回收**" << endl; |
154 | First_Fit(); |
155 | |
156 | return 0; |
157 | } |
程序中的命令格式
- 装入作业:n 作业号 作业长度,如:
- n 1 20
- n 2 50
- 卸载作业:d 作业号,如:
- d 2
- d 1