mirror of
https://github.com/Didnelpsun/CS408.git
synced 2026-02-08 21:34:41 +08:00
更新进程管理
This commit is contained in:
@@ -22,6 +22,8 @@ public:
|
||||
LinkListNode();
|
||||
explicit LinkListNode(element_type data);
|
||||
LinkListNode(element_type data, LinkListNode* next);
|
||||
// 销毁
|
||||
bool Destory();
|
||||
};
|
||||
|
||||
class LinkList {
|
||||
@@ -60,6 +62,9 @@ public:
|
||||
bool PriorInsert(element_type* elem, int start, int length);
|
||||
// 后插入
|
||||
bool NextInsert(element_type* elem, int start, int length);
|
||||
// 删除
|
||||
virtual bool Delete(int index);
|
||||
virtual bool Delete(int index, int length) = 0;
|
||||
};
|
||||
|
||||
class LinkListWithHead : public LinkList {
|
||||
@@ -70,6 +75,8 @@ public:
|
||||
bool Print() override;
|
||||
// 插入
|
||||
bool Insert(int index, element_type data) override;
|
||||
// 删除
|
||||
bool Delete(int index, int length) override;
|
||||
};
|
||||
|
||||
class LinkListWithoutHead : public LinkList {
|
||||
@@ -87,6 +94,8 @@ public:
|
||||
bool Print() override;
|
||||
// 插入
|
||||
bool Insert(int index, element_type data) override;
|
||||
// 删除
|
||||
bool Delete(int index, int length) override;
|
||||
};
|
||||
|
||||
bool LinkListNode::SetData(element_type data) {
|
||||
@@ -122,6 +131,19 @@ LinkListNode::LinkListNode(element_type data, LinkListNode* next) {
|
||||
this->SetNext(next);
|
||||
}
|
||||
|
||||
bool LinkListNode::Destory() {
|
||||
// 循环删除next指向链表后续结点
|
||||
while(this->GetNext()!= nullptr){
|
||||
LinkListNode* node = this->GetNext();
|
||||
this->SetNext(node->GetNext());
|
||||
node->SetData(NULL);
|
||||
node->SetNext(nullptr);
|
||||
}
|
||||
this->SetData(NULL);
|
||||
this->SetNext(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinkList::SetNext(LinkListNode* next) {
|
||||
this->_next = next;
|
||||
return true;
|
||||
@@ -188,16 +210,16 @@ bool LinkList::Empty() const {
|
||||
|
||||
bool LinkListWithHead::Print() {
|
||||
int i = 1;
|
||||
cout << "第0个元素值为空" << endl;
|
||||
if (this->GetLength() == 0) {
|
||||
return true;
|
||||
}
|
||||
cout << "第0个元素值为空" << endl;
|
||||
// 当前遍历指针
|
||||
LinkListNode* p = this->GetNext();
|
||||
while (p != nullptr) {
|
||||
cout << "第" << i << "个元素值为" << p->GetData() << endl;
|
||||
LinkListNode* node = this->GetNext();
|
||||
while (node != nullptr) {
|
||||
cout << "第" << i << "个元素值为" << node->GetData() << endl;
|
||||
i++;
|
||||
p = p->GetNext();
|
||||
node = node->GetNext();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -209,91 +231,108 @@ bool LinkListWithoutHead::Print() {
|
||||
}
|
||||
cout << "第" << i << "个元素值为" << this->GetData() << endl;
|
||||
// 当前遍历指针
|
||||
LinkListNode* p = this->GetNext();
|
||||
while (p != nullptr) {
|
||||
LinkListNode* node = this->GetNext();
|
||||
while (node != nullptr) {
|
||||
i++;
|
||||
cout << "第" << i << "个元素值为" << p->GetData() << endl;
|
||||
p = p->GetNext();
|
||||
cout << "第" << i << "个元素值为" << node->GetData() << endl;
|
||||
node = node->GetNext();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinkListWithHead::Insert(int index, element_type data) {
|
||||
if (index < 1) {
|
||||
cout << "Insert:插入索引值过小!" << endl;
|
||||
cout << "Insert:插入索引值" << index << "过小!" << endl;
|
||||
return false;
|
||||
}
|
||||
// 定义一个结点指针p指向当前扫描到的结点
|
||||
LinkListNode* p;
|
||||
LinkListNode* node;
|
||||
// 定义一个变量i表示当前扫描到的结点的索引号
|
||||
int i = 1;
|
||||
// 将链表头结点的next指向p,为第1个结点
|
||||
p = this->GetNext();
|
||||
LinkListNode* s = new LinkListNode(data);
|
||||
// 将链表头结点的next指向node,为第1个结点
|
||||
node = this->GetNext();
|
||||
// 设置一个新结点进行插入
|
||||
auto* new_node = new LinkListNode(data);
|
||||
// 如果该链表为空链表
|
||||
if (p == nullptr) {
|
||||
if (node == nullptr) {
|
||||
this->SetNext(new_node);
|
||||
this->SetLength();
|
||||
return true;
|
||||
}
|
||||
// 当插入的是第一个节点
|
||||
if (index == 1) {
|
||||
new_node->SetNext(node);
|
||||
this->SetNext(new_node);
|
||||
this->SetLength();
|
||||
this->SetNext(s);
|
||||
return true;
|
||||
}
|
||||
// 循环遍历到达指定索引号的单链表的结点
|
||||
// 条件是当前结点的下一个不为空且索引号到达,所到达的结点一定不是空结点
|
||||
while (p->GetNext() != nullptr && i < index - 1) {
|
||||
p = p->GetNext();
|
||||
while (node->GetNext() != nullptr && i < index - 1) {
|
||||
node = node->GetNext();
|
||||
i++;
|
||||
}
|
||||
// 如果此时i小于index-1,表示遍历完还没有到达对应的索引
|
||||
if (i < index - 1) {
|
||||
cout << "Insert:插入索引值过大!" << endl;
|
||||
cout << "Insert:插入索引值" << index << "过大!" << endl;
|
||||
return false;
|
||||
}
|
||||
// 此时i==index-1
|
||||
// 将p原来的后继给新的结点
|
||||
s->SetNext(p->GetNext());
|
||||
p->SetNext(s);
|
||||
// 将node原来的后继给新的结点
|
||||
new_node->SetNext(node->GetNext());
|
||||
node->SetNext(new_node);
|
||||
this->SetLength();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinkListWithoutHead::Insert(int index, element_type data) {
|
||||
if (index < 0) {
|
||||
cout << "Insert:插入索引值过小!" << endl;
|
||||
cout << "Insert:插入索引值" << index << "过小!" << endl;
|
||||
return false;
|
||||
}
|
||||
if (index == 0) {
|
||||
LinkListNode* node = new LinkListNode(this->GetData());
|
||||
this->SetData(data);
|
||||
this->SetLength();
|
||||
if (this->GetLength() == 0) {
|
||||
this->SetData(data);
|
||||
this->SetLength();
|
||||
}
|
||||
else {
|
||||
auto* node = new LinkListNode(this->GetData());
|
||||
node->SetNext(this->GetNext());
|
||||
this->SetData(data);
|
||||
this->SetNext(node);
|
||||
this->SetLength();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// 定义一个结点指针p指向当前扫描到的结点
|
||||
LinkListNode* p;
|
||||
// 定义一个结点指针node指向当前扫描到的结点
|
||||
LinkListNode* node;
|
||||
// 定义一个变量i表示当前扫描到的结点的索引号
|
||||
int i = 1;
|
||||
// 将链表头结点的next指向p,为第1个结点
|
||||
p = this->GetNext();
|
||||
LinkListNode* s = new LinkListNode(data);
|
||||
node = this->GetNext();
|
||||
// 设置一个新结点进行插入
|
||||
auto* new_node = new LinkListNode(data);
|
||||
// 如果该链表为空链表
|
||||
if (p == nullptr) {
|
||||
if (node == nullptr) {
|
||||
this->SetLength();
|
||||
this->SetNext(s);
|
||||
this->SetNext(new_node);
|
||||
return true;
|
||||
}
|
||||
// 循环遍历到达指定索引号的单链表的结点
|
||||
// 条件是当前结点的下一个不为空且索引号到达,所到达的结点一定不是空结点
|
||||
while (p->GetNext() != nullptr && i < index - 1) {
|
||||
p = p->GetNext();
|
||||
while (node->GetNext() != nullptr && i < index - 1) {
|
||||
node = node->GetNext();
|
||||
i++;
|
||||
}
|
||||
// 如果此时i小于index-1,表示遍历完还没有到达对应的索引
|
||||
if (i < index - 1) {
|
||||
cout << "Insert:插入索引值过大!" << endl;
|
||||
cout << "Insert:插入索引值" << index << "过大!" << endl;
|
||||
return false;
|
||||
}
|
||||
// 此时i==index-1
|
||||
// 将p原来的后继给新的结点
|
||||
s->SetNext(p->GetNext());
|
||||
p->SetNext(s);
|
||||
new_node->SetNext(node->GetNext());
|
||||
node->SetNext(new_node);
|
||||
this->SetLength();
|
||||
return true;
|
||||
}
|
||||
@@ -303,7 +342,7 @@ bool LinkList::PriorInsert(element_type* elem, int start, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
bool result = this->Insert(1, elem[i + start]);
|
||||
if (!result) {
|
||||
cout << "PriorInsert:循环插入失败!" << endl;
|
||||
cout << "PriorInsert:循环插入失败!索引值为" << i + start << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -313,7 +352,7 @@ bool LinkList::PriorInsert(element_type* elem, int start, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
bool result = this->Insert(0, elem[i + start]);
|
||||
if (!result) {
|
||||
cout << "PriorInsert:循环插入失败!" << endl;
|
||||
cout << "PriorInsert:循环插入失败!索引值为" << i + start << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -326,7 +365,7 @@ bool LinkList::NextInsert(element_type* elem, int start, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
bool result = this->Insert(i + 1, elem[i + start]);
|
||||
if (!result) {
|
||||
cout << "NextInsert:循环插入失败!" << endl;
|
||||
cout << "NextInsert:循环插入失败!索引值为" << i + start << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -336,10 +375,121 @@ bool LinkList::NextInsert(element_type* elem, int start, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
bool result = this->Insert(i, elem[i + start]);
|
||||
if (!result) {
|
||||
cout << "NextInsert:循环插入失败!" << endl;
|
||||
cout << "NextInsert:循环插入失败!索引值为" << i + start << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool LinkList::Delete(int index) {
|
||||
this->Delete(index, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinkList::Delete(int index, int length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LinkListWithHead::Delete(int index, int length) {
|
||||
if (index < 1) {
|
||||
cout << "Delete:删除索引值" << index << "过小!" << endl;
|
||||
return false;
|
||||
}
|
||||
if (length < 1) {
|
||||
cout << "Delete:删除长度" << length << "过小!" << endl;
|
||||
}
|
||||
// 定义一个结点指针start指向当前扫描到的结点,即要删除第一的元素的前一个
|
||||
LinkListNode* start;
|
||||
// 定义一个结点指针start指向当前扫描到的结点,要删除最后的元素
|
||||
LinkListNode* end;
|
||||
// 定义一个变量i表示当前扫描到的结点的索引号
|
||||
int i = 1;
|
||||
// 将链表头结点的next指向start,为第1个结点
|
||||
start = this->GetNext();
|
||||
// 如果链表没有任何数据
|
||||
if(start == nullptr) {
|
||||
cout << "Delete:链表为空!" << endl;
|
||||
return false;
|
||||
}
|
||||
// 循环遍历到达指定索引号的单链表的结点
|
||||
// 条件是当前结点的下一个不为空且索引号到达,所到达的结点一定不是空结点
|
||||
while (start->GetNext() != nullptr && i < index - 1) {
|
||||
start = start->GetNext();
|
||||
i++;
|
||||
}
|
||||
// 如果此时i小于index-1,表示遍历完还没有到达对应的索引
|
||||
if (i < index - 1) {
|
||||
cout << "Delete:删除索引值" << index << "过大!" << endl;
|
||||
return false;
|
||||
}
|
||||
// 此时i==index-1,start到达,求end
|
||||
end = start;
|
||||
for (int i = 0; i < length; i++) {
|
||||
end = end->GetNext();
|
||||
if (end == nullptr) {
|
||||
cout << "Delete:删除索引最大值" << index + length - 1 << "大于链表最大索引" << length << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (index == 1) {
|
||||
this->SetNext(end);
|
||||
}
|
||||
else {
|
||||
start->SetNext(end->GetNext());
|
||||
}
|
||||
this->SetLength(this->GetLength() - length);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinkListWithoutHead::Delete(int index, int length) {
|
||||
if (index < 0) {
|
||||
cout << "Delete:删除索引值" << index << "过小!" << endl;
|
||||
return false;
|
||||
}
|
||||
if (length < 1) {
|
||||
cout << "Delete:删除长度" << length << "过小!" << endl;
|
||||
}
|
||||
// 定义一个结点指针start指向当前扫描到的结点,即要删除第一的元素的前一个
|
||||
LinkListNode* start;
|
||||
// 定义一个结点指针start指向当前扫描到的结点,要删除最后的元素
|
||||
LinkListNode* end;
|
||||
// 定义一个变量i表示当前扫描到的结点的索引号
|
||||
int i = 1;
|
||||
// 将链表头结点的next指向start,为第1个结点
|
||||
start = this->GetNext();
|
||||
// 如果链表没有任何数据
|
||||
if (this->GetData() == NULL) {
|
||||
cout << "Delete:链表为空!" << endl;
|
||||
return false;
|
||||
}
|
||||
// 循环遍历到达指定索引号的单链表的结点
|
||||
// 条件是当前结点的下一个不为空且索引号到达,所到达的结点一定不是空结点
|
||||
while (start->GetNext() != nullptr && i < index - 1) {
|
||||
start = start->GetNext();
|
||||
i++;
|
||||
}
|
||||
// 如果此时i小于index-1,表示遍历完还没有到达对应的索引
|
||||
if (i < index - 1) {
|
||||
cout << "Delete:删除索引值" << index << "过大!" << endl;
|
||||
return false;
|
||||
}
|
||||
// 此时i==index-1,start到达,求end
|
||||
end = start;
|
||||
for (int i = 0; i < length; i++) {
|
||||
end = end->GetNext();
|
||||
if (end == nullptr) {
|
||||
cout << "Delete:删除索引最大值" << index + length - 1 << "大于链表最大索引" << length << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (index == 0) {
|
||||
this->SetNext(end);
|
||||
}
|
||||
else {
|
||||
start->SetNext(end->GetNext());
|
||||
}
|
||||
this->SetLength(this->GetLength() - length);
|
||||
return true;
|
||||
}
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
// 打印函数
|
||||
bool Print() const;
|
||||
// 循环插入函数
|
||||
bool LoopInsert(element_type *elem, int start, int length);
|
||||
bool LoopInsert(element_type *elem, int index, int length);
|
||||
// 删除函数
|
||||
bool Delete(int index, element_type &elem);
|
||||
// 多个删除函数
|
||||
@@ -238,9 +238,9 @@ bool DynamicSequenceList::Insert(int index, element_type elem) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SequenceList::LoopInsert(element_type *elem, int start, int length) {
|
||||
bool SequenceList::LoopInsert(element_type *elem, int index, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
bool result = this->Insert(i, elem[i + start]);
|
||||
bool result = this->Insert(i, elem[i + index]);
|
||||
if (!result) {
|
||||
cout << "LoopInsert:循环插入失败!" << endl;
|
||||
return false;
|
||||
|
||||
@@ -22,12 +22,14 @@ int LinkListTest() {
|
||||
LinkListWithHead list;
|
||||
//cout << list.Empty() << endl;
|
||||
element_type a[6] = { '1','2','3','4','5','6' };
|
||||
list.PriorInsert(a, 2, 3);
|
||||
list.NextInsert(a, 0, 5);
|
||||
list.Print();
|
||||
cout << list.GetLength() << endl;
|
||||
list.Delete(2, 3);
|
||||
list.Print();
|
||||
/*cout << list.GetLength() << endl;
|
||||
LinkListWithoutHead list2;
|
||||
list2.PriorInsert(a, 2, 3);
|
||||
list2.Print();
|
||||
cout << list2.GetLength() << endl;
|
||||
cout << list2.GetLength() << endl;*/
|
||||
return 0;
|
||||
}
|
||||
@@ -567,3 +567,35 @@ $C.$根据$x$的值判断该进程是否进入阻塞态
|
||||
$D.$阻塞该进程,并将之插入$x$的阻塞队列中
|
||||
|
||||
解:$D$。这是基本的概念,且由于进程已经执行`x.wait()`,证明判断条件为真应该阻塞,所以$C$不对。“条件变量”是管程内部说明和使用的一种特殊变量,其作用类似于信号量机制中的“信号量”,都用于实现进程同步。需要注意的是,在同一时刻,管程中只能有一个进程在执行。若进程$A$执行了`x.wait()`操作,则该进程会阻塞,并挂到条件变量`x`对应的阻塞队列上。这样,管程的使用权被释放,就可以有另一个进程进入管程。若进程$B$执行了`x.signal()`操作,则会唤醒`x`对应的阻塞队列的队首进程。在$Pascal$语言的管程中,规定只有一个进程要离开管程时才能调用`signal()`操作。
|
||||
|
||||
## 死锁
|
||||
|
||||
### 避免死锁
|
||||
|
||||
**例题** 系统中有$5$个进程$P_0$到$P_4$,$3$种资源$R_0$到$R_2$,初始数量为$(10,5,7)$,某一时刻的情况可表示如下:
|
||||
|
||||
进程|最大需求|已分配
|
||||
:-:|:------:|:----:
|
||||
P0|(7,5,3)|(0,1,0)
|
||||
P1|(3,2,2)|(2,0,0)
|
||||
P2|(9,0,2)|(3,0,2)
|
||||
P3|(2,2,2)|(2,1,1)
|
||||
P4|(4,3,3)|(0,0,2)
|
||||
|
||||
解:将已分配的部分全部加起来得到$(7,2,5)$,还剩余资源数$Available=(3,3,2)$。
|
||||
|
||||
将每个进程的最大需求减去已分配,会得到最多还需要的资源数量,三列分别为$Max$、$Allocation$、$Need$:
|
||||
|
||||
进程|最大需求|已分配|最多还需要
|
||||
:-:|:------:|:----:|:-------:
|
||||
P0|(7,5,3)|(0,1,0)|(7,4,3)
|
||||
P1|(3,2,2)|(2,0,0)|(1,2,2)
|
||||
P2|(9,0,2)|(3,0,2)|(6,0,0)
|
||||
P3|(2,2,2)|(2,1,1)|(0,1,1)
|
||||
P4|(4,3,3)|(0,0,2)|(4,3,1)
|
||||
|
||||
将剩余资源数$Work=(3,3,2)$与各个进程的最多还需要值$Need$对比,如果剩余资源数每个资源值都大于该进程的最多还需要的资源值,就代表这个进程可以分配资源。
|
||||
|
||||
对比得到$P_1$和$P_3$可以分配。
|
||||
|
||||
其中若先$P_1$分配完归还资源后可用资源为$(3,3,2)+(1,2,2)=(5,3,2)$,然后使用该可用资源序列进行下一轮的分配,直到五次循环检查后五个进程都加入了安全序列中$\{P_1,P_3,P_4,P_2,P_0\}$,就得到了一个最终的序列。该算法称为安全性算法。
|
||||
|
||||
@@ -1529,6 +1529,9 @@ monitor Demo {
|
||||
|
||||
#### 死锁发生的条件
|
||||
|
||||
1. 系统资源的竞争。
|
||||
2. 进程推进顺序非法。
|
||||
|
||||
产生死锁必须同时满足一下四个条件,只要其中任一条件不成立,死锁就不会发生:
|
||||
|
||||
+ 互斥条件:只有对必须互斥使用的资源的争抢才会导致死锁(如哲学家的筷子、打印机设备)。像内存、扬声器这样可以同时让多个进程使用的资源是不会导致死锁的(因为进程不用阻塞等待这种资源)。
|
||||
@@ -1536,13 +1539,13 @@ monitor Demo {
|
||||
+ 请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己己有的资源保持不放。
|
||||
+ 循环等待条件:存在一种进程资源的循环等待链,链中的每一个进程己获得的资源同时被下一个进程所请求。
|
||||
+ 发生死锁时一定有循环等待,但是发生循环等待时未必死锁(循环等待是死锁的必要不充分条件)。
|
||||
+ 如果同类资源数大于1,则即使有循环等待,也未必发生死锁。但如果系统中每类资源都只有一个,那循环等待就是死锁的充分必要条件了。
|
||||
+ 如果同类资源数大于$1$,则即使有循环等待,也未必发生死锁。但如果系统中每类资源都只有一个,那循环等待就是死锁的充分必要条件了。
|
||||
|
||||
#### 死锁发生的情况
|
||||
|
||||
1. 对系统资源的竞争。各进程对不可剥夺的资源(如打印机)的竞争可能引起死锁,对可剥夺的资源($CPU$)的竞争是不会引起死锁的。
|
||||
2. 进程推进顺序非法。请求和释放资源的顺序不当,也同样会导致死锁。例如,并发执行的进程$P1$、$P2$分别申请并占有了资源$R1$、$R2$,之后进程$P1$又紧接着申请资源$R2$,而进程$P2$又申请资源$R1$,两者会因为申请的资源被对方占有而阻塞,从而发生死锁。
|
||||
3. 信号量的使用不当也会造成死锁。如生产者-消费者问题中,如果实现互斥的P操作在实现同步的Р操作之前,就有可能导致死锁。(可以把互斥信号量、同步信号量也看做是一种抽象的系统资源)
|
||||
3. 信号量的使用不当也会造成死锁。如生产者-消费者问题中,如果实现互斥的$P$操作在实现同步的$Р$操作之前,就有可能导致死锁。(可以把互斥信号量、同步信号量也看做是一种抽象的系统资源)
|
||||
|
||||
总之,对不可剥夺资源的不合理分配,可能导致死锁。
|
||||
|
||||
@@ -1552,6 +1555,12 @@ monitor Demo {
|
||||
2. 避免死锁。用某种方法防止系统进入不安全状态,从而避免死锁(银行家算法)。
|
||||
3. 死锁的检测和解除。允许死锁的发生,不过操作系统会负责检测出死锁的发生,然后采取某种措施解除死锁。
|
||||
|
||||
|资源分配策略|各种可能模式|主要优点|主要缺点
|
||||
:----:|:----------:|:----------:|:-------:|:-----:|
|
||||
死锁预防|保守,宁可资源闲置|一次请求所有资源,资源剥夺,资源按序分配|适用于突发式处理的进程,不必进行剥夺|效率低,进程初始化时间延长;剥夺次数过多;不便灵活申请新资源
|
||||
死锁避兔|是“预防”和“检测”的折中(在运行时判断是否可能死锁)|寻找可能的安全允许顺序|不必进行剥夺|必须知道将来的资源需求;进程不能被长时间阻塞
|
||||
死锁检测|宽松,只要允许就分配资源|定期检查死锁是否已经发生|不延长进程初始化时间,允许对死锁进行现场处理|通过剥夺解除死锁,造成损失
|
||||
|
||||
### 预防死锁
|
||||
|
||||
预防死锁是不允许死锁发生的静态策略。
|
||||
@@ -1560,7 +1569,7 @@ monitor Demo {
|
||||
|
||||
如果把只能互斥使用的资源改造为允许共享使用,则系统不会进入死锁状态。
|
||||
|
||||
比如:SPOOLing技术。操作系统可以采用SPoOLing技术把独占设备在逻辑上改造成共享设备。比如,用SPooLing技术将打印机改造为共享设备,将多个进程的请求合并为一个输出进程。
|
||||
比如:$SPOOLing$技术。操作系统可以采用$SPOOLing$技术把独占设备在逻辑上改造成共享设备。比如,用$SPOOLing$技术将打印机改造为共享设备,将多个进程的请求合并为一个输出进程。
|
||||
|
||||
该策略的缺点:并不是所有的资源都可以改造成可共享使用的资源。并且为了系统安全,很多地方还必须保护这种互斥性。因此,很多时候都无法破坏互斥条件。
|
||||
|
||||
@@ -1572,7 +1581,7 @@ monitor Demo {
|
||||
该策略的缺点:
|
||||
|
||||
1. 实现起来比较复杂。
|
||||
2. 释放已获得的资源可能造成前一阶段工作的失效。因此这种方法一般只适用于易保存和恢复状态的资源,如$CPU$。
|
||||
2. 释放已获得的资源可能造成前一阶段工作的失效。因此这种方法一般只适用于易保存和恢复状态的资源,如$CPU$,而不能用于打印机。
|
||||
3. 反复地申请和释放资源会增加系统开销,降低系统吞吐量。
|
||||
4. 若采用方案一,意味着只要暂时得不到某个资源,之前获得的那些资源就都需要放弃,以后再重新申请。如果一直发生这样的情况,就会导致进程饥饿。
|
||||
|
||||
@@ -1611,60 +1620,42 @@ monitor Demo {
|
||||
|
||||
可以在资源分配之前预先判断这次分配是否会导致系统进入不安全状态,以此决定是否答应资源分配请求。这也是“银行家算法”的核心思想。
|
||||
|
||||
**例题** 系统中有5个进程P0到P4,3种资源ROR到2,初始数量为(10,5,7),某一时刻的情况可表示如下:
|
||||
|
||||
进程|最大需求|已分配
|
||||
:-:|:------:|:----:
|
||||
P0|(7,5,3)|(0,1,0)
|
||||
P1|(3,2,2)|(2,0,0)
|
||||
P2|(9,0,2)|(3,0,2)
|
||||
P3|(2,2,2)|(2,1,1)
|
||||
P4|(4,3,3)|(0,0,2)
|
||||
|
||||
将已分配的部分全部加起来得到(7,2,5),还剩余资源数(3,3,2)。
|
||||
|
||||
将每个进程的最大需求减去已分配,会得到最多还需要的资源数量:
|
||||
|
||||
进程|最大需求|已分配|最多还需要
|
||||
:-:|:------:|:----:|:-------:
|
||||
P0|(7,5,3)|(0,1,0)|(7,4,3)
|
||||
P1|(3,2,2)|(2,0,0)|(1,2,2)
|
||||
P2|(9,0,2)|(3,0,2)|(6,0,0)
|
||||
P3|(2,2,2)|(2,1,1)|(0,1,1)
|
||||
P4|(4,3,3)|(0,0,2)|(4,3,1)
|
||||
|
||||
将剩余资源数(3,3,2)与各个进程的最多还需要值对比,如果剩余资源数每个资源值都大于该进程的最多还需要的资源值,就代表这个进程可以分配资源。
|
||||
|
||||
对比得到P1和P3可以分配。
|
||||
|
||||
其中P1分配完归还资源后可用资源为(3,3,2)+(1,2,2)=(5,3,2),然后使用该可用资源序列进行下一轮的分配,直到五次循环检查后五个进程都加入了安全序列中,就得到了一个最终的序列。该算法称为安全性算法。
|
||||
|
||||
若每一轮检查都从最小编号开始会更快得到安全序列。
|
||||
|
||||
同时还有一个更快的方法得到安全序列:将剩余资源数与最多还需要对比,满足条件的进程全部加入安全序列(而非一个个),然后把归还的资源相加,进行下一轮的比较。
|
||||
|
||||
使用代码实现:
|
||||
|
||||
假设系统中有n个进程,m种资源。
|
||||
假设系统中有$n$个进程,$m$种资源。
|
||||
|
||||
每个进程在运行前先声明对各种资源的最大需求数,则可用一个n×m的矩阵(可用二维数组实现)表示所有进程对各种资源的最大需求数。不妨称为最大需求矩阵Max,Max[i,j]=K表示进程Pi最多需要K个资源Rj。
|
||||
用一个长度为$m$的一维数组 $Available$表示当前系统中还有多少可用资源。`Available[j]=K`表示系统中有$R_j$类资源$K$个。
|
||||
|
||||
同理,系统可以用一个n×m的分配矩阵Allocation表示对所有进程的资源分配情况。Max-Allocation=Need矩阵,表示各进程最多还需要多少各类资源。
|
||||
每个进程在运行前先声明对各种资源的最大需求数,则可用一个$n\times m$的矩阵(可用二维数组实现)表示所有进程对各种资源的最大需求数。不妨称为最大需求矩阵$Max$,`Max[i,j]=K`表示进程$P_i$最多需要$K$个资源$R_j$。
|
||||
|
||||
另外,还要用一个长度为m的一维数组 Available表示当前系统中还有多少可用资源。
|
||||
同理,系统可以用一个$n\times m$的分配矩阵$Allocation$表示对所有进程的资源分配情况。`Allocation[i,j]=K`表示进程$P_i$当前已经分片到$R_j$类资源的数目为$K$。
|
||||
|
||||
某进程Pi向系统申请资源,可用一个长度为m的一维数组Ri表示本次申请的各种资源量。
|
||||
$Max-Allocation=Need[i,j]$矩阵,表示各进程最多还需要多少各类资源。
|
||||
|
||||
某进程$P_i$向系统申请资源,可用一个长度为$m$的一维数组$R_i$表示本次申请的各种资源量。
|
||||
|
||||
可用银行家算法预判本次分配是否会导致系统进入不安全状态:
|
||||
|
||||
1. 如果Ri[j]≤Need\[i,j](O≤j≤m)则转向步骤二,否则因为其需要的资源数已经大于最大值,认为出错。
|
||||
2. 如果Ri[j]≤Available\[i](0≤j≤m),便转向步骤三看,否则表示尚无足够资源,Pi必须等待。
|
||||
3. 系统试探着把资源分配给进程Pi,并修改相应的数据(并非真的分配,修改数值只是为了做预判)。
|
||||
1. 如果`Ri[j] <= Need[i,j]`($0\leqslant j\leqslant m$)则转向步骤二,否则因为其需要的资源数已经大于最大值,认为出错。
|
||||
2. 如果`Ri[j] <= Available[i]`($0\leqslant j\leqslant m$),便转向步骤三看,否则表示尚无足够资源,$P_i$必须等待。
|
||||
3. 系统试探着把资源分配给进程$P_i$,并修改相应的数据(并非真的分配,修改数值只是为了做预判)。
|
||||
+ `Available = Available - Ri;`
|
||||
+ `Allocation[i,j] = Allocation[i,j] + Ri[j];`
|
||||
+ `Need[i,j] = Need[i,j] - Ri[j];`
|
||||
4. 操作系统执行安全性算法,检查此次资源分配后,系统是否处于安全状态。若安全,才正式分配,否则,恢复相应数据,让进程阻塞等待。
|
||||
|
||||
安全性算法,设置工作向量$Work$,有$m$个元素,表示系统中的剩余可用资源数目。在执行安全性算法开始时,`Work = Available`。
|
||||
|
||||
1. 初始时安全序列为空。
|
||||
2. 从N$eed$矩阵中找出符合下面条件的行:该行对应的进程不在安全序列中,而且该行小于等于$Work$向量,找到后,把对应的进程加入安全序列;若找不到,则执行步骤四。
|
||||
3. 进程$P_i$进入安全序列后,可顺利执行,直至完成,并释放分配给它的资源,因此应执行
|
||||
`Work = Work + Allocation[i]`,其中`Allocation[i]`表示进程$P_i$代表的在$Allocation$矩阵中对应的行,返回步骤二。
|
||||
若此时安全序列中已有所有进程,则系统处于安全状态,否则系统处于不安全状态。
|
||||
|
||||
### 检测解除死锁
|
||||
|
||||
允许死锁的产生。
|
||||
@@ -1679,11 +1670,11 @@ P4|(4,3,3)|(0,0,2)|(4,3,1)
|
||||
数据结构——资源分配图:
|
||||
|
||||
+ 两种结点:
|
||||
+ 进程结点:对应一个进程。
|
||||
+ 资源结点:对应一类资源,一类资源可能有多个。
|
||||
+ 进程结点(圆圈):对应一个进程。
|
||||
+ 资源结点(方框):对应一类资源,一类资源可能有多个。
|
||||
+ 两种边:
|
||||
+ 进程结点——>资源结点(请求边):表示进程想申请几个资源(每条边代表一个)。
|
||||
+ 资源节点——>进程结点(分配边):表示已经为进程分配了几个资源(每条边代表一个)。
|
||||
+ 进程结点→资源结点(请求边):表示进程想申请几个资源(每条边代表一个)。
|
||||
+ 资源节点→进程结点(分配边):表示已经为进程分配了几个资源(每条边代表一个)。
|
||||
|
||||
+ 如果系统中剩余的可用资源数足够满足进程的需求,那么这个进程暂时是不会阻塞的,可以顺利地执行下去。
|
||||
+ 如果这个进程执行结束了把资源归还系统,就可能使某些正在等待资源的进程被激活,并顺利地执行下去。
|
||||
@@ -1694,8 +1685,8 @@ P4|(4,3,3)|(0,0,2)|(4,3,1)
|
||||
|
||||
总结上面的描述,所以检测死锁的算法:
|
||||
|
||||
1. 在资源分配图中,找出既不阻塞又不是孤点的进程Pi(即找出一条有向边与它相连,且该有向边对应资源的申请数量小于等于系统中已有空闲资源数量。若所有的连接该进程的边均满足上述条件,则这个进程能继续运行直至完成,然后释放它所占有的所有资源)。消去它所有的请求边和分配变,使之称为孤立的结点。
|
||||
2. 进程Pi所释放的资源,可以唤醒某些因等待这些资源而阻塞的进程,原来的阻塞进程可能变为非阻塞进程。根据步骤一中的方法进行一系列简化后,若能消去途中所有的边,则称该图是可完全简化的。
|
||||
1. 在资源分配图中,找出既不阻塞又不是孤点的进程$P_i$(即找出一条有向边与它相连,且该有向边对应资源的申请数量小于等于系统中已有空闲资源数量。若所有的连接该进程的边均满足上述条件,则这个进程能继续运行直至完成,然后释放它所占有的所有资源)。消去它所有的请求边和分配边,使之称为孤立的结点。
|
||||
2. 进程$P_i$所释放的资源,可以唤醒某些因等待这些资源而阻塞的进程,原来的阻塞进程可能变为非阻塞进程。根据步骤一中的方法进行一系列简化后,若能消去途中所有的边,则称该图是可完全简化的。
|
||||
3. 死锁定理:若是不能完全简化,则系统死锁。
|
||||
|
||||
#### 解除死锁
|
||||
|
||||
Reference in New Issue
Block a user