底层通讯Word文档格式.docx
- 文档编号:17899424
- 上传时间:2022-12-12
- 格式:DOCX
- 页数:9
- 大小:21.85KB
底层通讯Word文档格式.docx
《底层通讯Word文档格式.docx》由会员分享,可在线阅读,更多相关《底层通讯Word文档格式.docx(9页珍藏版)》请在冰豆网上搜索。
skb->
dev;
Qdisc
*q;
/*首先检查skb_shinfo(skb)->
frag_list是否有值,如果有,但是网络设备不支持skb的碎片列表,
*则通过skb_linearize把这些碎片重组到一个完整的skb中.
*/
if
(skb_shinfo(skb)->
frag_list
&
!
(dev->
features&
NETIF_F_FRAGLIST)
skb_linearize(skb,
GFP_ATOMIC)
0)
kfree_skb(skb);
return
-ENOMEM;
}
nr_frags
(!
NETIF_F_SG)
||
illegal_highdma(dev,
skb))
/*如果数据包未做检验和,并且设备对该协议不支持检验和计算,则在
*此处计算检验和.
(skb->
ip_summed
==
CHECKSUM_HW
(NETIF_F_HW_CSUM|NETIF_F_NO_CSUM))
NETIF_F_IP_CSUM)
||
skb->
protocol
htons(ETH_P_IP))))
((skb
skb_checksum_help(skb))
NULL)
/*Grab
device
queue*/
spin_lock_bh(&
dev->
queue_lock);
/*net_device的成员qdisk是一个发送队列,缓冲等待网络设备进行发送的skb*/
q
qdisc;
//如果队列存在,入队,一般情况下为实设备,有自己的发送队列,如果是虚设备,则一般没有自己的发送队列.
(q->
enqueue)
ret
q->
enqueue(skb,
q);
/*根据出队规则,挑选一个skb发送出去*/
qdisc_run(dev);
spin_unlock_bh(&
/*返回状态信息*/
NET_XMIT_BYPASS
?
NET_XMIT_SUCCESS
:
ret;
/*如执行到此步,则表明网络接口没有自己的发送队列,一般为虚设备的情况,如loopback,ip隧道等*/
flags&
IFF_UP)
cpu
smp_processor_id();
xmit_lock_owner
cpu)
spin_unlock(&
spin_lock(&
xmit_lock);
cpu;
netif_queue_stopped(dev))
//如果ptype_all中有成员,则先发给其中注册的处理函数
(netdev_nit)
dev_queue_xmit_nit(skb,dev);
//dev->
hard_start_xmit对应实际设备驱动程序的发送函数
hard_start_xmit(skb,
dev)
-1;
0;
/*如果device的发送队列被禁止*/
(net_ratelimit())
printk(KERN_CRIT
"
Virtual
%saskstoqueuepacket!
\n"
name);
-ENETDOWN;
}
else
/*Recursionisdetected!
It
ispossible,
unfortunately*/
Dead
loop
on
virtual
%s,fix
it
urgently!
/*如果设备没有被打开*/
由上面的注释可以看到,dev_queue_xmit会遇到两种情况,一种情况是有trafficcontrol层(QoS层),一种是直接调用设备驱动程序的发送函数hard_start_xmit(比如一般的虚拟设备都是如此)前种情况的路线是:
dev_queue_xmit-->
qdisc_run-->
qdisc_restart(dev),后者的路线是:
hard_start_xmit.
qdisc的源代码为:
static
inline
void
qdisc_run(struct
*dev)
while
netif_queue_stopped(dev)
qdisc_restart(dev)<
0)
/*NOTHING*/;
可以看出,qdisc_run只是qdisc_restart的包裹函数,此函数过滤了发送队列被禁止的dev,然后调用qdisc_restart.
/*函数作用:
当device有自己的发送队列时的发送函数.
*此函数涉及到的两种锁:
*dev->
queue_lock:
是device发送队列queue的锁,
xmit_lock:
是driver的发送程序hard_start_xmit的锁.
*
*函数的执行路线:
qdisc_restart---->
sniffer(如果有)---->
dev_hard_xmit.
*返回值:
*0:
发送队列为空时.
*1:
队列非空,但由于某种原因没能发送数据,比如不能获得dev的发送队列的锁dev->
xmit_lock.
*-1:
发送成功时.
qdisc_restart(struct
/*dev->
qdisc为dev的发送队列*/
*q
*skb;
/*根据一定的规则,挑选一个skb发送出去,此处为体现有trafficcontrol时的区别*/
dequeue(q))
NULL)
(spin_trylock(&
xmit_lock))
/*Rememberthatthedriverisgrabbedbyus.*/
/*Andreleasequeue*/
/*如果有注册的sniffer,则发送个各个sniffer,当sniffer注册时,netdev_nit会相应的加1,所以netdev_nit代表了sniffer的数量*/
dev_queue_xmit_nit(skb,
dev);
/*Releasethedriver*/
/*如果没得到device的发送函数的锁,则说明此device已经被别的cpu调用在发送数据*/
/*So,
someonegrabbedthedriver.*/
/*
maybetransientconfigurationerror,
whenhard_start_xmit()recurses.Wedetect
bycheckingxmitowneranddropthe
packetwhendeadloopisdetected.
/*如果device的发送函数锁为本cpu所有,但却还忙,则free掉sk_buff*/
smp_processor_id())
printk(KERN_DEBUG
netdevice%s,
fixit
/*更新冲突状态信息*/
netdev_rx_stat[smp_processor_id()].cpu_collision++;
Device
kickedusout:
(
Thisispossibleinthreecases:
0.driverislocked
1.fastrouteisenabled
2.
cannotdeterminebusystate
beforestartoftransmission(f.e.dialout)
3.
isbuggy(ppp)
/*走到此,应该是没发送成功,则把skb重新放回到队列中,然后调用net0f_schedule(dev)再次试图将其通过dev发送出去*/
ops->
requeue(skb,
netif_schedule(dev);
1;
/*程序走到这时,说明队列为空,q.qlen应该是0*/
q.qlen;
可以看到,调用了qdisk_restart函数的一般都是device有自己的发送队列的情况,此时在出队列函数dequeue处体现了trafficcontrol的作用.
由代码可知,在发送失败时,会将skb重新放入队列中,然后调用netif_schedule(dev)将其重新发送.下面来看看netif_schedule的源代码.
netif_schedule(struct
test_bit(__LINK_STATE_XOFF,
state))
__netif_schedule(dev);
可以看到,netif_schedule是__netif_schedule(dev)的包裹函数.
*函数作用:
把参数dev加入到softnet_data中的output_queue链表首,优先调度,然后触发发送软中断,调用net_tx_action.
__netif_schedule(struct
test_and_set_bit(__LINK_STATE_SCHED,
state))
unsigned
long
flags;
local_irq_save(flags);
next_sched
softnet_data[cpu].output_queue;
softnet_data[cpu].output_queue
cpu_raise_softirq(cpu,
NET_TX_SOFTIRQ);
local_irq_restore(flags);
如注释所示,此函数功能很简单,把参数传进来的dev放到softnet_data[cpu].output_queue的链表首,优先等待调度,然后触发发送软中断,调用相应的发送处理函数,即net_tx_action.读到此,产生了两个问题,就是说,发送成功后,直接返回-1,由于某种原因不能发送skb时才会有这些操作,也就是说,只有在因为某种原因不能发送skb时,需要将数据报重新放回队列,然后把dev放在output_queue的链表首,此时才触发发送软中断,不是每次发送数据时都触发发送软中断?
还有,我记得当数据报发送数据包成功后会把skb加入到completion_queue中,但是怎么没看到相关代码?
这两个问题先放下,看看读完net_tx_action的代码后能不能找到答案.
*net_tx_action为发送软中断的处理函数.
*1释放softnet_data[cpu].completion_queue中发送成功的sk_buff.
*2调度softnet_data[cpu].output_queue中的device发送数据.
net_tx_action(struct
softirq_action
*h)
(softnet_data[cpu].completion_queue)
*clist;
/*因为net_tx_action不是在中断的环境中运行的,所以驱动程序可以在任意时候不顾net_tx_action的执行而向completion_queue中添加数据,
所以此处要禁止中断,因为禁止中断时间越短越好,所以先用本地变量clist指向softnet_data[cpu].completion_queue,然后将softnet_data[cpu].completion_queue
设置为NULL,开中断,由于本地变量是不能被外界访问的,所以之后再慢慢的处理skb*/
local_irq_disable();
clist
softnet_data[cpu].completion_queue;
softnet_data[cpu].completion_queue
NULL;
local_irq_enable();
(clist
*skb
clist;
clist->
next;
BUG_TRAP(atomic_read(&
users)
0);
__kfree_skb(skb);
(softnet_data[cpu].output_queue)
*head;
/*禁止中断的原因同上*/
head
(head
head;
head->
next_sched;
smp_mb__before_clear_bit();
/*调用output_queue中的device进行发送数据*/
clear_bit(__LINK_STATE_SCHED,
state);
/*在调用一个device发送数据时,应先获得此设备的发送队列的锁*/
queue_lock))
/*如果得不到此设备的发送队列的锁,此时可能有另外的cpu在用到此设备发送数据,则重新调度此设备发送数据*/
此函数完成两件事,如上面代码注释可知:
1,释放softnet_data[cpu].completion_queue中发送成功的sk_buff.2,调度softnet_data[cpu].output_queue中的device发送其发送队列中的数据.突然想起在以前读ldd2的时候看见过如下一句话:
网络接口在两种可能的事件下中断处理器:
新数据包的
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 底层 通讯