MIT.6828 HW5 CPU alarm

環(huán)境

ubuntu 18.04 64位,virtualbox 虛擬機(jī)
實(shí)驗(yàn)地址:CPU alarm

正文

本次實(shí)驗(yàn)我認(rèn)為需要認(rèn)真看一下xv6 book trap那一節(jié)析恋,本次實(shí)驗(yàn)要為xv6添加一個(gè)新的特性段直,使它能夠定期的提示一個(gè)進(jìn)程它所有的CPU時(shí)間寺庄。這對一些進(jìn)程來說非常有用越驻,比如說compute-bound進(jìn)程來限制他們CPU使用時(shí)間闯传,或者那些需要計(jì)算但是又需要在固定的時(shí)間間隔執(zhí)行一些事件(比如說每10s輸出內(nèi)容)惕橙。
你需要增加一個(gè)系統(tǒng)調(diào)用alarm(interval,handler)良蛮,如果一個(gè)程序調(diào)用了alarm(n,fn)融蹂,其中n表示進(jìn)程所消耗的n ticks個(gè)cpu 時(shí)間帮寻,然后內(nèi)核會調(diào)用fn乍狐。當(dāng)fn執(zhí)行結(jié)束的時(shí)候,又會返回之前代碼正在執(zhí)行的地方固逗∏瞅剑可能這段話比較難理解,看接下來的代碼就理解了烫罩。
什么是compute-bound 和 IO-hound:點(diǎn)這里

下面是一個(gè)alarmtest進(jìn)程惜傲,它調(diào)用了alarm(10, periodic),也就是說當(dāng)每次cpu執(zhí)行了10 ticks,kernel就回去調(diào)用periodic函數(shù) ,輸出一個(gè)alarm贝攒!盗誊。因?yàn)檫@個(gè)程序主要的時(shí)間都是在執(zhí)行那個(gè)for循環(huán),這個(gè)循環(huán)會輸出一個(gè)點(diǎn)再屏幕上,然后當(dāng)這個(gè)程序使用了10個(gè)tick的時(shí)候,就會去輸出alarm!哈踱,然后程序繼續(xù)返回for循環(huán)荒适,直到循環(huán)結(jié)束。

#include "types.h"
#include "stat.h"
#include "user.h"

void periodic();

int
main(int argc, char *argv[])
{
  int i;
  printf(1, "alarmtest starting\n");
  alarm(10, periodic);
  for(i = 0; i < 25*500000; i++){
    if((i % 250000) == 0)
      write(2, ".", 1);
  }
  exit();
}

void
periodic()
{
  printf(1, "alarm!\n");
}

所以嚣鄙,alarmtest的輸出結(jié)果就是以下形式,不過會因?yàn)镃PU的性能不同吻贿,可能在10ticks當(dāng)中for循環(huán)的指令就可以執(zhí)行很多次,所以實(shí)際的輸出結(jié)果有點(diǎn)不同哑子。

$ alarmtest
alarmtest starting
.....alarm!
....alarm!
.....alarm!
......alarm!
.....alarm!
....alarm!
....alarm!
......alarm!
.....alarm!
...alarm!
...$ 

本次實(shí)驗(yàn)首先要做的就是新增加一個(gè)系統(tǒng)調(diào)用,這個(gè)應(yīng)該很熟悉肌割,在前面增加date這個(gè)系統(tǒng)調(diào)用的時(shí)候卧蜓,已經(jīng)做過了。下面是幾個(gè)官網(wǎng)給出的提示把敞,先稍微翻一下弥奸。

  1. 你需要修改Makefile文件,將alarmtest.c也編譯到xv6的用戶程序去
  2. 在user.h中聲明一下:
 int alarm(int ticks, void (*handler)());

