中文字幕理论片,69视频免费在线观看,亚洲成人app,国产1级毛片,刘涛最大尺度戏视频,欧美亚洲美女视频,2021韩国美女仙女屋vip视频

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
Linux協(xié)議棧中UDP數(shù)據(jù)報從網(wǎng)卡到用戶空間流程總結(jié)
Linux協(xié)議棧中UDP數(shù)據(jù)報從網(wǎng)卡到用戶空間流程總結(jié)
NAPI驅(qū)動流程:
    中斷發(fā)生
    -->確定中斷原因是數(shù)據(jù)接收完畢(中斷原因也可能是發(fā)送完畢,DMA完畢,甚至是中斷通道上的其他設(shè)備中斷)
    -->通過netif_rx_schedule將驅(qū)動自己的napi結(jié)構(gòu)加入softnet_data的poll_list鏈表,禁用網(wǎng)卡中斷,并發(fā)出軟中斷
   -->中斷返回時觸發(fā)軟中斷net_rx_action,從softnet_data的poll_list上取下剛掛入的napi結(jié)構(gòu),并且調(diào)用其poll函數(shù),這個poll函數(shù)也是驅(qū)動自己提供的,比如rtl8139網(wǎng)卡驅(qū)動中的rtl8139_poll等。
    -->在poll函數(shù)中進(jìn)行輪詢,直到接受完所有的數(shù)據(jù)或者預(yù)算(budget)耗盡。每接收一個報文要分配skb,用eth_type_trans處理并交給netif_receive_skb。
    -->如果數(shù)據(jù)全部接收完(預(yù)算沒有用完),則重新使能中斷并將napi從鏈表中取下。如果數(shù)據(jù)沒接收完,則什么也不作,等待下一次poll函數(shù)被調(diào)度。
非NAPI流程:
    中斷發(fā)生
    -->確定中斷發(fā)生的原因是接收完畢。分配skb,讀入數(shù)據(jù),用eth_type_trans處理并且將skb交給netif_rx
   -->在netif_rx中,將packet加入到softnet_data的input_pkt_queue末尾(NAPI驅(qū)動不使用這個input_pkt_queue),再通過napi_schedule將softnet_data中的backlog(這也是個napi結(jié)構(gòu))加入softnet_data的poll_list,最后發(fā)出軟中斷
    -->軟中斷net_rx_action從poll_list上取下softnet_data的backlog,調(diào)用其poll函數(shù),這個poll函數(shù)是內(nèi)核提供的process_backlog
    -->函數(shù)process_backlog從softnet_data的input_pkt_queue末尾取下skb,并且直接交給netif_receive_skb處理。
    -->如果input_pkt_queue中所有skb都處理完則將backlog從隊(duì)列中除去(注意input_pkt_queue中可能有多個網(wǎng)卡加入的報文,因?yàn)樗敲縞pu公用的)并退出循環(huán);如果預(yù)算用完后也跳出循環(huán)。最后返回接受到的包數(shù)
總結(jié):
NAPI和非NAPI的區(qū)別
    1.NAPI使用中斷+輪詢的方式,中斷產(chǎn)生之后暫時關(guān)閉中斷然后輪詢接收完所有的數(shù)據(jù)包,接著再開中斷。而非NAPI采用純粹中斷的方式,一個中斷接收一個數(shù)據(jù)包
    2.NAPI都有自己的struct napi結(jié)構(gòu),非NAPI沒有
    3.NAPI有自己的poll函數(shù),而且接收數(shù)據(jù)都是在軟中斷調(diào)用poll函數(shù)時做的,而非NAPI使用公共的process_backlog函數(shù)作為其poll函數(shù),接收數(shù)據(jù)是在硬件中斷中做的
   4.NAPI在poll函數(shù)中接收完數(shù)據(jù)之后直接把skb發(fā)給netif_receive_skb,而非NAPI在硬件中斷中接收了數(shù)據(jù)通過netif_rx把skb掛到公共的input_pkt_queue上,最后由軟中斷調(diào)用的process_backlog函數(shù)來將其發(fā)送給netif_receive_skb
