neu-os-experiments 注意注意一定要看!!! 使用阿里OSS+typora+picgo图床一键自动上传图片的时候,总是会出现复制到md文档里面的图片顺序出现混乱的情况,我的初步猜测是picgo图片上传的时候速度不一样与阿里OSS自动生成的图片ID不一样导致的矛盾。(事实上,我相信有基础的兄弟们就算是看乱序的图片也能对应的上正确的位置)
我想修改来着,但工作量繁琐且收效甚微。(因为我的GitHub仓库有完整的报告,所以直接上我的GitHub仓库吧,
https://github.com/BradTorres/neu-os-experiments
如果想看初步粗略看的话,可以接着往下看)
正文 NEU操作系统实验(共四次)
详情见实验ppt
实验一:三状态转换(五状态,七状态)
实验二:生产者消费者模型
实验三:管道通信与进程同步
实验四:页面置换算法
实验内容说明请参考实验PPT
实验一 一. 实验内容 1.设计并实现一个模拟进程状态转换及其相应PCB组织结构变化的程序。
2.独立设计、编写、调试程序。
3.程序界面应能反映出在模拟条件下,进程之间状态转换及其对应的PCB组织的变化。
4.进程的状态模型(三状态、五状态、七状态或其它)可自行选择。
二. 程序流程图
三. 程序描述 使用C++进行开发,实现了一个五状态的进程间状态转移的模型。同时设定了内存的机制,具备一定的运行内存限制(100M),若处理进程内存超过系统内存限制则会先被放入等待队列中。完成了界面功能,可以显示实时的不同队列中的进程的id。
自定义了结构体 pcb,组织方式采用的是链表。结构体 pcb用来存储一个进程的 id和指向下一个节点的指针next。同一状态的进程其PCB成一队列,多个状态对应多个不同的队列;
所以这里形成了不同的队列:新建队列、就绪队列、运行队列、阻塞队列。可以实现初始化新建队列、就绪队列、运行队列、阻塞队列以及下述七个操作。
0:exit
1:Dispatch(ReadyQueue->RunningQueue)
2:Timeout(RunningQueue->ReadyQueue)
3:Event Wait(RunningQueue->BlockedQueue)
4:Event Occurs(BlockedQueue->ReadyQueue)
5:Admit(NewQueue->ReadyQueue)
6:Release(RunningQueue->Exit)
定义了 4 个队列,分别用来储存 Ready,Running,Block,New 队列。同时考虑到了非法的输入,对于不同的非法输入都有处理。同时设定了不同操作之间的在不同情况下的连带关系,例如结束进程后会自动把新的就绪态进程派遣到运行队列中等等。
四. 实验结果(部分截图) 创建进程
5:Admit(NewQueue->ReadyQueue)进入就绪队列。其他操作类似,都是实现进程队列的切换。
五. 部分核心代码 结构体定义 1 2 3 4 5 6 7 8 9 10 11 typedef int ElemType;typedef struct pcb { ElemType id; struct pcb *next; } pcb; pcb *NewQueue, *ReadyQueue, *RunningQueue, *BlockedQueue, *ExitQueue;
具体函数见我的GitHub仓库 主函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 int main () { NewQueue = create (); ReadyQueue = new pcb; ReadyQueue->next = NULL ; RunningQueue = new pcb; RunningQueue->next = NULL ; BlockedQueue = new pcb; BlockedQueue->next = NULL ; ExitQueue = new pcb; ExitQueue->next = NULL ; show (); int ChangeNum; cout << "Please choose one of the following actions" << endl; cout << "0:exit" << endl; cout << "1:Dispatch(ReadyQueue->RunningQueue)" << endl; cout << "2:Timeout(RunningQueue->ReadyQueue)" << endl; cout << "3:Event Wait(RunningQueue->BlockedQueue)" << endl; cout << "4:Event Occurs(BlockedQueue->ReadyQueue)" << endl; cout << "5:Admit(NewQueue->ReadyQueue)" << endl; cout << "6:Release(RunningQueue->Exit)" << endl; while (cin >> ChangeNum) { if (ChangeNum == 0 ) { break ; } else if (ChangeNum == 1 ) { if (ReadyQueue->next) { if (RunningQueue->next) { insert (BlockedQueue, RunningQueue->next); del (RunningQueue); } insert (RunningQueue, ReadyQueue->next); del (ReadyQueue); } else { if (BlockedQueue->next) { insert (BlockedQueue, RunningQueue->next); del (RunningQueue); insert (RunningQueue, BlockedQueue->next); del (BlockedQueue); } } } else if (ChangeNum == 2 ) { if (RunningQueue->next) { if (ReadyQueue->next) { insert (ReadyQueue, RunningQueue->next); del (RunningQueue); insert (RunningQueue, ReadyQueue->next); del (ReadyQueue); } } } else if (ChangeNum == 3 ) { if (RunningQueue->next) { if (ReadyQueue->next) { insert (BlockedQueue, RunningQueue->next); del (RunningQueue); insert (RunningQueue, ReadyQueue->next); del (ReadyQueue); } else { insert (BlockedQueue, RunningQueue->next); del (RunningQueue); insert (RunningQueue, BlockedQueue->next); del (BlockedQueue); } } } else if (ChangeNum == 4 ) { if (BlockedQueue->next) { insert (ReadyQueue, BlockedQueue->next); del (BlockedQueue); } } else if (ChangeNum == 5 ) { if (NewQueue->next) { insert (ReadyQueue, NewQueue->next); del (NewQueue); } } else if (ChangeNum == 6 ) { if (RunningQueue->next) { insert (ExitQueue, RunningQueue->next); del (RunningQueue); if (ReadyQueue->next) { insert (RunningQueue, ReadyQueue->next); del (ReadyQueue); } } } else { cout << "You can only type in numbers in range(0~6)" << endl; } show (); cout << "Please choose one of the following actions" << endl; cout << "0:exit" << endl; cout << "1:Dispatch(ReadyQueue->RunningQueue)" << endl; cout << "2:Timeout(RunningQueue->ReadyQueue)" << endl; cout << "3:Event Wait(RunningQueue->BlockedQueue)" << endl; cout << "4:Event Occurs(BlockedQueue->ReadyQueue)" << endl; cout << "5:Admit(NewQueue->ReadyQueue)" << endl; cout << "6:Release(RunningQueue->Exit)" << endl; } return 0 ; }
实验二 一. 实验内容 1.调试、运行给出的程序,从操作系统原理的角度验证程序的正确性。
2.发现并修改程序中的原理性错误或不完善的地方。
3.鼓励在程序中增加新的功能。完成基本。
二. 程序流程图 见我的GitHub仓库
三. 程序描述 利用写指针和读指针实现了生产者和消费者对缓冲区按顺序进行生产和消费。同时设定了缓冲区 BUFFER,设定了缓冲区的大小。
当缓冲区已满时,再次创建的生产进程会进入阻塞队列中,而下次消费者消费了一个产品后,会自动将阻塞队列中的生产进程执行并将产品放入缓冲区。同理消费者也有消费者阻塞队列,也能在产品生产后自动执行消费进程。
四. 实验结果(部分截图) 按P创建生产进程,将产出产品放入缓冲区。
按C创建消费进程,对缓存区产品进行消费。
当缓冲区满时,新创建的生产者进程会被加入生产者阻塞队列。
当消费者进程执行后,缓冲区重新获得空间,会自动执行阻塞队列中的生产者进程。
消费者进程同样会被阻塞,并放入阻塞队列。
五. 部分核心代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 void produce () { if (findEmpty (buffer, BUFFER_SIZE) == -1 ) { number = number + 1 ; producer++; pQueue.push (number); } else if (isEmpty (buffer, BUFFER_SIZE)) { if (cQueue.empty ()) { number = number + 1 ; buffer[writeptr] = number; } else { number = number + 1 ; buffer[writeptr] = number; show (); sleep (2 ); buffer[readptr] = 0 ; readptr = (readptr + 1 ) % BUFFER_SIZE; consumer--; cQueue.pop (); } writeptr = (writeptr + 1 ) % BUFFER_SIZE; } else { number = number + 1 ; buffer[writeptr] = number; writeptr = (writeptr + 1 ) % BUFFER_SIZE; } } void consume () { if (findDirty (buffer, BUFFER_SIZE) == -1 ) { consumer++; cQueue.push (number); } else if (isFull (buffer, BUFFER_SIZE)) { if (!pQueue.empty ()) { buffer[readptr] = 0 ; readptr = (readptr + 1 ) % BUFFER_SIZE; show (); sleep (2 ); buffer[writeptr] = pQueue.front (); writeptr = (writeptr + 1 ) % BUFFER_SIZE; pQueue.pop (); producer--; } else { buffer[readptr] = 0 ; readptr = (readptr + 1 ) % BUFFER_SIZE; } } else { buffer[readptr] = 0 ; readptr = (readptr + 1 ) % BUFFER_SIZE; } }
实验三 一. 实验内容 1.使用系统调用pipe()建立一条管道,系统调用fork()分别创建两个子进程,它们分别向管道写一句话,如:
Child process1 is sending a message!
Child process2 is sending a message!
2.父进程分别从管道读出来自两个子进程的信息,显示在屏幕上。
二. 程序流程图 见我的GitHub仓库
三. 程序描述 在 linux 系 统 的 环 境 下 使 用 C++ 完 成 实 验 , 使 用 了 系 统 调 用fork(),read(),write(),pipe(),lockf(),wait(),exit(),sleep()等。随机创建了三个子进程,并且按照相关语句进行输出以检查进程执行顺序。过程中通过加锁解锁和wait函数对进程进行控制。(事实上是n个紫荆城
使用系统调用 pipe()建立一条管道,系统调用 fork()分别创建两个子进程,它们分别向管道写一句话,如:Child process1 is sending a message! Child process2 is sending a message!父进程分别从管道读出来自两个子进程的信息,显示在屏幕上
四. 实验结果(部分截图)
五. 部分核心代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/stat.h> #include <fcntl.h> #include <wait.h> #include <error.h> #define F_ULOCK 0 #define F_LOCK 1 int main () { int num, fd[2 ], result = -1 ; pid_t pid, reid, gtid; char outpipe[100 ], father_str[100 ]; result = pipe (fd); if (result == -1 ) { printf ("fail to create pipe \n" ); return -1 ; } printf ("Please input the number of processes: " ); scanf ("%d" , &num); printf ("\n-------------------------分割线----------------------\n" ); while (num > 0 ) { pid = fork(); printf ("pid: %d\n" , pid); if (pid == -1 ) { printf ("creat subprocess failed!\n" ); exit (0 ); } else if (pid == 0 ) { printf ("now I'm writing in subprocess %d\n" , gtid = getpid ()); lockf (fd[1 ], F_LOCK, 0 ); sprintf (outpipe, "child process %d is sending message!\n" , gtid); write (fd[1 ], outpipe, sizeof (outpipe)); sleep (1 ); lockf (fd[1 ], F_ULOCK, 0 ); printf ("send message successfully and exit subprocess %d\n" , gtid); exit (0 ); } else if (pid > 0 ) { reid = wait (NULL ); if (reid == -1 ) { printf ("father process calls subprocess failed!\n" ); } else { read (fd[0 ], father_str, sizeof (father_str)); printf ("\nfather process reads from subprocess %d:\n" , reid); printf ("%s\n" , father_str); } } printf ("\n-------------------------分割线----------------------\n" ); num--; } return 0 ; }
实验四 一. 实验内容 这是一个综合型实验,要求在掌握父子进程并发执行机制和内存页面置换算法的基础上,能综合运用这两方面的知识,自行编制程序。
程序涉及一个父进程和两个子进程。父进程使用rand()函数随机产生若干随机数,经过处理后,存于一数组Acess_Series[]中,作为内存页面访问的序列。两个子进程根据这个访问序列,分别采用FIFO和LRU两种不同的页面置换算法对内存页面进行调度。要求:
1) 每个子进程应能反映出页面置换的过程,并统计页面置换算法的命中或缺页情况。
设缺页的次数为diseffect。总的页面访问次数为total_instruction。
缺页率 = disaffect/total_instruction
命中率 = 1- disaffect/total_instruction
2)将为进程分配的内存页面数mframe作为程序的参数,通过多次运行程序,说明FIFO算法存在的Belady现象。
二. 程序流程图 见我的GitHub仓库
三. 程序描述 对生产的随机页面数据,开展了FIFO与LRU的页面置换算法,并对置换效果和命中率等信息进行展示。并对FIFO中的Belady现象进行了检验。
完成了基础点: 程序用父进程创建两个子进程, 父进程产生随机序列并完成初始化,两个子进程分别实现 FIFO 算法和 LRU 算法,并分别输出每个状态的内存中页帧状态,最终输出缺页率。
完成了扩展点一: 可以使用父进程创建了两个子进程,两个子进程分别使用不同大小的驻留集,都是用 FIFO 的置换算法,最终可以观察到驻留集更大的子进程反而缺页率更高。
程序涉及一个父进程和两个子进程。父进程使用 rand()函数随机产生若干随机数,经过处理后,存于一数组 Acess_Series[]中,作为内存页面访问的序列。两个子进程根据这个访问序列,分别采用 FIFO 和 LRU 两种不同的页面置换算法对内存页面进行调度。同时统计不同的页面调度算法的缺页率。在 linux 系 统 的 环 境 下 使 用 C++ 并 用 g++ 进 行 编 译 完 成 实 验 , 使 用 了 系 统 调 用fork(),wait(),exit(),sleep(),rand()等。可以随意修改程序序列的长度。
四. 实验结果(部分截图) 对于随机生产的页面序号分别进行FIFO、LRU进行页面置换,对FIFO进行再一次执行以检验是否有Belady现象发生。
若选择要看Belady异常
若选择不要看Belady异常
五. 部分核心代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 void FIFO (int memory_blocks_number, int choice = 0 ) { printf ("%d\n" ,choice); printf ("\nFIFO的内存块数: %d 块\n" , memory_blocks_number); int total_instructions = 12 ; srand ((unsigned )time (NULL )); int Acess_Series[total_instructions] = {3 , 2 , 1 , 0 , 3 , 2 , 4 , 3 , 2 , 1 , 0 , 4 }; if (choice == 1 ) { for (int i = 0 ; i < total_instructions; i++) { Acess_Series[i] = rand () % 10 ; } } Page_Frame page_Frame (memory_blocks_number) ; std::deque<int > fifo_queue; int diseffect = 0 ; for (int i = 0 ; i < total_instructions; ++i) { int pageid = Acess_Series[i]; printf ("访问页面序号:%d " , pageid); if (find (page_Frame.pageframe_array.begin (), page_Frame.pageframe_array.end (), pageid) != page_Frame.pageframe_array.end ()) { page_Frame.print_info (); } else { diseffect++; fifo_queue.push_back (pageid); if (page_Frame.occupied_size < page_Frame.pageframe_size) { page_Frame.occupied_size++; page_Frame.pageframe_array.push_back (pageid); page_Frame.print_info (); } else { int be_replaced_pageid = fifo_queue.front (); fifo_queue.pop_front (); auto be_replaced_pageid_iter = find (page_Frame.pageframe_array.begin (), page_Frame.pageframe_array.end (), be_replaced_pageid); *be_replaced_pageid_iter = pageid; page_Frame.print_info (); } } } printf ("缺页数:%d 缺页率:%f 命中率:%f\n" , diseffect, (float )diseffect / total_instructions, (1 - (float )diseffect / total_instructions)); printf ("\n--------------------------------分割线-------------------------------\n" ); } void LRU () { int total_instructions = 20 ; int Acess_Series[total_instructions] = {1 , 8 , 1 , 7 , 8 , 2 , 7 , 2 , 1 , 8 , 3 , 8 , 2 , 1 , 3 , 1 , 7 , 1 , 3 , 7 }; for (int i = 0 ; i < total_instructions; i++) { Acess_Series[i] = rand () % 10 ; } Page_Frame page_Frame (4 ) ; int diseffect = 0 ; for (int i = 0 ; i < total_instructions; i++) { int pageid = Acess_Series[i]; printf ("访问页面序号:%d " , pageid); std::vector<int >::iterator iter; if ((iter = find (page_Frame.pageframe_array.begin (), page_Frame.pageframe_array.end (), pageid)) != page_Frame.pageframe_array.end ()) { page_Frame.add_pageframe_array_time (); int position = std::distance (page_Frame.pageframe_array.begin (), iter); page_Frame.pageframe_array_time.at (position) = 0 ; page_Frame.print_info (); } else { diseffect++; if (page_Frame.occupied_size < page_Frame.pageframe_size) { page_Frame.add_pageframe_array_time (); page_Frame.occupied_size++; page_Frame.pageframe_array.push_back (pageid); page_Frame.pageframe_array_time.push_back (0 ); page_Frame.print_info (); } else { page_Frame.add_pageframe_array_time (); auto max_iter = std::max_element (page_Frame.pageframe_array_time.begin (), page_Frame.pageframe_array_time.end ()); int max_position = std::distance (page_Frame.pageframe_array_time.begin (), max_iter); page_Frame.pageframe_array.at (max_position) = pageid; page_Frame.pageframe_array_time.at (max_position) = 0 ; page_Frame.print_info (); } } } printf ("缺页数:%d 缺页率:%f 命中率:%f\n" , diseffect, (float )diseffect / total_instructions, (1 - (float )diseffect / total_instructions)); printf ("\n--------------------------------分割线-------------------------------\n" ); }
源码和其余参考资料见我的github仓库 https://github.com/BradTorres/neu-os-experiments