What is the focus of Lab Networking in MIT6.S081's Lab7 course?

2026-05-22 11:271阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1617个文字,预计阅读时间需要7分钟。

What is the focus of Lab Networking in MIT6.S081's Lab7 course?

开始日期:22.5.15

操作系统:Ubuntu 20.0.4

链接:Lab Networking

文档位置:Lab Networking/写在前面/实验内容/概述 e_1000_transmit e1000_recv 总结

实验内容概述:- 实验内容涉及网络实验室操作。- 重点探讨 e_1000_transmit 和 e1000_recv 的传输性能。- 总结实验结果,比较不同文档和文字。

开始日期: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.

    What is the focus of Lab Networking in MIT6.S081's Lab7 course?

    因为我们是按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
  • 对于网络协议层的代码其实我并不熟悉,也就只有基础的理论知识而已,后续得安排课程
  • 最近在听《任我行》陈奕迅

本文共计1617个文字,预计阅读时间需要7分钟。

What is the focus of Lab Networking in MIT6.S081's Lab7 course?

开始日期:22.5.15

操作系统:Ubuntu 20.0.4

链接:Lab Networking

文档位置:Lab Networking/写在前面/实验内容/概述 e_1000_transmit e1000_recv 总结

实验内容概述:- 实验内容涉及网络实验室操作。- 重点探讨 e_1000_transmit 和 e1000_recv 的传输性能。- 总结实验结果,比较不同文档和文字。

开始日期: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.

    What is the focus of Lab Networking in MIT6.S081's Lab7 course?

    因为我们是按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
  • 对于网络协议层的代码其实我并不熟悉,也就只有基础的理论知识而已,后续得安排课程
  • 最近在听《任我行》陈奕迅