实验一 Linux基本命令使用&vi编辑器&进程创建
一、实验目的
1.掌握Linux一般命令格式。
2.掌握有关文件和目录操作的常用命令。
3.掌握进程操作的常用命令。
4.熟练使用man命令。
5.学习使用vi编辑器建立、编辑、显示及加工处理文本文件。
6.掌握Linux进程的创建方法,加深对进程概念的理解,明确进程和程序的区别。
7.认识进程并发执行的实质。
二、实验原理或预习内容
(1)Linux一般命令的使用
(2)使用vi编辑器
(3)Linux进程的创建
三、实验环境
(1)硬件环境需求
(2)软件环境需求
Linux Ubuntu操作系统
四、实验内容
1.熟悉date, cal, who, clear命令。
2.在用户主目录下对文件进行操作:复制一个文件、显示文件内容、查找指定内容、排序、文件比较、文件删除等。
3.对目录进行管理:创建和删除子目录、改变和显示工作目录、列出和更改文件权限、链接文件等。
4.利用man显示date等命令的手册页。
5.显示系统中的进程信息。
6.进入和退出vi。
7.利用文本插入方式建立一个文件。
8.在新建的文本文件上移动光标位置。
9.对该文件执行删除、复原、修改、替换等操作。
五、实验结论及思考题
(1)pwd:返回当前工作目录;
ls -l:显示文件的详细信息
ls -ai:显示所有文件,包括隐藏文件,在输出的第一列显示文件的i节点号
(2)直接使用cd命令回到用户目录
mkdir建立一个子目录subdir,并将工作目录改到subdir
date > file1;cat file1 显示了系统的日期
cat subdir 显示“没有文件或目录”,因为当前目录是在subdir,而该目录下并无subdir目录或文件
man date; man date >> file1 无显示; cat file1 可以看到date的帮助手册
(3)ls -l file1:连接计数为1
Ln file1 …/fa; ls -l file1:连接计数为2
cat …/fa:显示fa文件内容
(4)head -10 file1:显示前十行
tail -10 file1:显示后十行
cp file1 file2:拷贝file1为file2
mv file2 file3:重命名file2为file3
cat f*:将当前目录下所有以f开头的文件的内容输出到了屏幕上
rm file3:file3被删除
实验二 进程管理和进程间通信
一、实验目的
四、实验内容
编写程序,用fork()创建一个子进程,父进程输出0-10范围内的偶数,子进程输出0-10范围内的奇数,要求子进程先输出,然后父进程再输出。
修改实验一中实验步骤9.的程序,用lockf( )来给每一个进程加锁,以实现进程之间的互斥。试观察记录屏幕上的显示结果,并分析原因。
分析:在第一个循环前,lockf(1, 1, 0)对stdout进行锁操作,只允许当前进程parent进行输出流操作,所以循环不间断的输出五次parent…,最后lockf(1, 0,0)释放锁使其他进程也能进行输出流操作;对子进程son和daughter也是类似的。
编写程序:用fork( )创建两个子进程,再用系统调用signal( )让父进程捕捉键盘上来的中断信号(即按^c键);捕捉到中断信号后,父进程用系统调用kill( )向两个子进程发出信号,子进程捕捉到信号后分别输出下列信息后终止:
Child process1 is killed by parent!
Child process2 is killed by parent!
父进程等待两个子进程终止后,输出如下的信息后终止:
Parent process is killed!
编写程序:实现进程的管道通信,用系统调用pipe( )建立一管道,二个子进程P1和P2分别向管道各写一句话:
Child 1 is sending a message!
Child 2 is sending a message!
父进程从管道中读出二个来自子进程的信息并显示(要求先接收P1,后P2)。
五、实验结论
signal和kill函数
父进程通过调用signal(SIGINT, stop)函数预置了对键入ctrl+C的处理,stop函数结束父进程的循环等待,同理,两个子进程分别通过signal(SIGUSR1, stop)和signal(SIGUSR2, stop)预置了父进程即将发出的SIGUSR1和SIGUSR2信号(父进程调用kill函数发出这些信号),stop函数结束子进程的循环等待,分别输出Child process1 is killed by parent!
Child process2 is killed by parent! ;父进程两次调用wait(NULL)等待两个进程结束后才输出Parent process is killed!
管道(pipe)
管道是一种遵循先进先出原则的数据结构,先进入管道的数据,也能先从管道中读出。所谓管道,是指能够连接一个写进程和一个读进程的、并允许它们以生产者—消费者方式进行通信的一个共享文件,又称为pipe文件。由写进程从管道的写入端(句柄1)将数据写入管道,而读进程则从管道的读出端(句柄0)读出数据。数据一旦读取后,就会在管道中自动删除。
创建一个无名管道的函数是int pipe(int fd[2]),它同时分配两个文件描述符,一个用于读,另一个用于写,它们是管道的两端。
管道为一临界资源,使用过程中父子进程之间除了需要读写同步以外,在对管道进行读写操作时还需要互斥进入,可以使用对文件上锁和开锁的系统调用lockf(fd,1/0,0)。
实验3-进程调度&内存分配与回收
一、实验目的
二、实验原理或预习内容
四、实验内容
I. 轮转调度
图1 进程控制块结构
图中的参数意义如下:
进程名:即进程标识。
链接指针:按照进程到达系统的时间将处于就绪状态的进程连接成一个就绪队列。指针指出下一个到达的进程控制块地址。最后一个进程的链接指针为NULL。
到达时间:进程创建时的系统时间或由用户指定,调度时,总是选择到达时间最早的进程。
估计运行时间:可由设计者任意指定一个时间值。
进程状态:为简单起见,这里假定进程有两种状态:就绪态和完成态。就绪状态用“R”表示,完成状态用“C”表示。假定进程一创建就处于就绪状态,运行结束时,就被置成完成状态。
2. 按照进程到达的先后顺序排成一个循环队列,设一个队首指针指向第一个到达进程的首址。另外再设一个当前运行的进程指针,指向当前正运行的进程。
3. 执行进程调度时,首先选择队首的第一个进程运行。
4. 由于本实验是模拟实验,所以对被选中的进程并不实际启动运行,而只是执行:估计运行时间减1,输出当前运行进程的名字。用这个操作来模拟进程的一次运行。
5. 进程运行一次后,以后的调度则将当前指针依次下移一个位置,指向下一个进程,即调整当前运行指针指向该进程的链接指针所指进程,以指示应运行进程,同时还应判断该进程的剩余运行时间是否为0。若不为0,则等待下一轮的运行;若该进程的剩余运行时间为0,则将该进程的状态置为完成状态“C”,并退出循环队列。
6. 若就绪队列不空,则重复上述步骤4和5直到所有进程都运行完为止。
7. 在所设计的调度程序中,应包含显示或打印语句,以便显示或打印每次选中进程的名称及运行一次后队列的变化情况。
#include
#include
using namespace std;
typedef struct PNode ///PCB
{
struct PNode *next; ///定义指向下一个节点的指针
char name[10]; ///定义进程名,并分配空间
int All_time; ///定义总运行时间
int Runed_Time; ///定义已运行时间
char state; ///定义进程状态Ready/End
}
*Proc; ///指向该PCB的指针
int ProcNum; ///总进程数
///初始化就绪队列
void lnitPCB(Proc &H)
{
cout << "请输入总进程个数:";
cin >> ProcNum; ///进程总个数
int Num = ProcNum;
H = (Proc)malloc(sizeof(PNode)); ///建立头结点
H->next = NULL;
Proc p = H; ///定义一个指针
cout << "总进程个数为" << ProcNum << "个,请依次输入相应信息"<<endl;
cout << endl;
while (Num--)
{
p = p->next = (Proc)malloc(sizeof(PNode));
cout << "进程名,总运行时间,已运行时间:";
cin >> p->name >> p->All_time >> p->Runed_Time;
p->state = 'R';
p->next = NULL;
}
p->next = H->next;
}
///输入运行中的进程信息
void Displnfo(Proc H)
{
Proc p = H->next;
do
{
if (p->state != 'E') ///如果该进程的状态不是End 的话
{
cout << "进程名:" << p->name << "\t总运行时间:" << p->All_time << "\t已运行时间" << p->Runed_Time << "\t状态:" << p->state << endl;
p = p->next;
}
else p = p->next;
}
while (p != H->next); ///整个进程链条始终完整,只是状态位有差异
}
///时间片轮转法
void SJP_Simulator(Proc &H)
{
cout << endl << "-------------START-----------------\n";
int flag = ProcNum; ///记录剩余进程数
int round = 0; ///记录轮转数
Proc p = H->next;
while (p->All_time>p->Runed_Time)
{
round++;
cout << endl << "Round" << round << "–正在运行" << p->name << "进程" << endl;
p->Runed_Time++; ///更改正在运行的进程的已运行的时间
Displnfo(H); ///输出此时为就绪状态的进程 的信息
if (p->All_time == p->Runed_Time)
{
//判断该进程是否结束
p->state = 'E';
flag--;
cout << p->name << "进程已运行结束,进程被删除!\n";
}
p = p->next;
while (flag && p->All_time == p->Runed_Time)
p = p->next; ///跳过先前已结束的进程
}
cout << endl << "-------------------END-----------------\n";
}
int main()
{
Proc H;
lnitPCB(H); ///数据初始化
Displnfo(H); ///输出此刻的进程状态
SJP_Simulator(H);///时间片轮转法
system("pause");
}
II.首次适配
#include
#include
#include
using namespace std;
#define maxPCB 100 //定义最大PCB结点数
#define maxPart 100 //定义最大分区
//进程PCB类型的描述
struct PCB
{
char name; //进程名
int address; //进程分区起止
int len; //进程所占分区长度
};
struct PCB PCBelem[maxPCB];//进程占用内存表
//分区类型的描述
struct Part
{
int address; //空闲分区起始地址
int len; //空闲分区大小
};
struct Part Partelem[maxPart];//空闲分区表
int length = 640 ; //系统有 640 KB 的空闲
int fnum = 0; //记录总的分区数量
int jnum = 0; //记录总的进程数量
struct Part part; //公共使用临时结点(分区)
struct PCB pcb; //公共使用临时结点(进程)
//为操作系统分配40k内存
void init4IOS(int tem)
{
length = length - tem; //剩余系统空闲空间减少
part.address = 0 + 40; //操作系统占用,空闲内存从40开始
part.len = length; //空闲内存大小
Partelem[fnum] = part; //存入空闲分区表
fnum ++; //分区数增加
}
//判定输入的进程是否存在以及位置
int getTagByPcb(char name)
{
int i;
for(i = 0; i < jnum; i ++)
{
if(name == PCBelem[i].name)
{
return i;
}
}
printf("\n\t\t找不到进程名%c,请重新输入!\n",name);
return -1;
}
//进程分配请求
void request()
{
char c = 0;
while(true)
{
printf("请输入请求内存进程 名称:");
fflush(stdin);//清空缓冲区
cin >> pcb.name;
//检查是否已存在进程
for(int j = 0; j < jnum; j++)
{
if(PCBelem[j].name == pcb.name)
{
printf("\n\t\t进程 %c 已存在,可尝试输入其他名称,也可以先回收该进程!\n",pcb.name);
return;
}
}
printf("长度:");
fflush(stdin);//清空缓冲区
cin >> pcb.len;
length = length - pcb.len; //减去相对应的操作系统剩余空闲空间
if(length <= 0)
{
if(length == 0)
{
printf("\n\t\t警告:系统资源已经全部分配!\n");
}
else
{
length = length + pcb.len; //分配失败将内存换回去,以免溢出
printf("\n\t\t未找到合适空间或者系统资源不足!\n"); return;
}
}
int i = 0;
//如果符合分配条件,进行分配
for(; i < fnum; i++)
{
//寻找一个可以分配的空间
if(pcb.len <= Partelem[i].len)
{
//改变进程占用地址
pcb.address = Partelem[i].address;
//保存该进程
PCBelem[jnum++] = pcb;
//对空闲分区进行划分
Partelem[i].address = Partelem[i].address + pcb.len;
Partelem[i].len = Partelem[i].len - pcb.len;
break;//关键作用(从低址找到一个空间就可以了,没必要再往后找了)
}
}
//除去分配后空闲空间为0的记录
if(Partelem[i].len == 0)
{
int leng = i;
//进行前移覆盖
while(leng != fnum)
{
part.address = Partelem[leng+1].address;
part.len = Partelem[leng+1].len;
Partelem[leng] = part;
leng++;
}
//分区数减少
fnum--;
}
printf("是否要继续输入进程?:");
fflush(stdin);
c = getchar();
fflush(stdin);
if(c=='N'||c=='n')
{
break;
}
}
}
//打印空闲分区
void getPrint()
{
printf("\t\t----------------------空闲分区 begin----------------------\n");
int j = 1;
for (int i = 0;i < fnum; i ++)
{
printf("\n\t\t第%d块空闲内存 起始地址为%d,容量为%d\n",j,Partelem[i].address,Partelem[i].len);
j ++;
}
printf("\n\t\t----------------------空闲分区 end ----------------------\n");
}
//打印进程
void jcPrintf()
{
printf("\n\t\t名称\t起始地址\t大小\n");
for(int i = 0 ; i < jnum; i++)
{
printf("\n\t\t%2c\t%4d\t\t%d KB\n",PCBelem[i].name,PCBelem[i].address,PCBelem[i].len);
}
}
//回收指定进程内存
void release()
{
int i = 0;
char name;
printf("\n\t\t请输入想要回收的进程名称:");
fflush(stdin);//清空缓冲区
cin >> name;
if(getTagByPcb(name) == -1)
{
printf("\n\t\t该进程不存在或者已经被回收!\n");
return;
}
printf("\n\t\t正在回收%c的内存:",name);
for(int j = 0; j < 15; j++)
{
printf("▊");
Sleep(200);
}
printf(" 完成 \n");
//for循环寻找该进程
for(i = fnum; i >= 0; i --)
{
int leng = fnum;
if(PCBelem[getTagByPcb(name)].address > Partelem[i-1].address || i == 0)
{
//while循环为该进程腾出一个位置
while(leng != i)
{
part.address = Partelem[leng-1].address;
part.len = Partelem[leng-1].len;
Partelem[leng] = part;
leng--;
}
break;//关键(从高址往前找到一个空间就可以了,没必要再往前找了)
}
}
//系统空闲空间对应增加
length = length + PCBelem[getTagByPcb(name)].len;
//使用公共的结点记录即将产生的空闲空间
part.address = PCBelem[getTagByPcb(name)].address;
part.len = PCBelem[getTagByPcb(name)].len;
//将该结点存入之前腾出的位置
Partelem[i] = part;
//分区数增加
fnum ++;
//对进程占用内存表进行调整,除去被回收进程
int leng = getTagByPcb(name);
//进行前移覆盖
while(leng != jnum)
{
pcb.name = PCBelem[leng+1].name;
pcb.address = PCBelem[leng+1].address;
pcb.len = PCBelem[leng+1].len;
PCBelem[leng] = pcb;
leng++;
}
//进程数减少
jnum--;
}
int main()
{
char tem = 0;
int OSsize = 40;
int b = 1, k;
//为操作系统分配内存
init4IOS(OSsize);
while (b)
{
system("cls");
printf("\n\n\t\t操作系统内存分配\n\n");
printf("\t\t已为操作系统分配了 40 KB 内存\n",tem);
printf("\t\t|1.... 请求分配内存 |\n");
printf("\t\t|2.... 输出空闲分区 |\n");
printf("\t\t|3.... 强制进程结束 |\n");
printf("\t\t|4.... 输出进程信息 |\n");
printf("\t\t|0.... 退出 |\n");
printf("\t\t当前操作系统空闲内存:%d KB\n",length);
printf("\n\t\t请选择:");
fflush(stdin);//清空缓冲区
cin >> k;
switch (k)
{
case 1: request(); break;
case 2: getPrint(); break;
case 3: release(); break;
case 4: jcPrintf(); break;
case 0: b = 0; break;
default:printf("\n\t\t输入无效!\n");break;
}
if (b != 0) { printf("\n\t\t"); system("pause"); }
}
}
五、实验结论及思考题
设计了一个按时间片轮转法实现进程调度的程序。
模拟实现了first fit首次适配算法。
实验4-简单文件系统模拟
课程名称:操作系统 实验教学学时:16学时
年级/班级: 2班 学生人数: 60 专业:软件工程
一、实验目的
(1)理解文件存储空间的管理、文件的物理结构和目录结构以及文件操作的实现。
(2)加深对文件系统内部功能和实现过程的理解。
二、实验原理或预习内容
模拟实现一个简单的、类Unix文件系统。
三、实验环境
(1)硬件环境需求
(2)软件环境需求
四、实验内容
五、实验结论及思考题
(1)Create函数:
void Create(char name[8], int size) {
for (int i = 0; i < 16; i++) {
// 先检查文件名是否唯一
if (i_nodes[i].used == 1) {
int j = 0;
for (j; j < 8; j++) {
if (i_nodes[i].name[j] != name[j]) {
break;
}
}
if (j == 8) {
cout << "创建失败,文件重名!" << endl;
}
}
// 找到空闲的i结点
else if (i_nodes[i].used == 0) {
// 文件名
for (int j = 0; j < 8; j++) {
i_nodes[i].name[j] = name[j];
i_nodes[i].blockPointers[j] = 0;
}
// 文件块数
i_nodes[i].size = size;
// 标记已使用
i_nodes[i].used = 1;
cout << "成功创建文件:" << i_nodes[i].name << endl;
break;
}
}
}
(1)Delete函数:
void Delete(char name[8]) {
for (int i = 0; i < 16; i++) {
// 检查文件名是否存在
if (i_nodes[i].used == 1) {
int j = 0;
for (j; j < 8; j++) {
if (i_nodes[i].name[j] != name[j]) {
break;
}
}
if (j == 8) {
memset(i_nodes[i].name, '\0', sizeof(i_nodes[i].name));
i_nodes[i].size = 0;
memset(i_nodes[i].blockPointers, 0, sizeof(i_nodes[i].blockPointers));
i_nodes[i].used = 0;
cout << "成功删除文件:" << name << endl;
break;
}
}
else {
cout << "删除失败,文件名不存在!" << endl;
}
}
}
(3)Read函数:
void Read(char name[8], int blockNum, char buf[1024]) {
for (int i = 0; i < 16; i++) {
// 检查文件名是否存在
if (i_nodes[i].used == 1) {
int j = 0;
for (j; j < 8; j++) {
if (i_nodes[i].name[j] != name[j]) {
break;
}
}
if (j == 8) {
if (blockNum < 0 || blockNum > 7) {
cout << "文件块号错误!" << endl;
}
int block_index = i_nodes[i].blockPointers[blockNum];
if (block_index == 0) {
cout << "该块号数据为空" << endl;
break;
}
for (int k = 0; k < 1024; k++) {
buf[k] = Disk[block_index][k];
}
printf("成功从文件:%s的块号为%d的读取数据:‘%s’\n", i_nodes[i].name, blockNum, buf);
break;
}
}
else {
cout << "读失败,文件名不存在!" << endl;
break;
}
}
}
(4)Write函数:
void Write(char name[8], int blockNum, char buf[1024]) {
for (int i = 0; i < 16; i++) {
// 检查文件名是否存在
if (i_nodes[i].used == 1) {
int j = 0;
for (j; j < 8; j++) {
if (i_nodes[i].name[j] != name[j]) {
break;
}
}
if (j == 8) {
if (blockNum < 0 || blockNum > 7) {
cout << "文件块号错误!" << endl;
}
int block_index = i_nodes[i].blockPointers[blockNum];
// 如果没有分配块,从空闲链表中分配
if (block_index == 0) {
for (int k = 1; k < 128; k++) {
if (block_list[k] != '1') // 空闲
{
block_index = k;
i_nodes[i].blockPointers[blockNum] = k;
block_list[k] = '1';
break;
}
}
}
memset(Disk[block_index], '\0', sizeof(Disk[block_index]));
for (int k = 0; k < 1024; k++) {
Disk[block_index][k] = buf[k];
}`在这里插入代码片`
printf("成功将'%s'写入文件:%s的块号为%d\n", buf, i_nodes[i].name, blockNum);
break;
}
}
else {
cout << "写失败,文件名不存在!" << endl;
break;
}
}
}
(5)全局变量和main函数
#include
using namespace std;
typedef struct i_node {
char name[8] = { 0 }; // 文件名
int size = 0; // 文件大小(文件块数)
int blockPointers[8] = { 0 }; // 数据块指针
int used = 0; // 0表示空闲,1表示被使用。初始化为0
}i_node;
// i结点数组
i_node* i_nodes = new i_node[16];
char block_list[128]; // 空闲块链表
char Disk[128][1024];
void Create(char name[8], int size);
void Delete(char name[8]);
void Read(char name[8], int blockNum, char buf[1024]);
void Write(char name[8], int blockNum, char buf[1024]);
int main() {
memset(block_list, '0', sizeof(block_list));
// 初始化超级块不为空
block_list[0] = '1';
char file1[8] = "aaa";
Create(file1, 4);
char buf[1024] = "hello world!";
Write(file1, 2, buf);
Read(file1, 2, buf);
Delete(file1);
return 0;
}