加入收藏 | 设为首页 | 会员中心 | 我要投稿 云计算网_泰州站长网 (http://www.0523zz.com/)- 视觉智能、AI应用、CDN、行业物联网、智能数字人!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

Linux内核分析 - 网络[十二]:UDP模块 - 收发

发布时间:2016-09-26 03:21:42 所属栏目:Linux 来源:站长网
导读:副标题#e# 内核版本:2.6.34 UDP报文接收 UDP报文的接收可以分为两个部分:协议栈收到udp报文,插入相应队列中;用户 调用recvfrom()或recv()系统调用从队列中取出报文,这里的队列就是sk-sk_receive_queue,它是报文中转的纽带,两部 分的联系如下图所示。
副标题[/!--empirenews.page--]

内核版本:2.6.34

UDP报文接收

UDP报文的接收可以分为两个部分:协议栈收到udp报文,插入相应队列中;用户 调用recvfrom()或recv()系统调用从队列中取出报文,这里的队列就是sk->sk_receive_queue,它是报文中转的纽带,两部 分的联系如下图所示。

Linux内核分析 - 网络[十二]:UDP模块 - 收发

第一部分:协议栈如何收取udp报文的。

udp模块的注册在inet_init()中,当收到的是udp报文,会 调用udp_protocol中的handler函数udp_rcv()。

if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)     
 printk(KERN_CRIT "inet_init: Cannot add UDP protocoln");

udp_rcv() -> __udp4_lib_rcv() 完成udp报文接收,初始化udp的校验和,并不验证校验和的正确性。

if 

(udp4_csum_init(skb, uh, proto))     
 goto csum_error;

在udptable中以套接字的[saddr, sport, daddr, dport]查找相应的sk,在上一篇中已详细讲过” sk的查找”,这里报文的source源端口相当于源主机的端口,dest目的端口相当于本地端口。

sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable);

如果udptable中存在相应的sk,即有 socket在接收,则通过udp_queue_rcv_skb()将报文skb入队列,该函数稍后分析,总之,报文会被放到sk- >sk_receive_queue队列上,然后sock_put()减少sk的引用计算,并返回。之后的接收工作的完成将有赖于用户的操作。

if (sk != NULL) {     
 int ret = udp_queue_rcv_skb(sk, skb);     
 sock_put(sk);     
if (ret > 0)     
  return -ret;     
 return 0;     
}

当没有在udptable中找到sk时,则本机没有socket会接收它,因此要发送icmp不可达报文,在此之前,还要验证校验 和udp_lib_checksum_complete(),如果校验和错误,则直接丢弃报文;如果校验和正确,则会增加mib中的统计,并发送icmp端 口不可达报文,然后丢弃该报文。

if (udp_lib_checksum_complete(skb))     
 goto csum_error;     
UDP_INC_STATS_BH(net, UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE);     
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);     
kfree_skb(skb);

udp_queue_rcv_skb() 报文入队列

sock_woned_by_user()判断sk- >sk_lock.owned的值,如果等于1,表示sk处于占用状态,此时不能向sk接收队列中添加skb,执行else if部分, sk_add_backlog()将skb添加到sk->sk_backlog队列上;如果等于0,表示sk没被占用,执行if部分,__udp_queue_rcv_skb() 将skb添加到sk->sk_receive_queue队列上。

bh_lock_sock(sk);     
if (!sock_owned_by_user(sk))     
 rc = __udp_queue_rcv_skb(sk, skb);     
else if (sk_add_backlog(sk, skb)) {     
 bh_unlock_sock(sk);     
 goto drop;     
}     
bh_unlock_sock(sk);

那么何时sk会被占用?何时sk->sk_backlog上的skb被处理的?

创建socket时, sys_socket() -> inet_create() -> sk_alloc() -> sock_lock_init() -> sock_lock_init_class_and_name()初 始化sk->sk_lock_owned=0。

比如当销毁socket时,udp_destroy_sock()会调用lock_sock()对sk加锁,操作完后,调用 release_sock()对sk解锁。

void udp_destroy_sock(struct sock *sk)     
{     
 lock_sock(sk);     
 udp_flush_pending_frames(sk);
 release_sock(sk);     
}

实际上,lock_sock()设置sk->sk_lock.owned=1;而release_sock()设置sk->sk_lock.owned=0,并处理sk_backlog队 列上的报文,release_sock() -> __release_sock(),对于sk_backlog队列上的每个报文,调用sk_backlog_rcv() -> sk->sk_backlog_rcv()。同样是在socket的创建中,sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv()即 __udp_queue_rcv_skb(),这个函数的作用上面已经讲过,将skb添加到sk_receive_queue,这样,所有的sk_backlog上的报文转 移到了sk_receive_queue上。简单来说,sk_backlog队列的作用就是,锁定时报文临时存放在此,解锁时,报文移到 sk_receive_queue队列。

Linux内核分析 - 网络[十二]:UDP模块 - 收发

(编辑:云计算网_泰州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读