lab11

networks

  • arp:将互联网协议(IP)地址解析为对应的媒体访问控制(MAC)地址。
  • ip:定义了数据包的格式以及如何在源和目的地之间传输这些数据包。
  • udp、tcp:UDP和TCP都是传输层协议,它们在IP层之上,负责应用间的数据传输。
  • 网卡与处理线程之间有接收队列、和发送队列,以控制网络流量。
  • DMA(直接内存访问)环允许硬件(如网络接口卡NIC)直接访问系统内存,而无需CPU的干预。
  • 考虑一个路由器接收和转发包
    • 随着输入数据包的增多,转发的数据包也增多
    • 受限于cpu等因素达到最大值
    • 因中断活锁不断产生中断,导致cpu时间全部用于处理中断
    • 解决方案:通过设置一个中断线程轮询,当有数据包到达时产生中断,唤醒线程,处理数据包;当数据包处理完成时,再重新打开中断,线程休眠。

Your Job(hard)

实验要求是我们实现网卡驱动中与发送与接收队列的部分。因为可能会有多线程同时向网卡发送数据,这里我们需要加锁来保护发送DMA环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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);
int i = regs[E1000_TDT];

if((tx_ring[i].status & E1000_TXD_STAT_DD) == 0)
{
release(&e1000_lock);
return -1;
}

if(tx_mbufs[i] != 0)
{
mbuffree(tx_mbufs[i]);
}
tx_ring[i].addr = (uint64) m->head;
tx_ring[i].length = m->len;
tx_mbufs[i] = m;

tx_ring[i].cmd = E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS;


regs[E1000_TDT] = (i + 1) % TX_RING_SIZE;
// printf("%d\n", regs[E1000_TDT]);
//printf("transmit\n");
release(&e1000_lock);
return 0;
}

在recv中,我们不需要加锁。

  • 首先,对接收DMA环来说,只有网卡在写入数据,可以认为是单线程写入
  • 当接收DMA环内有数据时,触发中断,调用该函数。且我们在函数内的实现是尽可能读更多的数据。当数据量大时,可能出现包丢失和重复。别忘了,上层还有传输层协议TCP、UDP来应对这些问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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()).
//
//acquire(&e1000_lock);

int i = (regs[E1000_RDT] + 1) % RX_RING_SIZE;
while(1)
{
if((rx_ring[i].status & E1000_RXD_STAT_DD) == 0)
{
break;
}
rx_mbufs[i]->len = rx_ring[i].length;
net_rx(rx_mbufs[i]);

rx_mbufs[i] = mbufalloc(0);
if (!rx_mbufs[i])
panic("e1000_recv");

rx_ring[i].addr = (uint64) rx_mbufs[i]->head;
rx_ring[i].status = 0;
i = (i + 1) % RX_RING_SIZE;
}


regs[E1000_RDT] = (i - 1 + RX_RING_SIZE) % RX_RING_SIZE;

//release(&e1000_lock);
// printf("recv\n");
}