操作系统第二十八章作业(1-7题)

操作系统第二十八章作业(1-7题)

!所有题目均为自己所做,仅供学习参考,禁止抄袭!
!!某些题目中文和原文不太一样,因为有些词翻译得太烂了,所以有所修改,最好还是看英文的题目。

1. 首先用-p flag.s运行x86.py。该代码通过一个内存标志“实现”锁。你能理解汇编代码试图做什么吗?Examine flag.s. This code “implements” locking with a single memory flag. Can you understand the assembly?

  • .acquire中首先获取flag,如果为0说明锁目前没有被持有,将flag设置为1,线程获取锁;如果不为0说明锁目前被别的线程持有,跳转到.acquire头部继续上述操作(一直循环直到flag为0)。
  • 接下来是临界区代码。获取count值,将其值加一后再存回原来的地址(使用寄存器ax做中介)。
  • 然后是释放锁的操作,将flag置零。
  • 最后将寄存器bx值减1后与0相比较,如果大于0,就跳转至.top处(在.acquire之前);否则停机。

2. 使用默认值运行时,flags.s是否按预期工作?它会产生正确的结果吗?使用-M和-R标志跟踪变量和寄存器(并打开-c查看他们的值)。你能预测代码运行时flag最终会变成什么值吗?When you run with the defaults, does flag.s work? Use the -M and -R flags to trace variables and registers (and turn on -c to see their values). Can you predict what value will end in flag?

python ./x86.py -p flag.s

  • flag.s会按预期工作,线程0先运行,执行完所有的操作后停机,之后线程1才开始运行,没有出现多个线程同时访问临界区的情况,所以预测其会产生正确的结果。
  • 对于count:线程0对count执行了一次加1操作,线程1也对count执行了一次加1操作,所以结束时count的值为2。
  • 对于flag:线程1,1007处将flag置零,所以结束时flag的值为0。

使用-M,-R标志追踪地址、寄存器,使用-c标志查看结果:

python ./x86.py -p flag.s -M count,flag -R ax,bx -c

flag.s确实会按预期工作,产生了正确的结果:结束是count为2,flag为0。

3. 使用-a标志更改寄存器%bx的值(例如,如果只运行两个线程,就用-a bx=2,bx=2)。代码是做什么的?对这段代码重复上面的问题,答案是什么?Change the value of the register %bx with the -a flag (e.g., -a bx=2, bx=2) if you are running just two threads). What dose the code do? How does it change your answer for the question above?

python ./x86.py -p flag.s -a bx=2,bx=2

  • 可以看到,在某个线程所有指令执行完毕之前不会切换到另一个线程,没有出现多个线程同时访问临界区的情况,这一点与之前一样,所以flag.s会按预期工作。
  • 而-a标志使得寄存器bx的初值由默认的0变为了2。在第一题的分析中我们已经知道,每次获取锁、访问临界区、释放锁之后,都会将寄存器bx的值减1后与0比较,如果大于0,就跳至.top,进行下一轮获取锁、访问临界区、释放锁的操作。所以,将bx初值改为2后,每个线程访问临界区的次数由之前的一次变为两次,即每个线程都对count执行了两次自加1的操作。
  • 对于count:线程0对count执行了两次加1操作,线程1也对count执行了两次加1操作,所以结束时count的值为4。
  • 对于flag:线程1,1007处将flag置零,所以结束时flag的值为0。

使用-M,-R标志追踪地址、寄存器,使用-c标志查看结果:

python ./x86.py -p flag.s -a bx=2,bx=2 -M count,flag -R ax,bx -c

flag.s确实会按预期工作,产生了正确的结果:结束是count为4,flag为0。

4. 对每个线程将bx设置为高值,然后使用-i标志生成不同的中断频率。什么值导致不好结果的产生?什么值导致良好结果的产生?Set bx to a high value for each thread, and then use the -i flag to generate different interrupt frequencies; what values lead to a bad outcomes? Which lead to good outcomes?

  当-i较小时,会有不好的结果产生;当-i较大时,会产生好的结果。但是对于不同的bx,-i具体的临界值不好确定。

  错误产生的一个例子:count当前为20,线程0访问临界区,执行完add $1, %ax但还没有来得及将新值写入count时,发生了线程切换。线程1将count加1后重新写入count,此时count为21。当又切换回线程0时,由于已经执行过add指令,所以线程0直接向count写入21。于是不期望的事情发生了:add $1, %ax执行了两次,但实际count只增加了一次。这样就会导致结束时count的值不是两个线程bx初值之和。

5. 现在让我们看看程序test-and-set.s。首先,尝试理解使用xchg指令构建简单锁原语的代码。获取锁怎么写?释放锁怎么写?Now let’s look at the program test-and-set.s First, try to understand the code, which uses the xchg instruction to build a simple locking primitive. How is the lock acquire written? How about lock release?

