一、时间片轮转法调度
假定系统有5个进程,每个进程用一个PCB来代表。PCB的结构为:
·进程名——如Q1~Q5。
·指针——把5个进程连成队列,用指针指出下一个进程PCB的首地址。
·要求运行时间——假设进程需要运行的时间单位数。
·已运行时间——进程已运行的时间单位数,初始值为0。
·状态——假设两种状态,就绪和结束,用R表示就绪,用E表示结束。初始状态都为就绪状态。
(2) 运行之前,为每个进程确定它的“要求运行时间”。通过键盘输入这些参数。
(3) 把5个进程按顺序排成循环队列,用指针指出队列连接情况。用一个标志单元记录轮到运行的进程。处理器调度总是选择标志单元指示的进程运行,对所指的进程,将其“已运行时间”加1。
(4) 进程运行一次后,若“要求运行时间”等于“已运行时间”,则将状态改为“结束”,退出队列,否则将继续轮转。
(5) 若就绪队列为空,结束,否则转到(3)重复。
#include <iostream>
#include <string>
using namespace std;
class PCB {
public:
string name;//进程名
int runningtime;//要求运行时间
PCB* next;//下个进程块
int elapsedtime;//已运行时间
string state = "R";//状态
PCB(string n, int t);
};
PCB::PCB(string n, int t) {
name = n;
runningtime = t;
}
int main()
{
int rt[5];
cout << "请依次输入五个进程的要求运行时间" << endl;
cin >> rt[0] >> rt[1] >> rt[2] >> rt[3] >> rt[4];
PCB Q1("Q1", rt[0]);
PCB Q2("Q2", rt[1]);
PCB Q3("Q3", rt[2]);
PCB Q4("Q4", rt[3]);
PCB Q5("Q5", rt[4]);
Q1.next = &Q2;//默认顺序
Q2.next = &Q3;
Q3.next = &Q4;
Q4.next = &Q5;
Q5.next = &Q1;
PCB* runningPcb;//当前运行的进程;
runningPcb = &Q1;
while (Q1.state == "R" || Q2.state == "R" || Q3.state == "R" || Q4.state == "R" || Q5.state == "R") {
if (runningPcb->state == "E") {
runningPcb = runningPcb->next; continue;
}
cout << "正在运行进程" << runningPcb->name << ",该进程要求运行时间为" << runningPcb->runningtime << ",已运行时间为" << runningPcb->elapsedtime << endl;
PCB* checkPcb = runningPcb->next;//检查指针遍历全部就绪进程输出进程情况
while (runningPcb != checkPcb) {
if (checkPcb->state == "R") {
cout << "等待的就绪进程" << checkPcb->name << ",该进程要求运行时间为" << checkPcb->runningtime << ",已运行时间为" << checkPcb->elapsedtime << endl;
}
checkPcb = checkPcb->next;
}
runningPcb->elapsedtime++;
if (runningPcb->elapsedtime == runningPcb->runningtime) {//判定进程是否运行结束
runningPcb->state = "E";
}
runningPcb = runningPcb->next;
cout << endl;
}
}
二、可变分区管理方式下采用首次适应算法实现主存分配和回收
(1) 可变分区方式是按作业需要的主存空间大小来分割分区的。当要装入一个作业时,根据作业需要的主存容量查看是否有足够的空闲空间,若有,则按需分配,否则,作业无法装入。假定内存大小为128K,空闲区说明表格式为:
·分区号——表示是第几个空闲分区;
·起始地址——指出空闲区的起始地址;
·长度——一个连续空闲区的长度;
(2) 采用首次适应算法分配回收内存空间。运行时,输入一系列分配请求和回
收请求。
using System;
using System.Collections.Generic;
namespace FirstFit
{
public class zone
{
public bool isfree=true;
public int freeid;
public int startaddress;
public int size;
public zone(int sa,int s)
{
startaddress = sa;
size = s;
}
}
class Program
{
static void Allocate(List<zone> listz,int s)//内存申请
{
bool isok = false;
for(int i=0; i< listz.Count; i++)
{
if (listz[i].isfree) {
if (listz[i].size >= s)
{
listz[i].isfree = false;
listz.Insert(i + 1, new zone(listz[i].startaddress + s, listz[i].size - s));
if (listz[i+1].size == 0) { listz.Remove(listz[i+1]); }
listz[i].size = s;
isok = true;
break;
}
}
}if (!isok) Console.WriteLine("内存不够!申请空间失败!");
}
static void Reclaim(List<zone> listz, int sa)//内存的回收
{
bool isok = false;
for (int i = 0; i < listz.Count; i++)
{
if (listz[i].startaddress == sa) {
if (listz[i].isfree == false)
{
listz[i].isfree = true;
isok = true;
if (i+1<listz.Count&& listz[i +1].isfree == true) {
listz[i].size += listz[i+1].size;
listz.Remove(listz[i+1]);
}
if (i - 1 >= 0 && listz[i - 1].isfree == true) {
listz[i - 1].size += listz[i].size;
listz.Remove(listz[i]);
}
break;
}
}
}
if (!isok) Console.WriteLine("地址错误!回收失败!");
}
static void display(List<zone> listz) {
int j = 1;
for (int i = 0; i < listz.Count; i++)
{
if (listz[i].isfree) { listz[i].freeid = j; j++;
System.Console.WriteLine($"分区号:{listz[i].freeid} 起始地址:{listz[i].startaddress} 长度:{listz[i].size}");
}
}
}
static void Main(string[] args)
{
Console.WriteLine("2");
List<zone> lz = new List<zone>();
lz.Add(new zone(0, 128));
display(lz);
string str= "";
Console.WriteLine("请输入一系列分配请求和回收请求(如10代表请求分配10大小的内存,-10代表回收起始地址为10的分区):");
while (str!=null)
{
str= "";
str = Console.ReadLine();
int i = int.Parse(str);
if (i > 0) {
Allocate(lz, i);
Console.WriteLine($"分配了大小为{i}的空间之后的空闲区情况为:");
}
if (i <= 0)
{
i = 0-i;
Reclaim(lz, i);
Console.WriteLine($"回收了起始地址为{i}的分区之后的空闲区情况为:");
}
display(lz);
}
}
}
}
三、用位示图管理磁盘存储空间
1) 为了提高磁盘存储空间的利用率,可在磁盘上组织成链接文件、索引文件,这类文件可以把逻辑记录存放在不连续的存储空间。为了表示哪些磁盘空间已被占用,哪些磁盘空间是空闲的,可用位示图来指出。位示图由若干字节构成,每一位与磁盘上的一块对应,“1”状态表示相应块已占用,“0”状态表示该块为空闲。位示图的形式与实习二中的位示图一样,但要注意,对于主存储空间和磁盘存储空间应该用不同的位示图来管理,绝不可混用。
(2) 申请一块磁盘空间时,由分配程序查位示图,找出一个为“0”的位,计算出这一位对应块的磁盘物理地址,且把该位置成占用状态“1”。假设现在有一个盘组共8个柱面,每个柱面有2个磁道(盘面),每个磁道分成4个物理记录。那么,当在位示图中找到某一字节的某一位为“0”时,这个空闲块对应的磁盘物理地址为:
柱面号=字节号
磁道号= 位数 / 4
物理记录号= 位数 % 4
(3) 归还一块磁盘空间时,由回收程序根据归还的磁盘物理地址计算出归还块在位示图中的对应位,把该位置成“0”。按照(2)中假设的盘组,归还块在位示图中的位置计算如下:
字节号=柱面号
位数=磁道号?4+物理记录号
(4) 设计申请磁盘空间和归还磁盘空间的程序。
#include <iostream>
using namespace std;
void Assign(int map[8][8])//分配
{
int c = 0;
int t = 0;
int p = 0;
cout << "你要申请哪一块磁盘空间啊?:"<<endl;
int i;
int j;
cout << "输入你要申请的空间的位置(第几行第几列的空间)" << endl;
cin >> i;
cin >> j;
i--;
j--;
if (map[i][j] == 0 ) {
c = i;
t = j / 4;
p = j % 4;
map[i][j] = 1;
cout << "申请成功:柱面号为" << c << ",磁道号为 " << t << ",物理记录号为 " << p << endl;
}
else {
cout << "申请失败,该空间已满。" << endl;
}
}
void Recovery(int map[8][8])//归还空间
{
int c, t, p;
cout << "请输入你想回收的物理地址:柱面号、磁道号、物理记录号" << endl;
cin >> c >> t >> p;
int i = c;
int j = t * 4 + p;
if (0<=i&&i<=7&&0<=j&&j<=7)
{
if (map[i][j] == 1) {
map[i][j] = 0;
cout << "归还空间成功!字节号为 " << i << "位数为 " << j << endl;
}
else {
cout << "归还空间失败!该空间未被占用!" << endl;
}
}
else {
cout << "归还空间失败!输入的物理地址错误!" << endl;
}
}
void Print(int map[8][8])
{
cout << "-------磁盘存储存储位示图-------" << endl;
for (int i = 0; i < 8; i++)
{
cout << "\t ";
for (int j = 0; j < 8; j++)
{
cout << map[i][j];
}
cout << endl;
}
cout << endl;
cout << endl;
}
int main()
{
int BitMap[8][8];
for (int i = 0; i < 8; i++)//位示图初始化
{
for (int j = 0; j < 8; j++)
BitMap[i][j] = 0;
}
cout << "学号: 姓名:"<<endl;
Print(BitMap);
while (true)
{
int choice = 0;
cout << "输入你要执行的操作: 1(分配) 2(回收) " << endl;
cin >> choice;
if (choice == 1)
Assign(BitMap);
else if (choice == 2)
Recovery(BitMap);
Print(BitMap);
}
return 0;
}
四、利用fork()系统调用创建进程。
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
int pid[3];
int a = 0;
while (a<3) {
printf("当前进程id为%d,父进程为%d a:%d,接下来准备创建进程\n", getpid(), getppid(), a);
pid[a] = fork();
a++;
}
printf("当前进程id为%d,父进程为%d a:%d\n", getpid(), getppid(), a);
sleep(100);
return 0;
}
五、模拟P、V操作实现同步机构,且用P、V操作解决生产者—消费者问题
#include <iostream>
using namespace std;
using std::rand;
char B[10];//缓冲区
int IN=0, OUT=0;//插入删除指针
class Process {
public :
string name;
bool isConsumer;
char product;
string state="ready";
int breakpoint=-1;
Process(string n,bool is) {
name = n; isConsumer = is;
}
Process() {
name = "Undefined";
}
};
Process p1("生产者1", false);
Process p2("生产者2", false);
Process c1("消费者1", true);
Process c2("消费者2", true);
struct SQType
{
Process* i[100];
int head = 0;
int rear = 0;
};
struct semaphore {
int count;
string name;
SQType sq;
};
semaphore Empty = { 10 ,"empty"};
semaphore full = { 0 ,"full"};
SQType ready;
void InSQType(semaphore& s,Process &p)
{
if (s.name == "empty") {
cout << p.name << "缓冲区满,"<<p.name<<"进程阻塞移入等待队列" << endl;
}
if (s.name == "full") {
cout << p.name << "缓冲区空," << p.name << "进程阻塞移入等待队列" << endl;
}
if (s.sq.head == 100)
{
cout << "队列已满!操作失败!" << endl;
}
else
{
s.sq.i[s.sq.head] = &p; //将进程移入等待队列
p.state = "wait原因是"+s.name;
s.sq.head++;
}
}
void OutSQType(semaphore& s,Process &p) {
if (s.name == "empty") {
cout << "有产品了!," << s.sq.i[s.sq.rear]->name << "进程就绪" << endl;
}
if (s.name == "full") {
cout << "有空间了!," << s.sq.i[s.sq.rear]->name << "进程就绪" << endl;
}
if (s.sq.rear == 100)
{
cout << "队列已空!操作失败!" << endl;
}
else
{
if (ready.head == 100)
{
cout << "就绪队列已满!操作失败!" << endl;
}
else {
ready.i[ready.head] = s.sq.i[s.sq.rear]; //将等待队列中的队尾进程移进就绪队列
s.sq.i[s.sq.rear]->state = "ready";
ready.head++;
s.sq.rear++;
}
}
}
void W(semaphore& s,Process &p) {
if (s.name=="empty") {
cout << p.name<<"试图生产但发现缓冲区满了" << endl;
}
if (s.name == "full") {
cout << p.name << "试图消费但发现缓冲区空了" << endl;
}
InSQType(s,p);
}
void P(semaphore& s,Process &p) {
s.count--;
if (s.count < 0) {
W(s,p);
}
}
void R(semaphore& s,Process &p) {
OutSQType(s,p);
}
void V(semaphore& s,Process &p) {
s.count++;
if (s.count <= 0) {
R(s,p);
}
}
void display() {
cout << endl<< "此时生产者1" << p1.state << endl;
cout << "此时生产者2" << p2.state << endl;
cout << "此时消费者1" << c1.state << endl;
cout << "此时消费者2" << c2.state << endl;
cout << "缓冲区情况:" << endl;
for (int i = 0; i<10; i++) { cout << B[i]<<", "; }
cout <<endl<< "缓冲区插入指针为:" << IN<<endl;
cout << "缓冲区删除指针为:" << OUT<<endl;
}
void dispatch(Process &p){//处理机调度
p.breakpoint++;if(p.breakpoint!=4)p.state = "run";
display();
if (!p.isConsumer) {
switch (p.breakpoint) {
case 0:
p.product = 'A' + rand() % (('Z' + 1) - 'A');
cout << p.name << "生产了一个" << p.product << endl;
p.state = "ready";
break;
case 1:
p.state = "ready";
cout << p.name << "对empty进行P原语操作" <<Empty.count<< endl;
P(Empty, p);
break;
case 2:p.state = "ready"; cout << p.name << "放入了" << p.product << "到缓冲区" << endl;
B[IN] = p.product;
IN = (1 + IN) % 10;
break;
case 3:p.state = "ready";
cout << p.name << "对full进行V原语操作" <<full.count<< endl;
V(full, p);
case 4:p.state = "ready";
p.breakpoint = -1; cout << p.name << "进程执行结束但又要准备重新工作" << endl; break;
}
}
if (p.isConsumer) {
switch (p.breakpoint) {
case 0:p.state = "ready";
cout << p.name << "对full进行P原语操作" <<full.count<< endl;
P(full, p); break;
case 1:p.state = "ready";
p.product = B[OUT];
B[OUT] = ' ';
cout << p.name << "从缓冲区取出了一个" << p.product << endl;
OUT = (1 + OUT) % 10; break;
case 2:p.state = "ready";
cout << p.name << "对empty进行V原语操作" <<Empty.count<< endl;
V(Empty, p); break;
case 3:cout << p.name << "从缓冲区消费了一个" << p.product << endl;
cout << p.name << "进程执行结束" << endl;
p.state = "ready";
break;
case 4:p.state = "ready";
p.breakpoint = -1; cout << p.name << "进程执行结束但又要准备重新工作" << endl; break;
}
}
}
void random() {
if (ready.head == ready.rear) {
switch (rand()%4)
{
case 0:if (p1.state == "ready")dispatch(p1); break;
case 1:if (p2.state == "ready")dispatch(p2); break;
case 2:if (c1.state == "ready")dispatch(c1); break;
case 3:if (c2.state == "ready")dispatch(c2); break;
}
}
else {
dispatch(*ready.i[ready.rear]);
ready.rear++;
}
}
int main()
{
display();
if (ready.head == ready.rear) {
for (int i=0; i < 200; i++) {
random();
}
}
}
|