分类: LINUX
2013-09-03 10:53:33
原文地址:linux协议栈(1)-- 数据从哪里来的 作者:fireaxe
协议栈是用来处理网口数据的,数据当然是来自于网口。网口驱动程序是与硬件相关的,它会把数据从网口取出来后放在一个队列中。协议栈就通过不断的读取这个队列来获得待处理数据。通过这种方式,可以保证协议栈的硬件无关性,它不用管数据时怎么来的,甚至不用管是不是硬件来的(可以通过程序直接把数据放到队列中去),只要去读队列就行了。
以CS8900a为例。CS8900a为CIRRUS LOGIC公司生产的低功耗、性能优越的16位以太网控制器,应用非常广泛。它的驱动在文件driver/net/cs8900/cs8900.c中。
cs8900_interrupt是中断处理函数,当有数据收发或者其他信号被触发时,会调用该函数。首先从PP_ISQ中读出中断状态,如果是网口设备接到了数据,则中断状态为RxEvent,调用函数cs8900_receive处理数据。cs8900_receive读出数据放到skb中,并送给函数netif_rx处理。netif_rx中调用__skb_queue_tail把接收到的数据放到接收队列input_pkt_queue中。到此网口驱动就完成了数据处理的工作。其实netif_rx就已经是标准函数了,任何一款网口的驱动都会调用这个函数把数据送入协议栈。由于其标准性,它的实现是在文件net/core/dev.c中。
接下来的操作用到了一个叫软中断的机制。下面的代码会绑定一个软中断处理函数:
open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
当中断NET_RX_SOFTIRQ被触发时,处理函数net_rx_action会被调用。那么怎么触发软中断呢,它又没有硬件触发? 既然是软中断,当然是软件触发,通过函数__raise_softirq_irqoff触发,调用下面的代码后,会触发一个软中断:
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
net_rx_action中通过下面这句进行数据的处理:
work = n->poll(n, weight);
poll是一个回调函数,它根据挂的回调的不同而进行不同的处理。系统默认的处理函数是process_backlog,该函数中调用__skb_dequeue获得之前放到队列input_pkt_queue中的数据取出来,传入函数netif_receive_skb处理。netif_receive_skb中会根据收到数据的格式调用相应的处理函数:
ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
如果是以太网报文,则会调用结构体ip_packet_type中定义的处理函数ip_rcv。
从ip_rcv开始,就正式进入协议栈处理流程。
总结:
网口驱动数据---->放入队列---->触发软中断---->从队列中取数据---->传给协议栈处理
关于软中断机制可参考