那個(gè)handler表示無返回值且無參數(shù)的函數(shù)指針奋早。

  1. 修改一下syscall.h 和usys.S是的alarmtest來調(diào)用alarm 這個(gè)system call.
  2. 你的sys_alarm()應(yīng)該將alarm interval和指向handler的指針盛霎,存放在proc structure(proc.h)
  3. 下面是sys_alarm()的代碼:
    int
    sys_alarm(void)
    {
      int ticks;
      void (*handler)();

      if(argint(0, &ticks) < 0)
        return -1;
      if(argptr(1, (char**)&handler, 1) < 0)
        return -1;
      myproc()->alarmticks = ticks;
      myproc()->alarmhandler = handler;
      return 0;
    }

當(dāng)我們調(diào)用alarm(n,fn)這個(gè)系統(tǒng)調(diào)用的時(shí)候,兩個(gè)參數(shù)分別在棧當(dāng)中。第一個(gè)參數(shù)是tick,第二個(gè)參數(shù)指向handler的指針耽装。進(jìn)入sys_alarm()首先先對參數(shù)檢查愤炸,然后再設(shè)置alarmticks和alarmhandler。所以我們很自然的需要在proc struture添加這個(gè)兩個(gè)成員掉奄。
別忘了规个,還需要再syscall.c中的數(shù)組中添加sys_alarm。

  1. 你需要追蹤自從上次調(diào)用handler之后已經(jīng)過了多少ticks姓建。所以需要在porc sturct當(dāng)中加入一個(gè)成員來記錄這個(gè)诞仓。
  2. 你只要在有進(jìn)程運(yùn)行的時(shí)候以及時(shí)間中斷來時(shí)用戶空間的時(shí)候使用相應(yīng)alarm(),所以需要下面的條件:
 if(myproc() != 0 && (tf->cs & 3) == 3)

8.在你的IRQ_TIMER的代碼中速兔,當(dāng)進(jìn)程的ticks超過alarmticks墅拭,就去執(zhí)行alarm handler,但是這個(gè)要做怎么做呢涣狗?

  1. 你還需要做的事谍婉,當(dāng)handler返回的時(shí)候,程序會繼續(xù)執(zhí)行它執(zhí)行的地方屑柔,這個(gè)又怎么做呢屡萤?
  2. 你可以看下alarmtest.asm代碼

實(shí)驗(yàn):
下面的代碼我有一些地方是沒有理解的,希望有知道的鐵子告訴一下掸宛。先上代碼再解釋吧,第一段是先增加一個(gè)新的system call,和之前類似應(yīng)該不是怎么難死陆,我將修改了的代碼都放在一起。


// 新創(chuàng)建一個(gè)文件alarmtest.c
#include "types.h"
#include "stat.h"
#include "user.h"

void periodic();

int
main(int argc, char *argv[])
{
  int i;
  printf(1, "alarmtest starting\n");
  //當(dāng)alarmtest這個(gè)進(jìn)程運(yùn)行超過10ticks,就回去調(diào)用perodic函數(shù)
  alarm(10, periodic);
  for(i = 0; i < 25*500000; i++){
    if((i % 250000) == 0)
      write(2, ".", 1);
  }
  exit();
}

void
periodic()
{
  printf(1, "alarm!\n");
}

//syscall.c中引用一下新增加的system call,并且還要放到數(shù)組當(dāng)中
extern int sys_alarm(void); 
static int (*syscalls[])(void) = {
....
    [SYS_alarm] sys_alarm
}

//usys.S
SYSCALL(alarm)

//sysproc.c
int
sys_alarm(void)
{
  int ticks;
  void (*handler)();

  if(argint(0, &ticks) < 0)
    return -1;
  if(argptr(1, (char**)&handler, 1) < 0)
    return -1;
  myproc()->alarmticks = ticks;
  myproc()->alarmhandler = handler;
  return 0;
}

還有一個(gè)就是要在user.h中增加一個(gè)函數(shù)聲明,還要在porc.h中增加新的成員,alarmticks措译,(*alarmhandler)()别凤,ticks。

//user.h
int alarm(int ticks, void (*handler)());