驅(qū)動以及軟中斷這塊對skb僅僅做了以下簡單處理:
    1.調(diào)用skb_reserve預(yù)留出2個字節(jié)的空間,這是為了讓ip首部對齊,因?yàn)橐蕴W(wǎng)首部是14字節(jié)
    2.調(diào)用skb_put將tail指向數(shù)據(jù)末尾
    3.調(diào)用eth_type_trans進(jìn)行如下處理:
        (1)將skb->dev指向接收設(shè)備
        (2)將skb->mac_header指向data(此時data就是指向mac起始地址)
        (3)調(diào)用skb_pull(skb, ETH_HLEN)將skb->data后移14字節(jié)指向ip首部
        (4)通過比較目的mac地址判斷包的類型,并將skb->pkt_type賦值PACKET_BROADCAST或PACKET_MULTICAST或者PACKET_OTHERHOST,因?yàn)镻ACKET_HOST為0,所以是默認(rèn)值
        (5)最后判斷協(xié)議類型,并返回(大部分情況下直接返回eth首部的protocol字段的值),這個返回值被存在skb->protocol字段中
總結(jié),結(jié)束后,skb->data指向ip首部,skb->mac_header指向mac首部,skb->protocol儲存L3的協(xié)議代碼,skb->pkt_type已被設(shè)置,skb->len等于接收到的報文長度減去eth首部長度,也就是整個ip報文的總長。其余字段基本上還是默認(rèn)值。

netif_receive_skb
   1.將skb->iif賦值為skb->dev->ifindex,將skb->network_header和skb->transport_header都指向skb->data,也就是ip首部,然后skb->mac_len=skb->network_header-skb->mac_header,正常情況下應(yīng)該等于ETH_HLEN吧
    2.向ptype_all中注冊(通過dev_add_pack)的每一個packet_type調(diào)用一次deliver_skb,這里沒有拷貝skb,只是先增加了一下skb->users
    3.調(diào)用handle_bridge處理橋報文,如果該dev不是一個橋端口則直接返回
    4.調(diào)用handle_macvlan處理vlan
    5.對于每一個在ptype_base中注冊的packet_type(也是用dev_add_pack),調(diào)用deliver_skb
    6.如果沒有任何一個注冊的packet_type接受skb則直接kfree_skb并且返回NET_RX_DROP。否則返回最后一個pkt_type->func返回的值
總結(jié),需要說一下dev_add_pack,這個函數(shù)根據(jù)傳入的packet_type的type字段決定加入哪個隊(duì)列,如果是ETH_P_ALL就加入ptype_all,否則計(jì)算哈希值并加入ptype_base,通過這個函數(shù)注冊的都是L3層的協(xié)議,比如ip,arp,rarp,bootp等,其實(shí)還有packet協(xié)議族套接字的監(jiān)聽函數(shù)(除了ETH_P_ALL之外都加入ptype_base,它們對應(yīng)的接收函數(shù)是packet_rcv),這里對于ip來說,接受函數(shù)就是ip_rcv。
經(jīng)過這個函數(shù),又有幾個字段發(fā)生變化:
network_header和transport_header都指向ip首部,mac_len為mac首部長度

ip_rcv:
    1.丟棄所有pkt_type為OTHER_HOST的包,注意對于將網(wǎng)卡設(shè)為混雜模式的監(jiān)聽進(jìn)程來說,這個包已經(jīng)在netif_receive_skb中給它們發(fā)送了一份拷貝
    2.檢查skb是否被共享,如果被共享需要用skb_clone拷貝一份,因?yàn)楹竺嬉獙kb的內(nèi)容進(jìn)行變更
   3.常規(guī)檢測:如果報文的長度小于ip首部最小長度,丟棄;如果ip協(xié)議字段不等于4丟棄;若ip首部長度字段小于5,丟棄;若ip首部長度小于ip首部長度字段*4,丟棄;如果ip首部校驗(yàn)和出錯,丟棄;如果skb->len(此時len為整個ip報文長度)小于ip首部總長字段,丟棄;如果ip首部總長字段小于ip首部長度字段,丟棄;
   4.注意第三步中skb->len是可以小于ip首部的總長字段的,因?yàn)楦鶕?jù)代碼注釋,傳輸介質(zhì)有可能在末尾添加了padding,在這種情況下,會調(diào)用pskb_trim_rcsum將多余的結(jié)尾部分砍掉(通過把skb->tail往前移),并且還要將檢查和無效化
    5.此處調(diào)用NF_INET_PRE_ROUTING鉤子函數(shù)
總結(jié),ip_rcv主要進(jìn)行的常規(guī)檢查,唯一對skb進(jìn)行操作的就是將結(jié)尾的填充字段砍掉。

ip_rcv_finish:
    1.首先,如果skb->dst為空,說明還不確定這個ip報文的目的地是本機(jī)還是別的機(jī)器,這時通過ip_route_input來找到rtable并且賦給skb->rtable
   2.如果ip首部長度字段大于5則調(diào)用ip_rcv_options處理ip選項(xiàng)。該函數(shù)調(diào)用ip_options_compile將選項(xiàng)全部處理放在skb的cb字段中,作為一個structip_options(還要詳細(xì)看ip_options_compile)。如果有源站路由選項(xiàng)則檢查設(shè)備是否支持源站路由(軟件支持,可配置),則調(diào)用ip_options_rcv_srr(此函數(shù)也還需認(rèn)真看)填寫源站路由。
   3.添加統(tǒng)計(jì)信息并調(diào)用dst_input,dst_input只是調(diào)用skb->dst->input函數(shù),這個skb->dst就是前面用ip_route_input確定的,而根據(jù)dst類型的不同,這個input函數(shù)可能是ip_local_deliver或者ip_forward,這里我們看ip_local_deliver。
總結(jié),ip_rcv_finish改變了skb->dst字段(如果本來skb->dst字段已經(jīng)有值則不改變)和skb->cb字段(在ip_rcv_options中將ip首部選項(xiàng)編譯之后放入cb)。ip_options_compile可以改變報文內(nèi)容,比如填寫路由記錄選項(xiàng),填寫時間戳選項(xiàng)等

ip_local_deliver
   1.如果ip首部offset或者M(jìn)F不為0,則調(diào)用ip_defrag進(jìn)行ip分片的重組,ip_defrag只在成功完全重組了一個報文之后才會返回0,其他情況都是返回非0,如果返回非0就會從ip_local_deliver返回。ip_defrag也比較復(fù)雜,需要細(xì)看,總體來說就是將分片放在一個哈希表中,開啟定時器,來一個分片就與前面屬于同一ip報文的分片合并(兩個分片是否屬于同一個ip報文是通過ip的id字段,源目的地址,L4協(xié)議等多個參數(shù)確定的,可參考ip4_frag_match)
    2.鉤子NF_INET_LOCAL_IN,并調(diào)用ip_local_deliver_finish
總結(jié),從上兩個函數(shù)可以看出NF_INET_PRE_ROUTING和NF_INET_LOCAL_IN之間的區(qū)別,前者還沒有經(jīng)過路由處理,即skb->dst一般還沒有確定,而后者是已經(jīng)確定了skb->dst且dst為本地地址,假如skb->dst不是本地地址則會調(diào)用ip_forward,這就不會觸發(fā)NF_INET_LOCAL_IN了。另外NF_INET_PRE_ROUTING尚未對ip分片進(jìn)行合并處理,而NF_INET_LOCAL_IN抓到的數(shù)據(jù)包是已經(jīng)合并成的ip報文了

ip_local_deliver_finish
    1.將skb->data繼續(xù)移動指向傳輸層首部,并且將skb->transport_header也指向傳輸層首部,接下來開始處理
    2.首先從ip首部取得傳輸層協(xié)議號,然后用這個協(xié)議號調(diào)用raw_local_deliver將skb傳給raw_v4_hashinfo哈希表中的原始套接字協(xié)議
   3.再利用protocol值作為下標(biāo)取得inet_protos全局?jǐn)?shù)組中的注冊協(xié)議(對于tcp,udp,icmp分別是tcp_protocol,udp_protocol,icmp_protocol)。如果找到了對應(yīng)的協(xié)議處理結(jié)構(gòu),就把skb交給該結(jié)構(gòu)的handler函數(shù)處理(對于tcp,udp,icmp分別是tcp_v4_rcv,udp_rcv,icmp_rcv)。如果沒找到對應(yīng)的處理結(jié)構(gòu),則回發(fā)一個icmp協(xié)議不可達(dá)的目的不可達(dá)報文,并釋放skb。
