MIT-6.S081-2020實驗(xv6-riscv64)十一:net


實驗文檔

概述

這次實驗主要實現網卡驅動的一部分,文檔內容非常長,實際實驗不算難,跟着hint就行,但還是需要對整體框架有一定的了解。

內容

發送函數:

int
e1000_transmit(struct mbuf *m)
{
  acquire(&e1000_lock);
  uint32 index = regs[E1000_TDT];
  if ((tx_ring[index].status & E1000_TXD_STAT_DD) == 0) {
      release(&e1000_lock); return -1;
  }
  if (tx_mbufs[index] != 0) mbuffree(tx_mbufs[index]);
  tx_mbufs[index] = m;
  tx_ring[index].addr = (uint64)m->head; tx_ring[index].length = m->len;
  tx_ring[index].cmd = E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP;
  regs[E1000_TDT] = (index + 1) % TX_RING_SIZE;
  release(&e1000_lock); return 0;
}

這里有兩個問題,第一是可不可以只保存一個mbuf,每次調用函數的時候就釋放掉上一個mbuf,答案是不能,因為多進程的影響,可能有多個進程都調用了這個函數,那么就會有很多“上一個mbuf”,自然需要一個數組來存,那么怎么知道上一個mbuf的位置呢,這就是寄存器E1000_TDT的作用,個人猜測在進程切換的時候寄存器的值是包含在進程狀態里面的,所以相當於每個進程都有各自保存上一個mbuf的位置。第二是tx_desc結構體里的cmd應該填啥,本來按照實驗文檔給出的參考材料,這玩意有8個位,只有一個位明確是必須填0,其他位都是可以或者必須填成1的,但是查看e1000_dev.h,發現它里面剛好有且僅有兩個常量E1000_TXD_CMD_RSE1000_TXD_CMD_EOP,暗示我們就填這兩個位即可。

接收函數:

static void
e1000_recv(void)
{
  for (;;) {
      uint32 index = (regs[E1000_RDT] + 1) % RX_RING_SIZE;
      if ((rx_ring[index].status & E1000_RXD_STAT_DD) == 0) break;
      rx_mbufs[index]->len = rx_ring[index].length;
      net_rx(rx_mbufs[index]); rx_mbufs[index] = mbufalloc(0);
      rx_ring[index].addr = (uint64)rx_mbufs[index]->head; rx_ring[index].status = 0;
      regs[E1000_RDT] = index;
  }
}

這里我沒有使用鎖,因為這個函數是不會並行的。這個函數的作用是一次性讀取收到的所有rx_ring,然后由net_rx發給上一層,這里沒有區分收到的包是哪個進程的,因為這個函數實際上處於OSI里非常底層的數據鏈路層,net_rx函數收到包后會調用net_rx_ip或net_rx_arp,進入網絡層,net_rx_ip函數會調用net_rx_udp,進入傳輸層,net_rx_udp函數會調用sockrecvudp,再由socket機制統一根據端口把包分給各個進程。也就是說,在物理層之上,傳輸層及以下的層都是不考慮並行的。所以網上很多題解提到net_rx必須在鎖被釋放之后才能調用,實驗文檔的描述有問題,個人認為這個實驗設計預期recv函數就是不加鎖的,所以可以按照文檔里hint的步驟來。另外就是這個index的意義,上個函數里表示的是“上一個包”,這個函數里表示的是“當前包”,即把當前包對應的mbuf送給上層,然后再新建一個包,令當前的rx_desc指向它,等待后面物理層往里面填東西。

最后是測試,測試程序中用的dns是谷歌的域名服務器8.8.8.8,由於眾所周知的原因,不調整就會卡死。找到dns函數,修改有4個8的那一行為國內的dns服務器,我填的是114.114.114.114:

  dst = (114 << 24) | (114 << 16) | (114 << 8) | (114 << 0);

MIT-6.S081-2020實驗到這里就結束了,感謝MIT的教授們設計了這些實驗並公開給全世界學生學習。這些實驗雖然代碼量都沒多少,但是想要完成實驗就必須反復查看和修改中斷、內存、進程、文件相關的代碼,不需要看長篇累牘的書籍,聽高深復雜的課程,xv6內核實現的重點就已經躍然心中了。不說教學手段,僅說實驗文檔和代碼也是十分出色,實驗文檔循循善誘,每一步要做什么都清清楚楚,而xv6的程序代碼完全不拖泥帶水,每行都有其作用,不繞彎子,代碼注釋也是鞭辟入里,確實是受益匪淺。之后我打算再好好研究一下xv6內核本身以及實驗中各種測試函數的設計,這里面的應該也有很多值得我學習的地方。

不過我還是有點想法,之前雖然說syscall實驗我覺得很不錯,但現在看來似乎有點多余了,因為這個實驗的內容后面大部分的實驗基本每次都要重做一遍,個人感覺這個實驗可以和別的實驗合並;然后lazyalloc、copy on write和mmap三個實驗有不少重疊,多線程調度實驗和鎖的實驗的考察點也基本一致,這些都可以規約和化簡;最后我覺得最好能增加系統引導過程相關的實驗以及物理內存分配相關的實驗,系統引導這個我確實是不知道要怎么設計,物理內存分配的19年有一個,但是今年把它刪掉了,個人覺得要么弄個鏈表+淘汰策略,要么弄個伙伴算法的都可以。當然這些只是我站在外行角度的口胡,那些精通科研和教育的專家肯定會有更好的想法。

完結撒花!全部源代碼放在Github上了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM