当前位置 : 主页 > 编程语言 > 其它开发 >

MIT6.S081-Lab7 Lab Networking

来源:互联网 收集:自由互联 发布时间:2022-05-30
开始日期: 22.5.15 操作系统: Ubuntu20.0.4 Link:Lab Networking 目录 Lab Networking 写在前面 实验内容 概述 e_1000_transmit e1000_recv 总结 Lab Networking写在前面 本次实验看起来比较唬人,文档、字都

开始日期:22.5.15

操作系统:Ubuntu20.0.4

Link:Lab Networking

目录
  • Lab Networking
    • 写在前面
    • 实验内容
      • 概述
      • e_1000_transmit
      • e1000_recv
    • 总结

Lab Networking 写在前面

本次实验看起来比较唬人,文档、字都很多,但实际的coding并不难,本次的hints就是伪代码级别的,完全可以按着来写。难点应该在于理解整个收发包(recevice/transmit packet)的过程:
为了完成收发包的过程,cpu、网卡(ethernet)、RAM(buffer的存放处)这三者是如何通过xv6操作系统进行交互呢?
为了完成理解,笔者参考了不少博客。

  • 参考链接:
    [mit6.s081] 笔记 Lab11: Networking | 网络
    MIT6.S081 2021 networking
    MIT 6.S081 2020 LAB11记录
    MIT-6.S081-2020实验(xv6-riscv64)十一:net
    MIT-6.S081 Networking
实验内容 概述

xv6得依靠network stack(网络栈)实现收发数据,即通过network stack收发packet。

发包:

当network stackk需要发送一个packet的时候,会先将这个packet存放到发送环形缓冲区tx_ring,最后通过网卡将这个packet发送出去。
(每次发送packet前都需要检查一下上一次的packet发送完没,如果发送完了,要将其的释放掉)

收包:

当网卡需要接收packet的时候,网卡会直接访问内存(DMA),先将接受到的RAM的数据(即packet的内容)写入到接收环形缓冲区rx_ring中。接着,网卡会向cpu发出一个硬件中断,当cpu接受到硬件中断后,cpu就可以从接收环形缓冲区rx_ring中读取packet传递到network stack中了(net_rx())。
(网卡会一次性接收全部的packets,即接收到rx_ring溢出为止)

e_1000_transmit

这里有三个问题需要解决:

为什么要用锁?何时释放tx_mbufs[tx_index]cmd的flag该如何设置?

  • 因为会有多线程测试,可能会出现多个线程会访问同一个mbuf的情况,导致竞争出错

  • 获取锁之后,如果该索引能被访问,就要将原有的(上一次的)mbuf其释放掉,以便装入新的mbuf

    You will need to ensure that each mbuf is eventually freed, but only after the E1000 has finished transmitting the packet

  • 参考e1000_dev.hcmd实际是有8个标志位,但只提供了两个标志位,我们可以大胆猜测只用设置这两个

    /* Transmit Descriptor command definitions [E1000 3.3.3.1] */
    #define E1000_TXD_CMD_EOP    0x01 /* End of Packet */
    #define E1000_TXD_CMD_RS     0x08 /* Report Status */
    

    实际上我们可以对应查找文档:

    P39 The transmit descriptor status field is only present in cases where RS (or RPS for the 82544GC/EIonly) is set in the command field.

    因为我们要使用tx_ring[tx_index].status来检查该tx_mbufs[tx_index]是否可以被使用,使用要用到标志位E1000_TXD_CMD_RS

    P39 When set, indicates the last descriptor making up the packet. One or many descriptors can be used to form a packet.

    因为我们是按packet的形式传递数据的,所以要设置E1000_TXD_CMD_ECP,来标志一个packet结束了。

参考代码:

int
e1000_transmit(struct mbuf *m)
{
  //
  // Your code here.
  //
  // the mbuf contains an ethernet frame; program it into
  // the TX descriptor ring so that the e1000 sends it. Stash
  // a pointer so that it can be freed after sending.
  //
  acquire(&e1000_lock);
	
  // get the current index
  uint32 tx_index = regs[E1000_TDT];

  // check status
  if ((tx_ring[tx_index].status & E1000_TXD_STAT_DD) == 0){
    release(&e1000_lock);
    return -1;
  }
  
  // free the last mbuf  
  if (tx_mbufs[tx_index]){
    mbuffree(tx_mbufs[tx_index]);
  }
  
  // sent mbuf
  tx_mbufs[tx_index] = m;  
  tx_ring[tx_index].addr = (uint64)m->head;
  tx_ring[tx_index].length = m->len;
  tx_ring[tx_index].cmd = E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS; 
  
  // set the next index of mbuf
  regs[E1000_TDT] = (tx_index + 1) % TX_RING_SIZE;

  release(&e1000_lock);

  return 0;
}
e1000_recv

这里也有三个问题需要解决:

为什么获取的索引是下一个等待被接收的索引,而不是当前索引?
为什么不用锁?为什么要接受全部packet直到溢出?

  • 因为当前索引在前一次recv过程中已经被传递(line 21~22),重新分配并清空了(line 25~27
    • 重新分配并清空,是为下一次RAM的数据写入做准备
  • 后两个问题其实可以看作一个问题同时解答,因为recv过程发生在OSI协议的数据链路层,而在这个层次的packet接收是不区分进程。既然不区分进程接收,自然就可以一直接收,直到不能再接收为止(溢出)。

参考代码:

static void
e1000_recv(void)
{
  //
  // Your code here.
  //
  // Check for packets that have arrived from the e1000
  // Create and deliver an mbuf for each packet (using net_rx()).
  //

  while (1) {
  	// get next index
    uint32 rx_index = (regs[E1000_RDT] + 1) % RX_RING_SIZE;
    
		// check status, if not set we will return 
    if ((rx_ring[rx_index].status & E1000_RXD_STAT_DD) == 0)
      return ;
    
    // deliver to network stack
    rx_mbufs[rx_index]->len = rx_ring[rx_index].length;
    net_rx(rx_mbufs[rx_index]);
		
    // alloc a new mbuf and fill a new descriptor  
    rx_mbufs[rx_index] = mbufalloc(0);
    rx_ring[rx_index].addr = (uint64)rx_mbufs[rx_index]->head;
    rx_ring[rx_index].status = 0;
		
    // as current index
    regs[E1000_RDT] = rx_index; 
  }
}
总结
  • 完成日期:22.5.16
  • 一开始我就猜测要使用循环,但没看到hints里有具体提出就没有实现,嗯,以后要相信自己的直觉
  • e1000_recv的实现过程中有一句:重新分配一个新的mbuf,我理解出错,去重新创建了一个新的mbuf,根本上是因为我没理解到rx_ring是被循环使用的,这个循环数组一直是用来写入,传递packet
  • 对于网络协议层的代码其实我并不熟悉,也就只有基础的理论知识而已,后续得安排课程
  • 最近在听《任我行》陈奕迅
上一篇:Go sort包
下一篇:没有了
网友评论