test-and-set.s源码:

  • .acquire:先将1存入寄存器ax,然后将寄存器ax值与mutex值进行交换,如果寄存器ax值为0(也就是mutex刚刚为0,锁为空闲),就进入临界区;否则跳转到.acquire头部重复上述操作。
  • 接下来是临界区代码。获取count值,将其值加一后再存回原来的地址(使用寄存器ax做中介)。
  • 然后是释放锁的操作。将mutex置零。
  • 最后将寄存器bx值减1后与0相比较,如果大于0,就跳转至.top处(在.acquire之前);否则停机。

  test-and-set.s是这样获取/释放锁的:不断循环判断mutex是否为0,如果为0说明锁为空闲(被释放),将mutex置1获取锁,进入临界区。释放锁时将mutex置零即可。

6. 现在运行代码,再次更改中断间隔(-i)的值,并确保循环多次。代码是否总能按预期工作?有时会导致CPU使用率不高吗?如何量化呢?Now run the code, changing the value of the interrupt interval (-i) again, and making sure to loop for a number of times. Does the code always work as expected? Does it sometimes lead to an inefficient use of the CPU? How could you quantify that?

  将两个线程的bx初值都改为30,并不断改变-i的值,多次试验后,发现代码总能按预期的工作,结束时count总为60。我又将两个线程的bx更改很多组为不同的值,同样不断改变-i的值,代码也总是能按照预期的工作,结束时count值为两个线程bx初值之和。

  在test-and-set.s的实现当中,当一个线程持有锁时,另一个线程不断自旋等待,使得CPU使用率不高。

  我所想到的量化方法是:使用寄存器cx记录跳转到.acquire代码段的次数(.acquire中添加指令add $1, %cx),找到每个线程执行halt指令之前最后一次执行add $1, %cx指令后寄存器cx的值,若大于该线程对应的bx的初值,说明线程有自旋等待锁被释放的时间,浪费了一定的CPU;其与bx初值的比值越大,自旋等待时间越长,CPU的使用率越低。

比如:

python ./x86.py -p test-and-set.s -a bx=20:cx=0,bx=10:cx=0 -i 1000 -M count -R ax,bx,cx -c

  设置线程0对count执行20次加1操作,cx初值为0;线程1对count执行10次加1操作,cx初值为0。-i参数为1000,-M、-R追踪地址和寄存器,-c标志查看结果(部分)如下:

线程0:

线程1:

  可以看到线程0中cx最后的值为20,等于bx;线程1中cx的值为10,也等于bx。这说明CPU的使用率较高,两个线程都没有自旋等待(因为-i的参数比较大)。下面再看一个反例:

python ./x86.py -p test-and-set.s -a bx=20:cx=0,bx=10:cx=0 -i 5 -M count -R ax,bx,cx -c

  设置线程0对count执行20次加1操作,cx初值为0;线程1对count执行10次加1操作,cx初值为0。-i参数为5,-M、-R追踪地址和寄存器,-c标志查看结果(部分)如下:

线程0:

线程1:

  可以看到线程0中cx最后的值为30,而bx为20;线程1中cx最后的值为20,而bx为10。两个线程都有自旋等待,CPU的使用率较低(因为-i的参数比较小)。

7. 使用-P标志生成锁相关代码的具体测试。例如,执行一个调度,在第一个线程中获取锁,但随后尝试在第二个线程中获取锁。正确的事情发生了吗?你还应该测试什么?Use the -P flag to generate specific tests of the locking code. For example, run a schedule that grabs the lock in the first thread, but then tries to acquire it in the second. Does the right thing happen? What else should you test?

-P参数:

-P lets you specify exactly which threads run when;
e.g., 11000 would run thread 1 for 2 instructions, then thread 0 for 3, then repeat

test-and-set.s代码:

线程在.acquire通过第二条指令xchg获取锁,所以让线程0执行两个指令后切换到线程1执行一段时间:

python ./x86.py -p test-and-set.s -P 00111111111 -M mutex,count -R ax -c

-c标志查看结果(部分)如图:

  可以看到,当线程0持有锁时,线程1一直在自旋等待,确实是正确的,期望线程1做的事情。除此之外,还应该测试当线程0释放锁后线程1会不会获取锁。在刚刚-P参数的基础上,切换到线程0运行知道其释放锁,再切换到线程1运行一段时间:

python ./x86.py -p test-and-set.s -P 001111111110000001111111 -M mutex,count -R ax -c

-c标志查看结果(部分)如图:

可以看到,线程0释放锁后,线程1成功获取了锁,访问了临界区,也做了正确的事情。

同理,还可以测试线程1持有锁时线程0是否会自旋等待:

python ./x86.py -p test-and-set.s -P 11000000000 -M mutex,count -R ax -c

以及线程1释放锁后线程0是否会成功获取锁进入临界区:

python ./x86.py -p test-and-set.s -P 110000000001111110000000 -M mutex,count -R ax -c

可以看到,线程1持有锁时线程0一直在自旋等待;线程1释放锁后线程0就获取了锁,均正确。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值