總結(jié):這里又一次移動了skb->data指針,將其指向傳輸層首部,同時設(shè)置了transport_header也指向傳輸層首部。raw_v4_hashinfo和inet_protos都是一個256項(xiàng)的全局?jǐn)?shù)組,以協(xié)議號為下標(biāo)保存了各個協(xié)議的處理結(jié)構(gòu)。這兩個數(shù)組就像是L4層的ptype_base,根據(jù)本層的協(xié)議號來決定處理函數(shù)
注意區(qū)別raw_v4_hashinfo和上面的ptype_all,前者是AF_INET的SOCK_RAW套接字注冊的接收結(jié)構(gòu),而后者是AF_PACKET套接字注冊的接收結(jié)構(gòu),可見raw套接字是經(jīng)過了ip層處理的而packet是在netif_receive_skb中接收的,尚未經(jīng)過任何處理,其中一個顯著區(qū)別就是raw經(jīng)過了ip_defrag而packet沒有


對于udp來說,inet_protos中的結(jié)構(gòu)是全局變量udp_protocol,它的handler函數(shù)是udp_rcv
udp_rcv所做的就是直接調(diào)用__udp4_lib_rcv(skb, udp_hash, IPPROTO_UDP);


__udp4_lib_rcv
    此函數(shù)中會調(diào)用__udp4_lib_lookup_skb-->()__udp4_lib_lookup()來查找此udp包對應(yīng)的socket,主要是查找源目的地址和端口號都符合的socket。
    如果查找到了對應(yīng)的socket,則調(diào)用udp_queue_rcv_skb將skb放入udp的接收隊(duì)列,然后返回0
    如果沒有查找到對應(yīng)的socket,要向源地址發(fā)送一個ICMP端口不可達(dá)消息


udp_queue_rcv_skb
    它經(jīng)過__udp_queue_rcv_skb(sk,skb)-->__udp_queue_rcv_skb-->skb_queue_tail一系列調(diào)用過程將skb加入socket的接收隊(duì)列sk->sk_receive_queuek末尾。其中還要檢測接收緩沖區(qū)是否已經(jīng)滿。
    接著調(diào)用sk->sk_data_ready(sk, skb_len)通知socket有數(shù)據(jù)就緒,可以讀了。一般情況下這個函數(shù)對應(yīng)sock_def_readable,這個函數(shù)的功能就是喚醒在sk->sk_sleep上睡眠的進(jìn)程



    那么是誰在這里睡眠呢?在調(diào)用recvfrom系統(tǒng)調(diào)用接收報文的時候,會經(jīng)過這樣一個流程
    sys_socketcall
    -->sys_recvfrom
    -->sock_recvmsg
    -->__sock_recvmsg
    -->sock->ops->recvmsg,這個sock->ops對應(yīng)全局變量inet_dgram_ops,里面的recvmsg對應(yīng)sock_common_recvmsg
    -->sock_common_recvmsg
    -->sk->sk_prot->recvmsg,這個sk->sk_prot對應(yīng)全局變量udp_prot,里面的recvmsg對應(yīng)udp_recvmsg
    -->udp_recvmsg
    -->__skb_recv_datagram
   在__skb_recv_datagram中,會首先嘗試從sk->sk_receive_queue上取下數(shù)據(jù)包,如果發(fā)現(xiàn)隊(duì)列中沒有數(shù)據(jù)包,則開始在sk->sk_skeep上睡眠。而上面sock_def_readable喚醒的就是這里睡眠的進(jìn)程。
   可以看到,在__skb_recv_datagram中被喚醒后,函數(shù)又嘗試從sk->sk_receive_queue上取下數(shù)據(jù)包,這時當(dāng)然會成功,成功之后返回到udp_recvmsg。udp_recvmsg再進(jìn)行一些簡單的檢測之后就調(diào)用copy_to_user將數(shù)據(jù)拷貝到用戶空間了(其實(shí)這里并不是簡單調(diào)用copy_to_user,還要處理很多情況,比如用戶使用的msghdr可能包含多個iovec,skb可能有多個frags等等)

這樣,一個udp數(shù)據(jù)包就從網(wǎng)卡到達(dá)了用戶的緩沖區(qū)
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
圖解 Linux 網(wǎng)絡(luò)包接收過程
TCP/IP協(xié)議棧在Linux內(nèi)核中的運(yùn)行時序分析
Linux協(xié)議棧報文收發(fā)流程記錄
Linux原始套接字實(shí)現(xiàn)分析
理解 Linux 網(wǎng)絡(luò)棧(1):Linux 網(wǎng)絡(luò)協(xié)議棧簡單總結(jié)
Linux網(wǎng)絡(luò)
更多類似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服