[mit6.s081] 笔记 Lab11: Networking | 网络

2022-10-27 16:09:04 浏览数 (1)

这是我自学 MIT6.S081 操作系统课程的 lab 代码笔记第十一篇:Networking(最后一篇)。此 lab 大致耗时:2小时。 课程地址:https://pdos.csail.mit.edu/6.S081/2020/schedule.html Lab 地址:https://pdos.csail.mit.edu/6.S081/2020/labs/net.html 我的代码地址:https://github.com/Miigon/my-xv6-labs-2020/tree/net Commits: https://github.com/Miigon/my-xv6-labs-2020/commits/net 本文中代码注释是编写博客的时候加入的,原仓库中的代码可能缺乏注释或代码不完全相同。

Lab 11: Networking (hard)

熟悉系统驱动与外围设备的交互、内存映射寄存器与 DMA 数据传输,实现与 E1000 网卡交互的核心方法:transmit 与 recv。

本 lab 的难度主要在于阅读文档以及理解 CPU 与操作系统是如何与外围设备交互的。换言之,更重要的是理解概念以及 lab 已经写好的模版代码的作用。

代码实现

代码语言:javascript复制
int
e1000_transmit(struct mbuf *m)
{
  acquire(&e1000_lock); // 获取 E1000 的锁,防止多进程同时发送数据出现 race

  uint32 ind = regs[E1000_TDT]; // 下一个可用的 buffer 的下标
  struct tx_desc *desc = &tx_ring[ind]; // 获取 buffer 的描述符,其中存储了关于该 buffer 的各种信息
  // 如果该 buffer 中的数据还未传输完,则代表我们已经将环形 buffer 列表全部用完,缓冲区不足,返回错误
  if(!(desc->status & E1000_TXD_STAT_DD)) {
    release(&e1000_lock);
    return -1;
  }
  
  // 如果该下标仍有之前发送完毕但未释放的 mbuf,则释放
  if(tx_mbufs[ind]) {
    mbuffree(tx_mbufs[ind]);
    tx_mbufs[ind] = 0;
  }

  // 将要发送的 mbuf 的内存地址与长度填写到发送描述符中
  desc->addr = (uint64)m->head;
  desc->length = m->len;
  // 设置参数,EOP 表示该 buffer 含有一个完整的 packet
  // RS 告诉网卡在发送完成后,设置 status 中的 E1000_TXD_STAT_DD 位,表示发送完成。
  desc->cmd = E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS;
  // 保留新 mbuf 的指针,方便后续再次用到同一下标时释放。
  tx_mbufs[ind] = m;

  // 环形缓冲区内下标增加一。
  regs[E1000_TDT] = (regs[E1000_TDT]   1) % TX_RING_SIZE;
  
  release(&e1000_lock);
  return 0;
}

static void
e1000_recv(void)
{
  while(1) { // 每次 recv 可能接收多个包

    uint32 ind = (regs[E1000_RDT]   1) % RX_RING_SIZE;
    
    struct rx_desc *desc = &rx_ring[ind];
    // 如果需要接收的包都已经接收完毕,则退出
    if(!(desc->status & E1000_RXD_STAT_DD)) {
      return;
    }

    rx_mbufs[ind]->len = desc->length;
    
    net_rx(rx_mbufs[ind]); // 传递给上层网络栈。上层负责释放 mbuf

    // 分配并设置新的 mbuf,供给下一次轮到该下标时使用
    rx_mbufs[ind] = mbufalloc(0); 
    desc->addr = (uint64)rx_mbufs[ind]->head;
    desc->status = 0;

    regs[E1000_RDT] = ind;
  }

}

操作系统想要发送数据的时候,将数据放入环形缓冲区数组 tx_ring 内,然后递增 E1000_TDT,网卡会自动将数据发出。当网卡收到数据的时候,网卡首先使用 direct memory access,将数据放入 rx_ring 环形缓冲区数组中,然后向 CPU 发起一个硬件中断,CPU 在收到中断后,直接读取 rx_ring 中的数据即可。

完结撒花

本 lab 的完成,为接近两个月的 MIT6.S081 旅途画上了句号。不得不说 MIT 的课程以及作业设计真的很不错,目标明确但又充满探索的 xv6 lab,加上自动 grading,体验简直好到爆。推荐同样对操作系统有兴趣的同学们可以来刷一刷,亲自动手体验实现操作系统各项机能。

同时,对计算机科学的探索依然没有穷尽,后面还会同步更新更多的刷课记录以及课程推荐。

课程地址:https://pdos.csail.mit.edu/6.S081/2020/schedule.html

0 人点赞