Linux操作系统课程设计
中国地质大学(武汉)计算机学院计算机科学与技术专业本科生《操作系统概论》课程设计(作记录)。
1. 课程设计要求
- 按时参加上机实习,不得旷课、迟到、早退。
- 每个学生需在 Linux 下用 C 语言完成六道上机实习题。
- 每个学生需独立完成上机实习,不得相互抄袭。如发现抄袭者和被抄袭者成绩均不及格。
- 每个学生需在实习结束一周内将实习报告和源程序提交到班级学习委员,再由学习委员打包发到老师的邮箱,用于检查是否存在抄袭现象,邮件中请注明学生的班级和姓名。
- 每个学生需在实习结束一周内提交纸质实习报告一份。应包括如 下内容: 1)课程设计题目 2)程序功能及设计思路 3)数据结构及算法设计 4)程序运行情况 5)编程中遇到的困难及解决方法、实习心得或良好建议。
2. 评分
- 考勤占 20%,上机验收占 40%,实习报告占 40%。
- 抄袭程序者和被抄袭程序者,课程设计成绩不及格。
- 抄袭报告者和被抄袭报告者,课程设计成绩不及格。前六次上机每次完成一道实习题,最后两次上机组织验收。
3. 上机时间地点
- 17 周 (12 月 25-29 日)周二下午、周四下午、周五上午,晚上。
- 18 周 (元月 1-5 日)周二下午、周三下午,晚上 。
- 前 7 次地点:12 班在 313 机房(Fedora) 34 班在 413 机房(Ubuntu)
- 最后一次上机: 12 班:18 周周四 晚上 34 班:18 周周五 晚上
地点:313 机房(Fedora) 上午 8:30-11:30 下午 2:00-5:00 晚上 6:30-9:30
开机时进入 Linux 系统,默认使用用户名:suer,密码:123456。若默认密码登录不了,可能是被同学修改过了,换一台电脑使用。请同学们千万不要修改系统登录密码。若有同学自己的笔记本电脑已经装了 Linux,欢迎自带电脑上机实习。
4. 每次上机实习前先自学预备知识,并在作业本上编写初步的源程序。
5. 课程设计题目课程设计分六个实验,具体如下
5.1 实验 1:作业调度
5.1.1 实验目的
- 对作业调度的相关内容作进一步的理解。
- 明白作业调度的主要任务。
- 通过编程掌握作业调度的主要算法。
5.1.2 实验内容
1、假设系统中可同时运行两道作业,给出每道作业的到达时间和运行时间,如下表所示:
作业名 | A | B | C | D | E | F | G | H | I | J |
---|---|---|---|---|---|---|---|---|---|---|
到达时间 | 0 | 2 | 5 | 7 | 12 | 15 | 4 | 6 | 8 | 10 |
运行时间 | 10 | 20 | 30 | 40 | 8 | 8 | 20 | 10 | 12 | 7 |
2、分别用先来先服务算法、短作业优先和响应比高者优先三种算法给出作业的调度顺序。
3、计算每一种算法的平均周转时间及平均带权周转时间并比较不同算法的优劣。
5.1.3 预备知识
-
响应比=等待时间/运行时间+1
-
周转时间=完成时间-到达时间 带权周转时间=周转时间/运行时间
5.1.4 源代码
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
using namespace std;
struct Work
{
char name;//作业名称
double arrive;//到达时间点
double run;//运行时间
Work() {}
Work(char n, double a, double t) :name(n), arrive(a), run(t) {}
Work operator =(const Work &rhs)
{
this->name = rhs.name;
this->arrive = rhs.arrive;
this->run = rhs.run;
return *this;
}
};
vector<Work> gWork;//存放所有的作业信息
double sum_turn, sum_dturn;//总的周转时间和带权周转时间
void ReadFile();//从txt文本中读取作业调度信息
void Output();//输出结果
void Dispatch();//调度过程中的数据处理
void Fcfs();//先来先服务算法
void Sjf();//短作业优先算法
void Hrf();//高响应比优先算法
int main()
{
ReadFile();
cout << "先来先服务算法:" << endl;
Fcfs();
cout << "短作业优先算法:" << endl;
Sjf();
cout << "高响应比算法:" << endl;
Hrf();
return 0;
}
//从文本中读取作业调度信息
void ReadFile()
{
ifstream ifile;
ifile.open("/home/jason/文档/OS/Pro_1/data.txt");
if (!ifile)
{
cout << "文件打开失败!" << endl;
return;
}
char name;
double arrive;
double run;
while (ifile >> name >> arrive >> run)
{
gWork.push_back(Work(name, arrive, run));
}
ifile.close();
}
//输出结果
void Output()
{
cout << "作业调度顺序:";
for (unsigned i = 0; i < gWork.size(); ++i)
cout << gWork[i].name << " ";
cout << endl;
cout << "平均周转时间:" << sum_turn / gWork.size() << endl;
cout << "平均带权周转时间:" << sum_dturn / gWork.size() << endl;
cout << endl;
}
//调度过程中的数据处理
void Dispatch()
{
unsigned n = static_cast<unsigned>(gWork.size());
double t1 = 0, t2 = 0;//同时运行两个作业,设置两个时间轴以记录各个作业完成情况
sum_turn = 0;//初始化
sum_dturn = 0;
for (unsigned i = 0; i < n; ++i)
{
if (t1 > t2)//表明t1有作业时t2空闲
{
if (t2 < gWork[i].arrive)
t2 += gWork[i].arrive;
t2 = t2 + gWork[i].run;
sum_turn = sum_turn + t2 - gWork[i].arrive;
sum_dturn = sum_dturn + (t2 - gWork[i].arrive) / gWork[i].run;
}
else
{
if (t1 < gWork[i].arrive)
t1 += gWork[i].arrive;
t1 = t1 + gWork[i].run;
sum_turn = sum_turn + t1 - gWork[i].arrive;
sum_dturn = sum_dturn + (t1 - gWork[i].arrive) / gWork[i].run;
}
}
}
//先来先服务算法
void Fcfs()
{
unsigned n = static_cast<unsigned>(gWork.size());
//按照来到的时间点排序,升序
Work temp;
for (unsigned i = 0; i < n; ++i)
{
for (unsigned j = 0; j < n - i - 1; ++j)
{
if (gWork[j].arrive > gWork[j + 1].arrive)
{
temp = gWork[j + 1];
gWork[j + 1] = gWork[j];
gWork[j] = temp;
}
}
}
Dispatch();
Output();
}
//短作业优先算法
void Sjf()
{
unsigned n = static_cast<unsigned>(gWork.size());
//按照作业运行时间的长短排序,升序
Work temp;
for (unsigned i = 0; i < n; ++i)
{
for (unsigned j = 0; j < n - i - 1; ++j)
{
if (gWork[j].run > gWork[j + 1].run)
{
temp = gWork[j + 1];
gWork[j + 1] = gWork[j];
gWork[j] = temp;
}
}
}
Dispatch();
Output();
}
//高响应比优先算法
void Hrf()
{
int n = static_cast<int>(gWork.size());
//按照来到的时间点排序,升序
Work temp;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < n - i - 1; ++j)
{
if (gWork[j].arrive > gWork[j + 1].arrive)
{
temp = gWork[j + 1];
gWork[j + 1] = gWork[j];
gWork[j] = temp;
}
}
}
vector<Work> temp_work;
for (int i = 0; i < n; ++i)
temp_work.push_back(gWork[i]);
gWork.clear();
sum_turn = 0;//初始化
sum_dturn = 0;
double t1 = 0, t2 = 0;//同时运行两个作业,设置两个时间轴以记录各个作业完成情况
int i = 0, flag = 0;
n = static_cast<int>(temp_work.size());
double max_rate = 0, rate = 0;
//运行第一个作业
for (i = 0; i < n; ++i)
{
//rate = wait / run + 1
rate = (temp_work[n - 1].arrive - temp_work[i].arrive) / temp_work[i].run + 1;
if (max_rate < rate)
{
max_rate = rate;
flag = i;
}
}
t1 = temp_work[flag].arrive + temp_work[flag].run;
sum_turn = t1 - temp_work[flag].arrive;
sum_dturn = (t1 - temp_work[flag].arrive) / temp_work[flag].run;
gWork.push_back(temp_work[flag]);
temp_work.erase(temp_work.begin() + flag);//已经运行的作业要删去
//运行剩下的作业
while (!temp_work.empty())
{
n = static_cast<int>(temp_work.size());
flag = 0;
max_rate = 0;
rate = 0;
if (t1 > t2)//表明t1有作业时t2空闲
{
//运行第二个作业
if (t2 == 0)
{
for (i = 0; i < n; ++i)
{
//rate = wait / run + 1
rate = (temp_work[n - 1].arrive - temp_work[i].arrive) / temp_work[i].run + 1;
if (max_rate < rate)
{
max_rate = rate;
flag = i;
}
}
t2 = temp_work[flag].arrive + temp_work[flag].run;
sum_turn = t2 - temp_work[flag].arrive;
sum_dturn = (t2 - temp_work[flag].arrive) / temp_work[flag].run;
gWork.push_back(temp_work[flag]);
temp_work.erase(temp_work.begin() + flag);//已经运行的作业要删去
continue;
}
for (i = 0; i < n; ++i)
{
if (temp_work[i].arrive <= t2)
{
rate = (t2 - temp_work[i].arrive) / temp_work[i].run + 1;
if (max_rate < rate)
{
max_rate = rate;
flag = i;
}
}
else
break;
}
t2 += temp_work[flag].run;
sum_turn = sum_turn + t2 - temp_work[flag].arrive;//turn = finish - arrive
sum_dturn = sum_dturn + (t2 - temp_work[flag].arrive) / temp_work[flag].run;
gWork.push_back(temp_work[flag]);
temp_work.erase(temp_work.begin() + flag);//已经运行的作业要删去
}
else
{
for (i = 0; i < n; ++i)
{
if (temp_work[i].arrive <= t1)
{
rate = (t1 - temp_work[i].arrive) / temp_work[i].run + 1;
if (max_rate < rate)
{
max_rate = rate;
flag = i;
}
}
else
break;
}
t1 += temp_work[flag].run;
sum_turn = sum_turn + t1 - temp_work[flag].arrive;//turn = finish - arrive
sum_dturn = sum_dturn + (t1 - temp_work[flag].arrive) / temp_work[flag].run;
gWork.push_back(temp_work[flag]);
temp_work.erase(temp_work.begin() + flag);//已经运行的作业要删去
}
}
Output();
}
数据:
A 0 7
B 2 10
C 5 20
D 7 30
E 12 40
F 15 8
G 4 8
H 6 20
I 8 10
J 10 12
5.2 实验 2:磁盘调度
5.2.1 实验目的
- 对磁盘调度的相关知识作进一步的了解,明确磁盘调度的原理。
- 加深理解磁盘调度的主要任务。
- 通过编程,掌握磁盘调度的主要算法。
5.2.2 实验内容
- 对于如下给定的一组磁盘访问进行调度:
请求服务到达 | A | B | C | D | E | F | G | H | I | J | K | L | M | N |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
访问的磁道号 | 50 | 100 | 180 | 20 | 90 | 150 | 70 | 80 | 101 | 60 | 120 | 40 | 110 | 30 |
请求服务到达 | A | B | C | D | E | F | G | H | I | J | K | L | M | N |
- 要求分别采用先来先服务、最短寻道优先以及电梯调度算法进行调度。
- 要求给出每种算法中磁盘访问的顺序,计算出平均移动道数。
- 假定当前读写头在 90 号,向磁道号增加的方向移动。
5.2.3 源代码
#include <iostream>
#include <fstream>
#include <vector>
#include <cmath>
#include <stdlib.h>
using namespace std;
struct Disk
{
char name;//请求服务到达序号
int number;//访问的磁道号
Disk() {}
Disk(char n, int num):name(n), number(num){}
Disk operator =(const Disk& rhs)
{
this->name = rhs.name;
this->number = rhs.number;
return *this;
}
};
const int StartNum = 90;//开始磁道号为90
vector<Disk> gDisk;//存储请求服务到达的序列
vector<char> track;//磁道访问顺序
vector<int> MoveDistance;//移动距离
//int FindOrder[MaxNumber];//寻号序列
double TotalDistance;//总共寻到长度
bool direction;//方向,true为向外,false为向内
void Readfile();//从txt文件中读取请求服务信息
void Sortnum();//根据磁盘号进行排序,升序
void Fcfs();//先来先服务算法
void Sstf();//最短寻道优先算法
void Scan();//电梯调度算法,也是扫描算法
void Output();//输出结果
int main()
{
Readfile();
Fcfs();
Sstf();
Scan();
return 0;
}
void Readfile()
{
ifstream ifile;
ifile.open("/home/jason/文档/OS/Pro_2/data.txt");
if(!ifile)
{
cout << "文档打开失败!" << endl;
exit(1);
}
char name;
int num;
while(ifile >> name >> num)
gDisk.push_back(Disk(name, num));
ifile.close();
}
void Sortnum()
{
int i(0), j(0);
int n = static_cast<int>(gDisk.size());
Disk temp;
for(i = 0; i < n; ++i)
{
for(j = 0; j < n - i - 1; ++j)
{
if(gDisk[j].number > gDisk[j + 1].number)
{
temp = gDisk[j + 1];
gDisk[j + 1] = gDisk[j];
gDisk[j] = temp;
}
}
}
}
//先来先服务算法
void Fcfs()
{
int tempNum = StartNum;//寻道当前所在磁道号
track.clear();
MoveDistance.clear();
int n = static_cast<int>(gDisk.size());//提出的申请数
for(int i = 0; i < n; ++i)
{
MoveDistance.push_back(abs(gDisk[i].number - tempNum));
track.push_back(gDisk[i].name);
tempNum = gDisk[i].number;
}
cout << "###############先来先服务算法:###############" << endl;
Output();
}
//最短寻道优先算法
void Sstf()
{
int tempNum = StartNum;//寻道当前所在磁道号
track.clear();
MoveDistance.clear();
vector<Disk> temp_disk;
int MinNum = 9999;//最短寻道数
int flag = 0;//做标记
int n = static_cast<int>(gDisk.size());//提出的申请数
for(int i = 0; i < n; ++i)
{
temp_disk.push_back(gDisk[i]);
}
while(!temp_disk.empty())
{
MinNum = 9999;
int num = 0;//临时存储寻道数
flag = 0;
n = static_cast<int>(temp_disk.size());
for(int i = 0; i < n; ++i)
{
num = abs(temp_disk[i].number - tempNum);
if(MinNum > num)
{
MinNum = num;
flag = i;
}
}
track.push_back(temp_disk[flag].name);//记录第一个寻道号
tempNum = temp_disk[flag].number;
MoveDistance.push_back(MinNum);//记录第一个寻道距离
temp_disk.erase(temp_disk.begin() + flag);//已访问的删去
}
cout << "###############最短寻道优先服务算法:###############" << endl;
Output();
}
//电梯调度算法
void Scan()
{
track.clear();
MoveDistance.clear();
//按磁道号进行排序,升序
Sortnum();
int n = static_cast<int>(gDisk.size());
vector<Disk> temp_disk;
for(int i = 0; i < n; ++i)
temp_disk.push_back(gDisk[i]);
int tempNum = StartNum;
int flag(0);
for(int i = 0; i < n; ++i)
{
if(temp_disk[i].number < tempNum)
continue;
else
{
flag = i;
break;
}
}
//最开始是向外寻道
for(int i = flag; i < n; ++i)
{
track.push_back(temp_disk[i].name);
MoveDistance.push_back(temp_disk[i].number - tempNum);
tempNum = temp_disk[i].number;
}
//然后向里寻道
for(int i = flag - 1; i >= 0; --i)
{
track.push_back(temp_disk[i].name);
MoveDistance.push_back(tempNum - temp_disk[i].number);
tempNum = temp_disk[i].number;
}
cout << "###############电梯调度算法:###############" << endl;
Output();
}
//输出结果
void Output()
{
int n = static_cast<int>(gDisk.size());//提出的申请数
TotalDistance = 0;
cout << "寻道序号:" << " " << "寻道长度:"<<endl;
for(int i = 0; i < n; ++i)
{
cout << track[i] << " "<< MoveDistance[i] << endl;
TotalDistance += MoveDistance[i];
}
cout << "平均寻道长度:" << (static_cast<double>(TotalDistance) / n) << endl;
cout << endl;
}
数据:
A 30
B 50
C 100
D 180
E 20
F 90
G 150
H 70
I 80
J 10
K 160
L 120
M 40
N 110
5.3 实验 3:熟悉 linux 文件系统调用
5.3.1 实验目的
- 掌握 linux 提供的文件系统调用的使用方法;
- 熟悉文件系统的系统调用用户接口;
- 了解操作系统文件系统的工作原理和工作方式。
5.3.2 实验内容
使用文件系统调用编写一个文件工具 filetools,使其具有以下功能:
1.创建新文件 2.写文件 3.读文件 4.修改文件权限 5.查看当前文件权限 0.退出
提示用户输入功能号,并根据用户输入的功能选择相应的功能。 文件按可变记录文件组织,具体记录内容自行设计。
5.3.3 预备知识
用户在针对文件进行操作之前时一定要先打开它,这是由于系统需要根据用户提供的参数来查找文件 的目录项,并由目录项找到文件的磁盘 i 结点,再将它调到内存 i 结点,才能建立用户进程与该文件之间的联系。
同样,在文件操作完毕后要关闭文件,以切断用户进程与文件的联系,释放相关资源。
Open 系统调用
int open(const char *path, int flags);
int open(const char *path, int flags,mode_t mode);
一般情况下使用两个参数的格式,只有当打开的文件不存在时才使用 3 个参数的格式。参数:
- Path 指向所要打开的文件的路径名指针。
- Flag 标志参数,用来规定打开方式,必须包含以下 3 个之一:
- O_RDONLY 只读方式
- O_WRONLY 只写方式
- O_RDWR 读写方式
- 利用按位逻辑或“|”对下列标志进行任意组合:
- O_CREAT 如果文件不存在则创建该文件,若存在则忽略。
- O_TRUNC 如果文件存在则将文件长度截为 0,属性和所有者不变。
- C_EXECL 如果文件存在且O_CREAT 被设置则强制 open 调用失败。
- O_APPEND 每次写入时都从文件尾部开始。
- Mode 是文件的访问权限,分为文件所有者、文件用户组和其他用户。
Close 系统调用
对于一个进程来说可以同时打开的文件是有限的,为了使文件标识符能够及时释放,系统必须提供关闭文件操作。
Int close(int fd)
- Fd 为打开文件时系统返回的文件标识符。
- 系统执行该系统调用时,根据 fd 值在该进程的进程打开文件表中找到 fd 标识,根据指针找到系统打开 文件表,再找到内存i 结点表中相应的 i 结点,对其i_count 进行减 1 操作, 然后释放系统打开文件表中的表项和进程打开文件表的表项。
- 结果:调用成功返回 0。
5.3.4 源代码
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>//flag标志参数
#include <sys/stat.h>//文件权限标志位
#include <sys/types.h>
#define MAX 10000
int fd;//打开文件时系统返回的文件标识符
char *FileName;//要创建的文件名
void Menu();//显示主界面菜单
void CreateFile();//创建新文件
void WriteFile();//写文件
void ReadFile();//读文件
void ModifyPermission();//修改文件权限
void ShowPermission();//查看文件权限
int main()
{
int select = 0;
printf("########欢迎使用文件工具FileTools#######\n");
while(1)
{
Menu();
scanf("%d", &select);
switch (select) {
case 0:
close(fd);
return 0;
case 1:
CreateFile();break;
case 2:
WriteFile();break;
case 3:
ReadFile();break;
case 4:
ModifyPermission();break;
case 5:
ShowPermission();break;
default:
printf("输入有误!请重新输入:\n");
}
}
return 0;
}
void Menu()
{
printf("########################################\n");
printf("########1.创建新文件####################\n");
printf("########2.写文件########################\n");
printf("########3.读文件########################\n");
printf("########4.修改文件权限##################\n");
printf("########5.查看文件权限##################\n");
printf("########0.安全退出######################\n");
printf("请选择:\n");
}
//创建新文件
void CreateFile()
{
char name[32];
printf("请输入要创建的文件名:\n");
scanf("%s", name);
FileName = name;
//O_RDWR:读写方式
//O_TRUNC:如果文件存在则将文件长度截为0,属性和所有者不变
//O_CREAT:如果文件不存在则创建该文件,若存在则忽略
fd = open(FileName, O_RDWR|O_TRUNC|O_CREAT, 0750);
if(fd < 0)
perror("Create");
else
{
printf("文件创建成功!\n");
printf("fd =%d\n", fd);//打印文件的fd
close(fd);//关闭文件
}
}
//写文件
void WriteFile()
{
char name[32];
printf("请输入要写入信息的文件名:\n");
scanf("%s", name);
FileName = name;
char buffer[MAX];//存取写入的信息
fd = open(FileName, O_WRONLY|O_APPEND);
if(fd < 0)
{
perror("Write");
return;
}
printf("请输入要写入的信息:\n");
int num = read(0,buffer, MAX);//读取输入信息,0表示标从键盘标准输入
write(fd, buffer, num);//将读入的信息写入文件
printf("文件写入成功!\n");
close(fd);//关闭文件
}
//读文件
void ReadFile()
{
char buffer[MAX];
char name[32];
printf("请输入要读取的文件名:\n");
scanf("%s", name);
FileName = name;
fd = open(FileName,O_RDONLY);
if(fd < 0)
{
perror("Wrong");
return;
}
int num = read(fd, buffer, MAX);
printf("\n");
write(1, buffer, num);//向显示屏输出,1表示标准输出
printf("\n文件读取成功!\n");
close(fd);
}
//修改文件权限
void ModifyPermission()
{
char name[32];
printf("请输入要修改权限的文件名:\n");
scanf("%s", name);
FileName = name;
fd = open(FileName, O_RDONLY);
if(fd < 0)
{
perror("Modify");
return;
}
printf("0.所有者用户读写执行\n");
printf("1.所有者用户只可读\n");
printf("2.所有者用户只可写\n");
printf("3.所有者用户只可执行\n");
printf("4.用户组读写执行\n");
printf("5.用户组只可读\n");
printf("6.用户组只可写\n");
printf("7.用户组只可执行\n");
printf("请选择0-7:");
int select;
scanf("%d", &select);
switch (select)
{
case 0:
chmod(FileName, S_IRWXU);break;//文件所有者读写执行权限
case 1:
chmod(FileName, S_IRUSR);break;//文件所有者读权限
case 2:
chmod(FileName, S_IWUSR);break;//文件所有者写权限
case 3:
chmod(FileName, S_IXUSR);break;//文件所有者执行权限
case 4:
chmod(FileName, S_IRWXG);break;//文件所属组读写执行权限
case 5:
chmod(FileName, S_IRGRP);break;//文件所属组读权限
case 6:
chmod(FileName, S_IWGRP);break;//文件所属组写权限
case 7:
chmod(FileName, S_IXGRP);break;//文件所属执行权限
default:
printf("错误选择!\n");
}
close(fd);
}
//查看文件权限
void ShowPermission()
{
//stat();获取文件信息
char name[32];
printf("请输入要修改权限的文件名:\n");
scanf("%s", name);
FileName = name;
char *path = "/bin/ls";
char *argv[4] = {"ls", "-l", FileName, NULL};
execv(path, argv);
}
5.4 实验 4:进程管理
5.4.1 实验目的
- 理解进程的概念,明确进程和程序的区别。
- 理解并发执行的实质。
- 掌握进程的同步、撤销等进程控制方法。
5.4.2 实验内容
-
父进程使用系统调用
pipe()
建立一个管道,然后使用系统调用fork()
创建两个子进程:子进程1和子进程2。 -
子进程1每隔1秒通过管道向子进程2发送数据:
I send message x times.
(x 初值为1,以后发送一 次后做加一操作)子进程 2 从管道读出信息,并显示在屏幕上。 -
父进程用系统调用
signal()
来捕捉来自键盘的中断信号(即按Ctrl+C 键);当捕捉到中断信号后,父进程用系统调用kill()
向两个子进程发出信号,子进程捕捉到信号后分别输出如下信息后终止:Child Process 1 is killed by Parent! Child Process 2 is killed by Parent!
。 -
父进程等待两个子进程终止后,释放管道并输出如下的信息后终止
Parent Process is Killed!
。
5.4.3 源代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
pid_t child1, child2;//两个子进程
int fd[2];
void SignalHandler1(int iSignNo);//信号处理
void SignalHandler2(int iSignNo);
int main()
{
int Count = 1;
char buffer[40];
char info[40];
pid_t getpid();
printf("Process Parent PID:%d\n", getpid());
//创建无名管道,只能在父子或兄弟进程间通信
//fd[0]只能用于读,fd[1]只能用于写
if(pipe(fd) < 0)
{
printf("管道创建失败!\n");
return -1;
}
//设置软中断信号SIGINT,终端输入Ctrl+C,进程终止
signal(SIGINT, SignalHandler1);
//创建子进程1,2
child1 = fork();
if(child1 == 0) //子进程1
{
printf("Child Process 1 PID:%d\n", getpid());
//设置忽略信号,如果不设置,在子进程运行过程直接Ctrl+C,会造成进程终止,
//产生僵尸进程,因此需要在子进程中屏蔽,等子进程被父进程kill之后,再在
//父进程中Ctrl+C使进程完全结束,就不会产生僵尸进程了
signal(SIGINT, SIG_IGN);
//设置信号SIGUSR1
signal(SIGUSR1, SignalHandler2);
while(1)
{
close(fd[0]);
sprintf(info, "I send message %d times", Count);
//管道长度受到限制,管道写满时写入操作将被阻塞,直到管道中的数据被读取
write(fd[1], info, 30);//发送数据至管道数据
++Count;//计数器+1
sleep(1);//睡眠1s
}
}
else if(child1 > 0)//返回父进程
{
child2 = fork(); //父进程创建子进程2
if(child2 == 0) //子进程2
{
printf("Child Process 2 PID:%d\n", getpid());
//设置忽略信号
signal(SIGINT, SIG_IGN);
//设置信号SIGUSR1
signal(SIGUSR2, SignalHandler2);
while(1)
{
close(fd[1]);
//当数据被读取后,数据将自动被管道清除
//接受管道数据
read(fd[0], buffer, 40);
//显示管道数据
printf("%s\n", buffer);
}
}
else //返回父进程
{
//等待子进程1,2退出
waitpid(child1, NULL, 0);
//printf("Child Process 1 is over!\n");
waitpid(child2, NULL, 0);
//printf("Child Process 2 is over!\n");
//关闭管道
close(fd[0]);
close(fd[1]);
printf("Parent Process is Killed!\n");
}
}
return 0;
}
void SignalHandler1(int iSignNo)
{
printf("\nParent recive signal Ctrl+C\n");
if(iSignNo == SIGINT)//传递SIGUSR信号给子进程
{
//SIGUSR1和SIGUSR2是用户自定义信号,进程终止
kill(child1, SIGUSR1);
kill(child2, SIGUSR2);
}
}
void SignalHandler2(int iSignNo)
{
close(fd[0]);
close(fd[1]);
if(child1 == 0 && iSignNo == SIGUSR1)
{
printf("Child Process 1 is killed by Parent!\n");
exit(0);
}
if(child2 == 0 && iSignNo == SIGUSR2)
{
printf("Child Process 2 is killed by Parent!\n");
exit(0);
}
}
5.5 实验 5:请求分页系统中的置换算法
5.5.1 实验目的
- 了解虚拟存储技术的特点;
- 掌握请求分页系统的页面置换算法。
5.5.2 实验内容
1、通过如下方法产生一指令序列,共 320 条指令。
A. 在[0,319]的指令地址之间随机选取一起点 M;
B. 顺序执行一条指令,即执行地址为 M+1 的指令;
C. 在前地址[0,M+1]中随机选取一条指令并执行,该指令的地址为 M1;
D. 顺序执行一条指令,其地址为 M1+1;
E. 在后地址[M1+2,319]中随机选取一条指令并执行,该指令的地址为 M2;
F. 顺序执行一条指令,其地址为 M2+1;
G. 重复A—F,直到执行 320 次指令。
2、指令序列变换成页地址流,设 :
(1)页面大小为1K;
(2) 用户内存容量为 4 页到 32 页,步长为 1;
(3)用户虚存容量为 32K。在用户虚存中,按每页存放10 条指令排列虚存地址,即 320 条指令在虚存中的存放方式为: 第0条—第9条指令为第0页(对应虚存地址为[0,9]);第10条—第19条指令为第1页(对应虚存地址为[10,19]) ; …………………… 第310条—第319条指令为第31页(对应虚存地址为[310,319]) ;
按以上方式,用户指令可组成 32 页。
3、计算并输出下述各种算法在不同内存容量下的命中率。
A. 先进先出(FIFO)页面置换算法
B. 最近最久未使用(LRU)页面置换算法–最近最少使用算法
C. 最佳(Optimal)页面置换算法
5.5.3 源代码
#include<iostream>
#include<ctime>
#include<cstdlib>
using namespace std;
int N = 3;//内存
int Process[320];//页面队列
int Memory[32];//块数
int OPTQueue[320];//OPT算法的队列
int FIFOQueue[320];//FIFO算法队列
int LRUQueue[320];//LRU算法队列
int ttime[320];//设置的一个时间标志,FIFO算法时判断哪个是最先进来的
int flag[320];//设置一个标志,LUR算法判断哪个是最近最久未使用的
int ProcessNum;//页面数
int id[320];//320个进程序列
int address[320];//zhilingzidian
int Size = 0;
int Randf(int l, int r) { return (rand() % (r - l + 1) + l); }
void CreateProcess()
{
srand(static_cast<unsigned>(time(NULL)));
Size = 0;
for (int i = 0;i < 64;++i)
{
int m = Randf(0, 319);
id[Size++] = m + 1;
m = Randf(0, m + 1);
id[Size++] = m;
id[Size++] = m + 1;
m = Randf(m + 2, 319);
id[Size++] = m;
id[Size++] = m + 1;
}
cout << "指令序列:" << endl;
for (int i = 0;i < Size;++i)
cout << id[i] << " ";
cout << endl;
}
//OPT算法找到最长未使用的
int longest(int start)
{
int i;
int count[320];
for (int i = 0;i < 320;++i)
count[i] = 0;
for (int j = 0;j < N;++j)
{
for (i = start + 1;i < ProcessNum;i++)
{
if (Memory[j] != OPTQueue[i])
count[j]++;
if (Memory[j] == OPTQueue[i])
break;
}
}
int ti = -1, mmax = -1;
for (int i = 0;i < N;++i)
{
if (mmax < Memory[i])
{
mmax = Memory[i];
ti = i;
}
}
return ti;
}
//OPT 算法
void OPT()
{
int i, j, k;
int num = N;
for (i = 0;i < ProcessNum;i++)
{
OPTQueue[i] = Process[i];
}
for (i = 0;i < N;i++)
{
Memory[i] = OPTQueue[i];
}
for (i = N;i < ProcessNum; ++i)
{
for (j = 0;j < N;j++)
{
if (Memory[j] == OPTQueue[i])
break;
}
if (j == N)
{
k = longest(i);
Memory[k] = OPTQueue[i];
num++;
}
}
cout << "命中次数:" << ProcessNum - num << endl;
float str;
str = (float)(ProcessNum - num) / ProcessNum;
cout << "命中率=" << str * 100 << "%" << endl;
}
//FIFO算法找到最早进来的那个
int MaxTime()
{
int ti = -1, mmin = 100000;
for (int i = 0;i<N;++i)
{
if (mmin > ttime[i])
{
mmin = ttime[i];
ti = i;
}
}
return ti;
}
//FIFO算法
void FIFO()
{
int i, j, k;
int num = N;
for (i = 0;i<ProcessNum;i++)
{
FIFOQueue[i] = Process[i];
}
for (i = 0;i<N;i++)
{
Memory[i] = FIFOQueue[i];
ttime[i] = i;
}
for (i = N;i<ProcessNum;i++)
{
for (j = 0;j<N;j++)
{
if (Memory[j] == FIFOQueue[i])
break;
}
if (j == N)
{
k = MaxTime();
ttime[k] = i;
Memory[k] = FIFOQueue[i];
num++;
}
}
cout << "命中次数:" << ProcessNum - num << endl;
float str;
str = (float)(ProcessNum - num) / ProcessNum;
cout << "命中率=" << str * 100 << "%" << endl;
}
//LRU算法找到最近最久未使用的
int MinFlag()
{
int ti = -1, mmin = 100000;
for (int i = 0;i < N;++i)
{
if (mmin > flag[i])
{
mmin = flag[i];
ti = i;
}
}
return ti;
}
//LRU算法
void LRU()
{
int i, j, k;
int num = N;
for (i = 0;i<ProcessNum;i++)
{
LRUQueue[i] = Process[i];
}
for (i = 0;i<N;i++)
{
Memory[i] = LRUQueue[i];
flag[i] = i;
}
for (i = N;i<ProcessNum;i++)
{
for (j = 0;j<N;j++)
{
if (Memory[j] == LRUQueue[i])
{
flag[j] = i;
break;
}
}
if (j == N)
{
k = MinFlag();
flag[k] = i;
Memory[k] = LRUQueue[i];
num++;
}
}
cout << "命中次数:" << ProcessNum - num << endl;
float str;
str = (float)(ProcessNum - num) / ProcessNum;
cout << "命中率=" << str * 100 << "%" << endl;
}
int main()
{
int i;
while (N != 0) {
cout << "-------------------页面置换算法-------------------------" << endl;
cout << "进程数:320" << endl;
ProcessNum = 320;
CreateProcess();//生成320个指令序列
cout << "指令序列生成完毕" << endl << endl;
for (i = 0;i < ProcessNum; ++i)
Process[i] = id[i] / 10;
N = 4;
while (N != 1 && N != 0) {
cout << "请输入内存容量:[4-32]\n1 for 重新生成指令序列\n0 for 退出程序" << endl;
cin >> N;
if (N == 0 || N == 1)
break;
cout << "OPT最佳置换算法" << endl;
OPT();
cout << endl;
cout << "FIFO先进先出页面置换算法" << endl;
FIFO();
cout << endl;
cout << "LRU最近最久未使用置换算法" << endl;
LRU();
cout << endl;
}
}
return 0;
}
5.6 实验 6:进程通信
5.6.1 实验目的
- 理解管道机制、消息缓冲队列、共享存储区机制进行进程间的通信;
- 理解通信机制。
5.6.2 实验内容
编写一主程序可以由用户选择如下三种进程通信方式:
-
使用管道来实现父子进程之间的进程通信:子进程向父进程发送自己的进程标识符,以及字符串
is sending a message to parent
。父进程则通过管道读出子进程发来的消息,将消息显示在屏幕上,然后终止。 -
使用消息缓冲队列来实现
client
进程和server
进程之间的通信:server
进程先建立一个关键字为SVKEY
(如75)的消息队列,然后等待接收类型为REQ
(例如 1)的消息;在收到请求消息后,它便显示字符串serving for client
和接收到的client
进程的进程标识数,表示正在为client
进程服务;然后再向client
进程发送应答消息, 该消息的类型是client
进程的进程标识数,而正文则是server
进程自己的标识 ID。client
进程则向消息队列发送类型为REQ
的消息(消息的正文为自己的进程标识 ID) 以取得sever
进程的服务,并等待server
进程发来的应答;然后显示字符串receive reply from
和接收到的server
进程的标识 ID。 -
使用共享存储区来实现两个进程之间的进程通信:进程 A 创建一个长度为 512 字节的共享内存,并显示写入该共享内存的数据;进程 B 将共 享内存附加到自己的地址空间,并向共享内存中写入数据。
5.6.3 源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
struct msgform
{
long mtype;
char mtext[250];
}msg;
int msgqid, pid, *pint;
void Menu();
void UsePipe();//使用管道实现父子进程之间的进程通信
void MessageDeque();//使用消息缓冲队列实现C/S进程之间的通信
void Client();
void Server();
void ShareMemory();//使用共享存储区来实现两个进程之间的进程通信
void A();
void B();
int main()
{
printf("######欢迎使用“进程通信系统”######\n");
while (1) {
Menu();
int select = 0;
scanf("%d", &select);
switch (select) {
case 0:
return 0;
case 1:
UsePipe();break;
case 2:
MessageDeque();break;
case 3:
ShareMemory();break;
default:
printf("您输入的选择无效,请重新输入!\n");
break;
}
}
return 0;
}
void Menu()
{
printf("##################################\n");
printf("1.管道实现父子进程通信\n");
printf("2.消息队列C/S进程通信\n");
printf("3.共享存储区的进程通信\n");
printf("0.安全退出\n");
printf("请选择通信方式:\n");
}
void UsePipe()
{
int fd[2];//fd[0]用于读,fd[1]用于写
pid_t child;
pid_t parent = getpid();
printf("Process Parent PID:%d\n", parent);
if(pipe(fd) < 0)
{
printf("管道创建失败!\n");
return;
}
child = fork();
if(child == 0)//进入子进程
{
char info[40];
close(fd[0]);
sprintf(info, "PID:%d is sending a message to parent", getpid());
write(fd[1], info, 40);
sleep(1);//休眠1s
exit(0);
}
else //返回主进程
{
close(fd[1]);
char buffer[40];
read(fd[0], buffer, 40);
printf("%s\n", buffer);
close(fd[0]);
close(fd[1]);
waitpid(child, NULL, 0);
printf("Process child is over!\n");
}
}
void MessageDeque()
{
int i = 0;
while((i = fork()) == -1);//创建进程1
if(!i)
Server();
while((i = fork()) == -1);//创建进程2
if(!i)
Client();
wait(0);
wait(0);
}
void Client()
{
msgqid = msgget(75, 0400);//打开75消息队列
pid = getpid();
pint = (int *)msg.mtext;
*pint = pid;
msg.mtype = 1;//消息类型为1
msgsnd(msgqid, &msg, sizeof(int), 0);//发送消息
msgrcv(msgqid, &msg, 250, pid, 0);//接收消息
printf("(client):reveice reply from pid=%d\n", *pint);
exit(0);
}
void Server()
{
msgqid = msgget(75, 0400|IPC_CREAT);//创建75消息队列
msgrcv(msgqid, &msg, 250, 1, 0);//接收消息
//把正文的内容传给pint,并强制转换类型
pint = (int *)msg.mtext;
pid = *pint;//获得client进程标识数
printf("(server):serving for client pid=%d\n", pid);
msg.mtype = pid;//消息类型为client
*pint = getpid();
msgsnd(msgqid, &msg, sizeof(int), 0);//发送消息
exit(0);
}
void ShareMemory()
{
//需要包含头文件<sys/types.h>,<sys/ipc.h>,<sys/shm.h>
//shmget函数创建或打开一个新区,返回一个共享存储区ID
//int shmget(key_t key, int size, int shmflg)
//key:共享存储区的名字,关键字,int型
//size:共享存储区的大小(以字节计)
//shmflg:用户设置的标志,如IPC_CREAT
//IPC_CREAT 表示若系统中尚无指名的共享存储区,
//则由核心建立一个共享存储区;如系统中已有共享存储区,便忽略IPC_CREAT
//成功返回共享内存的标识符;不成功返回-1,errno 储存错误原因。
pid_t a = 0;
while ((a = fork()) == -1);
if(!a)
A();
while ((a = fork()) == -1);
if(!a)
B();
wait(0);
wait(0);
}
void A()
{
int shmid = shmget(100, 512, 0777|IPC_CREAT);//创建共享存储区
char *addr = shmat(shmid, 0, 0);//获取首地址
printf("get:%s\n", addr);
if(shmdt(addr) == 0)
printf("A断开链接成功!\n");
exit(0);
}
void B()
{
int shmid = shmget(100, 512, 0777|IPC_CREAT);//打开共享存储区
char *message[] = {"B writes something!"};
char *addr = shmat(shmid, 0, 0);//获得共享存储区首地址
memset(addr, '\0', 512);//addr内容初始化
strncpy(addr, message[0], 512);//向内存中写信息
if(shmdt(addr) == 0)
printf("B断开链接成功!\n");
exit(0);
}
6. 课程设计报告及源代码
https://download.csdn.net/download/diligent_lee/85015283