建立企业网站多少钱,网络架构师证书,电商网站建设关键词优化,深圳营销型网站方案0.写在前面(重点)
由于一些突发事件#xff0c;导致目前大家手里或多或少都有了完整版的答案了。甚至很多学长学姐们写的代码远比我写的要好很多。
但是这个系列我觉得还是稍微坚持下去一点#xff0c;或许某些地方可以帮到未来的同学们。
还是那句话#xff0c;有需要可…0.写在前面(重点)
由于一些突发事件导致目前大家手里或多或少都有了完整版的答案了。甚至很多学长学姐们写的代码远比我写的要好很多。
但是这个系列我觉得还是稍微坚持下去一点或许某些地方可以帮到未来的同学们。
还是那句话有需要可以随时向我反馈你遇到的问题你的指点就是我最大的动力
1.实验代码解析
注意这个实验比较特殊不是想以前一样直接从nachos源码文件中复制文件到本目录下然后做拓展。而是重新创建一个新的文件实现一些功能。
这个文件叫什么local文件中新增的条目已经向我们指明了threadsbar.cc()函数。
而且这个函数需要重写什么东西也很容易了解了就在这个文件中
至于实验大纲中提到的关于那个n屏障我酱在下一个小目录中实现。
2.手把手攻略
这里要说一下n屏障机制
N线程屏障N-thread barrier是一种同步机制用于确保多个线程在达到某个点之前都会被阻塞直到所有线程都到达该点后才能继续执行。它提供了一种线程同步的方式以便在多线程环境中协调并发操作。
N线程屏障中的N表示参与屏障同步的线程数量。当N个线程都到达屏障位置时屏障将打开所有线程将被释放并可以继续执行后续的操作。如果有任何一个线程到达屏障位置之前发生阻塞未到达屏障位置则其他线程也会被阻塞直到所有线程都准备好后才会继续执行。
而对于这个代码段我估计写过go语言的同学都不陌生
rendezvousmutex . wait ()
count count 1
mutex . signal ()if count n: barrier . signal ()barrier . wait ()
barrier . signal ()critical point
这是一个比较典型的创建n屏蔽的过程
首先mutex在go语言中我们经常称之为互斥锁互斥锁内的资源每次之允许一个线程进行访问其他的线程将会存入队列中等待被执行。在nachos中使用Semaphore实现了mutex这个东西。我们可以用图上的方式完成这些操作
而barrier其实也是通过Semaphore来实现的在这里我们先不关心是如何实现的具体的实现和定义我们会放在下面的代码中解释。barrier理想中的状态是在代码的某处拦住所有的线程当某个线程发送了signal这个指令的时候其他被阻塞的线程就”跨过“自己当前所在的wait语句。
因此这一整段代码实现的就是在第n个线程到达之前前面n-1个线程都处在被阻塞的状态第n个线程到达if语句以后经过判断释放signal信号让其他线程可以脱离。
而自身则可能会被wait洽住但是此时已经”逃脱“的线程重新发送了信号。这样让第n个县城也可以顺利脱困
至于这个东西需要不需要时间机制。。。我想说的事barrier只是一个逻辑上的概念尤其是在nachos上的模拟实现所以底层是存在信号量这一资源的所谓的”信号“也是通过资源数目来确定能否通行比如barrier本质上是一个初始资源数目为0的semaphore对象而mutex为初始资源数目为1的semaphore对象
3.实验过程分析
首先说明具体的实验流程和名称在这里先简写了所以题目可能对不上不过内容是大致一致的
3.1:分析说明nachos信号量如何实现
信号量主要依靠如下semaphore个类的实现 该类中使用P V两个函数来完成信号量增加和减少的原语操作
另外这个类中还使用了value来模拟”可支配资源“的数目以及信号等待队列List的实现这个队列中用来阻塞线程。
3.2:说明并发进程如何创建以及运行
在nachos中并发进程的创建是通过for循环进行多重创建生成数个可以并行的thread对象如图所示 在nachos中默认每个时刻只有一个线程在运行因此如果需要调度的话需要Threads类中的yieldsleep以及finish方法进行辅助。并且还需要scheduler中的这几个方法作为调度逻辑
并发的执行运行则可以在scheduler.cc中的方法看到
ReadyToRun负责进行从就绪转化为运行态
FindNextToRun方法负责将运行态转化为结束态并且将下一个线程移入
Run方法负责直接执行线程
3.3,修改代码以及实现n屏障机制
在文件夹lab3下面创建一个名为threadsbar.cc的cpp文件如图所展示 在lab3文件夹下存在一个readme的文件里面展示了一个代码框架我们根据这个代码框架实现了如下的代码在threadsbar.cc中
//事先说明需要改动的其实就是这些东西
//我们根据信号量Semaphore这个类来实现互斥锁mutex以及n线程屏障barrier//首先要导入一些新的包
#include unistd.h
#include stdio.h#include copyright.h
#include system.h
#include synch.h#define N_THREADS 20 // the number of threads
#define MAX_NAME 16 //设置最大长度“名称”
#define N_TICKS 1000 // the number of ticks to advance simulated time//创建线程队列
Thread *threads[N_THREADS];
char thread_names[N_THREADS][MAX_NAME]; //给每个线程队列都加上一个名字//提前设置好指针
Semaphore *barrier;
Semaphore *mutex;int count0;void MakeTicks(int n) // advance n ticks of simulated time n个模拟时间推进
{ //说实话至少这个函数哥们是没看懂一点的。。。。int i; //首先是我不会操作系统其次我不写cppIntStatus oldLevel; //根据代码框架的提示在最开始就写好了这个开关来增加时间的方法oldLevelinterrupt-SetLevel(IntOff);for(i0;in;i){interrupt-SetLevel(IntOff);interrupt-SetLevel(IntOn);}interrupt-SetLevel(oldLevel);
}void BarThread(_int which) //下面这个其实就是基本实现了
{MakeTicks(N_TICKS);printf(Thread %d rendezvous\n, which);//这个东西被称作rendezvous循环什么的乱七八糟的//总之就是首先由互斥锁保证计数器是合理运行的mutex-P();count; mutex-V();/*if(countN_THREADS){printf(Thread %d is the last\n,which);barrier-V(); //}barrier-P(); //前面n-1个线程会被阻塞在这里barrier-V(); //会去解救原本的第n个线程*///好像出现的问题之一是有的线程会觉得自己是最后一个。。。。没发现这种问题//好的加大力度现在是线程数目为20的时候随机种子为114514,会发生两个线程都认为自己是最后一个的情况//原因在于临界资源的访问无论是读写都应该加上一个互斥锁//在最开始的代码中对于count的判断并没有使用互斥导致了两个线程的同时访问//导致的错误最后一个线程需要处理内存相关的东西但是当有多个判断自己为最后一个线程的时候可能会导致内存等其他临界区域发生问题//解决方法对临界资源的读写仍然是加上互斥锁如下图代码所示,即可解决这个问题//最后是关于随机种子的情况//rz并非无效根据实验代码提供的提示应该是为每个进程分配的时间片不足导致的//使用空循环耗时并没有解决实际问题首先较少的空循环并不能起到拖延很多时间的作用其次空循环是在用户状态下执行的任务对整体的时钟没有什么改变效果//使用linux下的sleep则根本无法解决问题因为sleep的作用是linux上的进程而不是nachos上的“线程”nachos在linux的视角下本身就是一个进程而已//makeTick的开关中断处理了这个问题由于给的代码框架比较完善所以在第一次就完善了这个方法//导致后面就没有出现这些问题//所以正确的解决方法就是在makeTick中增加根据题目给出的框架我们默认使用1000来进行实验//的开关次数使用nachos内部的“进程”的开关中断进行模拟mutex-P();if(countN_THREADS){printf(Thread %d is the last\n,which);barrier-V(); }mutex-V();barrier-P(); //前面n-1个线程会被阻塞在这里barrier-V(); //会去解救原本的第n个线程printf(Thread %d critical point\n, which);
}void ThreadsBarrier() //这个函数用来设置县城之间的同步/并发控制
{int i;DEBUG(t,ThreadsBarrier); //DEBUG函数使用来干啥的来着//创建互斥锁以及屏障barriernew Semaphore(barrier,0);//障碍的信号量设置为0,带波奥这是个屏障mutexnew Semaphore(mutex,1);//互斥锁的信号量设置为1代表统一时刻只能有一个线程访问这个东西// Create and fork N_THREADS threads //在这里创造多个线程,也就是县城初始化的部分for(int i 0; i N_THREADS; i) {//...............sprintf(thread_names[i],thread_%d,i);//...............threads[i]new Thread(thread_names[i]);threads[i]-Fork(BarThread, i);};
}
其中对于n屏蔽机制如图所示代码加上了一些注释因为这段代码是需要后期改进的这里是完成了第三步要求的展示。 使用make指令进行编译并且根据-rz指令设置随机种子
在这里我们直接使用随机种子114514并且为了让结果更加明显我们创建了而是个并发的县城结果如下
使用指令
./nachos -rz 114514
结果如下 出现了明显的问题进程0和进程1都认为自己是最后一个进程
这个现象的原因是因为对于最后的count的读取判断并没有是互斥枷锁倒是了0和1都能读取到countn的情况导致二者均认为自己可以处理这个问题。
处理方法为对于if的count判断也要加上互斥锁 3.4:使用随机种子进行测试可能会发生-rz失效的情况如何处理以及怎么处理
rz确实会发生”无效“的情况根据实验代码提供的提示应该是为每个进程分配的时间片不足导致的
1。使用空循环耗时并没有解决实际问题首先较少的空循环并不能起到拖延很多时间的作用其次空循环是在用户状态下执行的任务对整体的时钟没有什么改变效果
2。使用linux下的sleep则根本无法解决问题因为sleep的作用是linux上的进程而不是nachos上的“线程”nachos在linux的视角下本身就是一个进程而已 makeTick的开关中断处理了这个问题由于给的代码框架比较完善所以在第一次就完善了这个方法导致在最开始的测试中就没有出现这些问题所以正确的解决方法就是在makeTick中增加根据题目给出的框架我们默认使用1000来进行实验
如图所示我们进一步填充了maketick函数的内容
void MakeTicks(int n) // advance n ticks of simulated time n个模拟时间推进
{ //说实话至少这个函数哥们是没看懂一点的。。。。int i; //首先是我不会操作系统其次我不写cppIntStatus oldLevel; //根据代码框架的提示在最开始就写好了这个开关来增加时间的方法oldLevelinterrupt-SetLevel(IntOff);for(i0;in;i){interrupt-SetLevel(IntOff);interrupt-SetLevel(IntOn);}interrupt-SetLevel(oldLevel);
}