3.7 Device Drivers
Driver在跟device通信之前,需要先初始化环境,包括:probing I/O ports、probing IRQs。
DMA是在没有CPU的干涉下将大块数据从内存传输数据(或将大块数据传输到内存)。
有两种方式可以出发DMA传输:
1)Read() or wirte()被调用
2)硬件异步的写数据到内存
DMA的操作步骤如下:
1)process调用read()时,驱动程序会分配一个DMA buffer。然后该process休眠。
2)Hardware写数据进DMA buffer后会产生一个interrupt。
3)Interrupt会唤醒process,现在,read()完成了。
1)当data到达时,hardware产生一个interrupt。
2)Handler分配一个DMAbuffer,并通知hardware开始传输。
3)Hardware写数据到buffer完成后,产生另一个interrupt
4)Handler会唤醒process来处理这些数据。
3.7.1 Interrupt Handling in Linux
当一个interrupt发生时,linux会将中断号push到stack中,然后调用do_IRQ(),do_IRQ()将查询interrupt handler,然后通过调用handle_IRQ_event()来调用handler。The handler会尽快释放CPU然后调度bottom-half function。
3.7.2 Network Device Driver in Linux
分3部分讲解network device driver:初始化、发送过程、接收过程。
下面是与初始化有关的net_device structure的几个field。
char name[IFNAMSIZ]:device name,如:eth0。
unsigned int irq:中断号
unsigned short type:该数字指示了设备类型,如:Ethernet。
unsigned char dev_addr[MAX_ADDR_LEN] : MAC address。
unsigned char addr_len:地址长度
int promiscuity:是否运行在混杂模式。
初始化过程
函数alloc_netdev() (在net/core/dev.c中)负责初始化net_device structure。如Ethernet,alloc_etherdev() (在net/ethernet/eth.c) 调用alloc_netdev()来初始化以太网卡。所初始化的field包括:IRQ, I/O memory, I/O port, MAC address等等。之后,netdev_boot_setup_check()被调用,来检查一些启动配置参数,然后,用register_netdevice()完成设备注册
Transmission Process
如上图,要发送frame时,首先调用hard_atart_xmit(),然后调用以太网的ei_start_xmit(),然后ei_start_xmit()调用ne2k_pci_block_output()(以NE2000为例)来把frame放到device接口。当frame发出后,device会产生一个hardware interrupt,内核将调用handler:ei_interrupt(),ei_interrupt()先判断中断类型,当它发现这是一个frame发送完成的interrupt时,ei_tx_intr()被调用,然后ei_tx_intr()调用NS8390_trigger_send()来发送下一个frame,然后调用netif_wake_queue()来处理下一个任务。
Reception process
如上图,当device收到一个frame时,产生一个hardware interrupt,该handler是ei_interrupt(),ei_interrupt()判断这是一个接收中断,就调用ei_receive(),ne2k_pci_black_input()将frame移动到系统内存,并填写sk_buff structure,netif_rx_action()将frame传输到上层协议。
Remark:interrupt handler的处理时间跟frame的size无关;DMA的时间随着frame的size的增加而增加。DMA的接收数据包的时间要明显高于其发送数据包的时间。