//proc.h
struct proc {
  uint sz;                     // Size of process memory (bytes)
  pde_t* pgdir;                // Page table
  char *kstack;                // Bottom of kernel stack for this process
  enum procstate state;        // Process state
  int pid;                     // Process ID
  struct proc *parent;         // Parent process
  struct trapframe *tf;        // Trap frame for current syscall
  struct context *context;     // swtch() here to run process
  void *chan;                  // If non-zero, sleeping on chan
  int killed;                  // If non-zero, have been killed
  struct file *ofile[NOFILE];  // Open files
  struct inode *cwd;           // Current directory
  char name[16];               // Process name (debugging)
  uint alarmticks;
  void (*alarmhandler)();
  uint ticks;
};

到這里位置創(chuàng)建好了一個(gè)新的系統(tǒng)調(diào)用领虹。用struct proc中的ticks來記錄當(dāng)前進(jìn)程消耗了多少ticks,當(dāng)進(jìn)程的ticks超過了alarmticks就去調(diào)用alarmhandler规哪,也就是我們在alarmtest.c中調(diào)用alarm(10,periodic)所設(shè)置的.接下來就是要完成本次實(shí)驗(yàn):

  case T_IRQ0 + IRQ_TIMER:
    if (myproc() != 0 && (tf->cs & 3) == 3)  //只處理來自用戶進(jìn)程
    {
      myproc()->ticks++; // 發(fā)生中斷的時(shí)候如果是運(yùn)行user process,ticks ++
      if (myproc()->ticks == myproc()->alarmticks) // 超過了alarmticks
      {
        myproc()->ticks = 0; //重新計(jì)數(shù)ticks
        //myproc()->alarmhandler(); // 不知道為什么不可以直接使用函數(shù)指針
        tf->esp -= 4; 
        *(uint *)(tf->esp) = tf->eip;
        tf->eip = (uint)myproc()->alarmhandler;
      }
    }
    //下面的代碼是xv6原來的時(shí)鐘中斷處理函數(shù),我原封不動的復(fù)制過來
    if (cpuid() == 0)
    {
      acquire(&tickslock);
      ticks++;
      wakeup(&ticks);
      release(&tickslock);
    }
    lapiceoi();
    break;

我最開始想直接使用myproc()->alarmhandler()來調(diào)用塌衰,不知道為什沒有用诉稍。上面的代碼的部分是從別人那邊抄的原文在這里。我來解釋一下比較關(guān)鍵的幾句(這些內(nèi)容需要閱讀一下xv6 book Trap那一節(jié))最疆。當(dāng)中發(fā)生后且此時(shí)正在運(yùn)行的程序是alarmtest杯巨,轉(zhuǎn)入到內(nèi)核去執(zhí)行中斷處理函數(shù),tf(trapframe)中因?yàn)榘l(fā)生了特權(quán)級的轉(zhuǎn)換努酸,所以trapframe中壓入了alarmtest的esp和ss服爷。當(dāng)中斷處理函數(shù)執(zhí)行結(jié)束后將trapframe中的內(nèi)容彈出到對應(yīng)的寄存器,恢復(fù)到之前在執(zhí)行的進(jìn)程获诈,這就是一個(gè)最基本的中斷響應(yīng)與恢復(fù)仍源。

        tf->esp -= 4; 
        *(uint *)(tf->esp) = tf->eip;
        tf->eip = (uint)myproc()->alarmhandler;

tf中的esp是原進(jìn)程的esp,第一句代碼的意思就是空出一個(gè)4字節(jié)的內(nèi)容,然后原進(jìn)程的eip放入到用戶棧里面去,然后把trapframe的eip設(shè)置為alarmhandler舔涎。當(dāng)中斷處理函數(shù)結(jié)束笼踩,返回到trapasm.S,然后trapasm.S中下面的代碼:

![Screenshot from 2020-08-04 19-36-08.png](https://upload-images.jianshu.io/upload_images/10683218-aee112fd3de84647.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
.globl trapret
trapret:
  popal
  popl %gs
  popl %fs
  popl %es
  popl %ds
  addl $0x8, %esp  # trapno and errcode
  iret

iret本來的話,應(yīng)該是跳轉(zhuǎn)到原來的進(jìn)程去執(zhí)行终抽,但是我們此時(shí)對tf->eip重新設(shè)置了戳表。所以iret就跳轉(zhuǎn)到了alarmhandler去執(zhí)行,也就是periodic()昼伴。此時(shí)應(yīng)該會發(fā)生從高特權(quán)級到特權(quán)級的轉(zhuǎn)換(中斷處理函數(shù)的特權(quán)級應(yīng)該是0匾旭,然后跳轉(zhuǎn)到用戶程序去了)。所以又發(fā)生了棧的切換圃郊,切換到了用戶棧价涝。來看一下periodic的匯編代碼:

periodic的匯編代碼

此時(shí)在用戶棧,而且我們又用*(uint *)(tf->esp) = tf->eip;將eip寫入到了棧當(dāng)中持舆,所以ret語句就會跳轉(zhuǎn)到之前的用戶程序去執(zhí)行色瘩,就是我們最開始的那個(gè)for循環(huán)。
下面是我沒有理解的問題,畢竟這個(gè)代碼代碼是抄的逸寓,沒有理解所有內(nèi)容:

  1. myproc()->alarmhandler()為什么不能直接調(diào)用函數(shù)居兆?
  2. trapret后,跳轉(zhuǎn)到alarmhandler()去執(zhí)行竹伸。因?yàn)樵趖rapret當(dāng)中泥栖,我們恢復(fù)了所有的寄存器內(nèi)容簇宽,如果在alarmhandler()中修改了一些寄存器的內(nèi)容,會不會導(dǎo)致原進(jìn)程(也就是前面的那個(gè)for循環(huán))無法正常運(yùn)行?

看起來本次實(shí)驗(yàn)比較簡單吧享,但是做起來還是有點(diǎn)不容易的魏割。下面是我的實(shí)驗(yàn)結(jié)果截圖:
可能是因?yàn)镃PU速度相對比較快,一個(gè)tick可以循環(huán)的次數(shù)更多钢颂。不過看結(jié)果應(yīng)該是實(shí)現(xiàn)了題目要求钞它。

實(shí)驗(yàn)結(jié)果

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市殊鞭,隨后出現(xiàn)的幾起案子遭垛,更是在濱河造成了極大的恐慌,老刑警劉巖操灿,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耻卡,死亡現(xiàn)場離奇詭異,居然都是意外死亡牲尺,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進(jìn)店門幌蚊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谤碳,“玉大人,你說我怎么就攤上這事溢豆⊙鸭颍” “怎么了?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵漩仙,是天一觀的道長搓茬。 經(jīng)常有香客問我,道長队他,這世上最難降的妖魔是什么卷仑? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮麸折,結(jié)果婚禮上锡凝,老公的妹妹穿的比我還像新娘。我一直安慰自己垢啼,他們只是感情好窜锯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著芭析,像睡著了一般锚扎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上馁启,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天驾孔,我揣著相機(jī)與錄音,去河邊找鬼。 笑死助币,一個(gè)胖子當(dāng)著我的面吹牛浪听,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播眉菱,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼迹栓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了俭缓?” 一聲冷哼從身側(cè)響起克伊,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎华坦,沒想到半個(gè)月后愿吹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡惜姐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年犁跪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歹袁。...
    茶點(diǎn)故事閱讀 39,754評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡坷衍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出条舔,到底是詐尸還是另有隱情枫耳,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布孟抗,位于F島的核電站迁杨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏凄硼。R本人自食惡果不足惜铅协,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望帆喇。 院中可真熱鬧警医,春花似錦、人聲如沸坯钦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽婉刀。三九已至吟温,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間突颊,已是汗流浹背鲁豪。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工潘悼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人爬橡。 一個(gè)月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓治唤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親糙申。 傳聞我的和親對象是個(gè)殘疾皇子宾添,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容