1. 程式人生 > >XV6學習(16)Lab net: Network stack

XV6學習(16)Lab net: Network stack

最後一個實驗了,程式碼在[Github](https://github.com/weijunji/xv6-6.S081/tree/net)上。 這一個實驗其實挺簡單的,就是要實現網絡卡的`e1000_transmit`和`e1000_recv`函式。不過看以前的實驗好像還要實現上層socket相關的程式碼,今年就只有網絡卡驅動了。 雖然實驗文件裡面給了一本400多頁的網絡卡文件,但其實也不需要怎麼讀這本厚厚的文件,實驗的hints裡面就講的挺清楚了。 ## 實驗 首先是`e1000_transmit`函式,按照hints一步步來就行了,唯一一個要查文件的就是`cmd`域,但其實這個域的巨集定義裡面就只給了`E1000_TXD_CMD_R`和`E1000_TXD_CMD_EOP`這兩個,也就是說我們只要關注這兩個就行了: ```c int e1000_transmit(struct mbuf *m) { acquire(&e1000_lock); uint32 idx = regs[E1000_TDT]; struct tx_desc* desc = &tx_ring[idx]; if((desc->status & E1000_TXD_STAT_DD) == 0){ release(&e1000_lock); printf("buffer overflow\n"); return -1; } if(tx_mbufs[idx]) mbuffree(tx_mbufs[idx]); desc->addr = (uint64)m->head; desc->length = m->len; desc->cmd = E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP; tx_mbufs[idx] = m; regs[E1000_TDT] = (idx + 1) % TX_RING_SIZE; __sync_synchronize(); release(&e1000_lock); return 0; } ``` 然後是`e1000_recv`函式,這裡注意一次中斷應該把所有到達的資料都處理掉,剩下的按hints裡面的來就行了: ```c static void e1000_recv(void) { int idx = (regs[E1000_RDT] + 1) % RX_RING_SIZE; struct rx_desc* desc = &rx_ring[idx]; while(desc->status & E1000_RXD_STAT_DD){ acquire(&e1000_lock); struct mbuf *buf = rx_mbufs[idx]; mbufput(buf, desc->length); rx_mbufs[idx] = mbufalloc(0); if (!rx_mbufs[idx]) panic("mbuf alloc failed"); desc->addr = (uint64) rx_mbufs[idx]->head; desc->status = 0; regs[E1000_RDT] = idx; __sync_synchronize(); release(&e1000_lock); net_rx(buf); idx = (regs[E1000_RDT] + 1) % RX_RING_SIZE; desc = &rx_ring[idx]; } } ``` ## 總結 到這裡整個XV6的實驗就完成了,趕在了過年之前寫完了。不得不說國外的實驗設計的真的好,實驗的程式碼量都不大,每個實驗就是幾個函式,實驗的難度也設定的很合適。十一個實驗做完,配套講義看完之後就把XV6核心的絕大部分內容都看完了,對於作業系統的核心部分也都通過實驗有了更加深入的瞭解。知道了執行緒和程序切換之間的區別以及上下文切換是如何進行的;從以前只直到頁表這個概念到現在知道了整個分頁機構是如何執行的,親手實現了基於分頁和缺頁異常的COW fork,mmap,lazy allocation等技術;知道了系統呼叫是如何實現的以及作業系統是如何與硬體配合來對系統呼叫和中斷陷阱進行處理。 總而言之,強烈推薦學完了作業系統系統通過這個課程的實驗來加深和鞏固理解,而不是隻停留在課本的那些概念上,一點實際的核心程式碼都沒有接觸過。XV6核心的實現非常精簡,但是所有核心功能都實現了,而且編譯速度快,也不需要很多的配置,相比於Linux核心更加適合初學者來學習,在講義中也介紹了很多相比於Linux的可以改進的