网站做竞价对seo有影响吗,中美俄最新军事新闻,建筑品牌网站,肇庆网站制作软件深入理解Linux网络#xff08;二#xff09;#xff1a;UDP接收内核探究 一、UDP 协议处理二、recvfrom 系统调⽤实现 一、UDP 协议处理
udp 协议的处理函数是 udp_rcv。
//file: net/ipv4/udp.c
int udp_rcv(struct sk_buff *skb)
{return __udp4_lib_rcv(skb, udp_… 深入理解Linux网络二UDP接收内核探究 一、UDP 协议处理二、recvfrom 系统调⽤实现 一、UDP 协议处理
udp 协议的处理函数是 udp_rcv。
//file: net/ipv4/udp.c
int udp_rcv(struct sk_buff *skb)
{return __udp4_lib_rcv(skb, udp_table, IPPROTO_UDP);
}int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, int proto)
{sk __udp4_lib_lookup_skb(skb, uh-source, uh-dest, udptable);if (sk ! NULL) {int ret udp_queue_rcv_skb(sk, skb);}icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
}__udp4_lib_lookup_skb 是根据 skb 来寻找对应的socket当找到以后将数据包放到 socket 的缓存队列⾥。如果没有找到则发送⼀个⽬标不可达的 icmp 包。
//file: net/ipv4/udp.c
int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
{ ......if (sk_rcvqueues_full(sk, skb, sk-sk_rcvbuf))goto drop;rc 0;ipv4_pktinfo_prepare(skb);bh_lock_sock(sk);if (!sock_owned_by_user(sk))rc __udp_queue_rcv_skb(sk, skb);else if (sk_add_backlog(sk, skb, sk-sk_rcvbuf)) {bh_unlock_sock(sk);goto drop;}bh_unlock_sock(sk);return rc;
}sock_owned_by_user 判断的是⽤户是不是正在这个 socket 上进⾏系统调⽤ socket 被占⽤。 如果没有那就可以直接放到 socket 的接收队列中。 如果有那就通过 sk_add_backlog 把数据包添加到 backlog 队列。 当⽤户释放的 socket 的时候内核会检查 backlog 队列如果有数据再移动到接收队列中。 sk_rcvqueues_full 接收队列如果满了的话将直接把包丢弃。接收队列⼤⼩受内核参数 net.core.rmem_max 和 net.core.rmem_default 影响。
二、recvfrom 系统调⽤实现
代码⾥调⽤的 recvfrom 是⼀个 glibc 的库函数该函数在执⾏后会将⽤户进⾏陷⼊到内核态进⼊到 Linux 实现的系统调⽤ sys_recvfrom 。 socket 数据结构中的 const struct proto_ops 对应的是协议的⽅法集合。每个协议都会实现不同的⽅法集对于IPv4 Internet 协议族来说,每种协议都有对应的处理⽅法如下 对于 udp 来说是通过 inet_dgram_ops 来定义的其中注册了 inet_recvmsg ⽅法。
//file: net/ipv4/af_inet.c
const struct proto_ops inet_stream_ops {.......recvmsg inet_recvmsg,.mmap sock_no_mmap,......
}
const struct proto_ops inet_dgram_ops {.......sendmsg inet_sendmsg,.recvmsg inet_recvmsg,......
}socket 数据结构中的另⼀个数据结构 struct sock *sk 是⼀个⾮常⼤⾮常重要的⼦结构体。其中的 sk_prot ⼜定义了⼆级处理函数。对于udp协议来说会被设置成 udp 协议实现的⽅法集 udp_prot 。
//file: net/ipv4/udp.c
struct proto udp_prot {.name UDP,.owner THIS_MODULE,.close udp_lib_close,.connect ip4_datagram_connect,.......sendmsg udp_sendmsg,.recvmsg udp_recvmsg,.sendpage udp_sendpage,......
}看完了 socket 变量之后我们再来看 sys_recvfrom 的实现过程。 在 inet_recvmsg 调⽤了 sk-sk_prot-recvmsg 。
//file: net/ipv4/af_inet.c
int inet_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg,size_t size, int flags)
{ ......err sk-sk_prot-recvmsg(iocb, sk, msg, size, flags
MSG_DONTWAIT,flags ~MSG_DONTWAIT, addr_len);if (err 0)msg-msg_namelen addr_len;return err;
}//file: net/core/datagram.c:EXPORT_SYMBOL(__skb_recv_datagram);
struct sk_buff *__skb_recv_datagram(struct sock *sk, unsignedint flags, int *peeked, int *off, int *err)
{......do {struct sk_buff_head *queue sk-sk_receive_queue;skb_queue_walk(queue, skb) {......}/* User doesnt want to wait */error -EAGAIN;if (!timeo)goto no_packet;} while (!wait_for_more_packets(sk, err, timeo, last));
}上⾯所谓的读取过程就是访问 sk-sk_receive_queue 。 如果没有数据且⽤户也允许等待则将调⽤ wait_for_more_packets() 执⾏等待操作它加⼊会让⽤户进程进⼊睡眠状态。 具体是怎么进⼊睡眠状态的和 TCP 的实现一样属于进程的基本知识了。
再次推荐飞哥的 《深入理解Linux网络》。