Linux核心分析

來源:互聯網
上載者:User

核心版本:2.6.34

UDP報文接收

UDP報文的接收可以分為兩個部分:協議棧收到udp報文,插入相應隊列中;使用者 調用recvfrom()或recv()系統調用從隊列中取出報文,這裡的隊列就是sk->sk_receive_queue,它是報文中轉的紐帶,兩部 分的聯絡如下圖所示。

第一部分:協議棧如何收取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 protocol\n");

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隊列。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.