国产无码免费,人妻口爆,国产V在线,99中文精品7,国产成人无码AA精品一,制度丝袜诱惑av,久久99免费麻辣视频,蜜臀久久99精品久久久久久酒店
        訂閱
        糾錯
        加入自媒體

        Linux內(nèi)核源代碼:tcp/ip協(xié)議棧的調(diào)用

        我們結(jié)合源碼進(jìn)行仔細(xì)分析:

        接收端調(diào)用的是__sys_recvfrom函數(shù):

        __sys_recvfrom函數(shù)具體如下:

        發(fā)現(xiàn)它調(diào)用了sock_recvmsg函數(shù):

        發(fā)現(xiàn)它調(diào)用了sock_recvmsg_nosec函數(shù):

        發(fā)現(xiàn)它調(diào)用了inet_recvmsg函數(shù):

        最后調(diào)用的是tcp_recvmsg這個系統(tǒng)調(diào)用。至此接收端調(diào)用分析完畢。

        下面用gdb打斷點(diǎn)進(jìn)行驗(yàn)證:

        驗(yàn)證結(jié)果剛好符合我們的調(diào)研。

        4 傳輸層流程

        4.1 發(fā)送端

        傳輸層的最終目的是向它的用戶提供高效的、可靠的和成本有效的數(shù)據(jù)傳輸服務(wù),主要功能包括 (1)構(gòu)造 TCP segment (2)計(jì)算 checksum (3)發(fā)送回復(fù)(ACK)包 (4)滑動窗口(sliding windown)等保證可靠性的操作。TCP 協(xié)議棧的大致處理過程如下圖所示:

        TCP 棧簡要過程:

        tcp_sendmsg 函數(shù)會首先檢查已經(jīng)建立的 TCP connection 的狀態(tài),然后獲取該連接的 MSS,開始 segement 發(fā)送流程。

        構(gòu)造 TCP 段的 playload:它在內(nèi)核空間中創(chuàng)建該 packet 的 sk_buffer 數(shù)據(jù)結(jié)構(gòu)的實(shí)例 skb,從 userspace buffer 中拷貝 packet 的數(shù)據(jù)到 skb 的 buffer。

        構(gòu)造 TCP header。

        計(jì)算 TCP 校驗(yàn)和(checksum)和 順序號 (sequence number)。

        TCP 校驗(yàn)和是一個端到端的校驗(yàn)和,由發(fā)送端計(jì)算,然后由接收端驗(yàn)證。其目的是為了發(fā)現(xiàn)TCP首部和數(shù)據(jù)在發(fā)送端到接收端之間發(fā)生的任何改動。如果接收方檢測到校驗(yàn)和有差錯,則TCP段會被直接丟棄。TCP校驗(yàn)和覆蓋 TCP 首部和 TCP 數(shù)據(jù)。

        TCP的校驗(yàn)和是必需的

        發(fā)到 IP 層處理:調(diào)用 IP handler 句柄 ip_queue_xmit,將 skb 傳入 IP 處理流程。

        UDP 棧簡要過程:

        UDP 將 message 封裝成 UDP 數(shù)據(jù)報(bào)

        調(diào)用 ip_append_data() 方法將 packet 送到 IP 層進(jìn)行處理。

        下面我們結(jié)合代碼依次分析:

        根據(jù)我們對應(yīng)用層的追查可以發(fā)現(xiàn),傳輸層也是先調(diào)用send()->sendto()->sys_sento->sock_sendmsg->sock_sendmsg_nosec,我們看下sock_sendmsg_nosec這個函數(shù):

        在應(yīng)用層調(diào)用的是inet_sendmsg函數(shù),在傳輸層根據(jù)后面的斷點(diǎn)可以知道,調(diào)用的是sock->ops-sendmsg這個函數(shù)。而sendmsg為一個宏,調(diào)用的是tcp_sendmsg,如下;

        struct proto tcp_prot = {
           .name            = "TCP",
           .owner            = THIS_M(jìn)ODULE,
           .close            = tcp_close,
           .pre_connect        = tcp_v4_pre_connect,
           .connect        = tcp_v4_connect,
           .disconnect        = tcp_disconnect,
           .a(chǎn)ccept            = inet_csk_accept,
           .ioctl            = tcp_ioctl,
           .init            = tcp_v4_init_sock,
           .destroy        = tcp_v4_destroy_sock,
           .shutdown        = tcp_shutdown,
           .setsockopt        = tcp_setsockopt,
           .getsockopt        = tcp_getsockopt,
           .keepalive        = tcp_set_keepalive,
           .recvmsg        = tcp_recvmsg,
           .sendmsg        = tcp_sendmsg,
           ......

        而tcp_sendmsg實(shí)際上調(diào)用的是

        int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)

        這個函數(shù)如下:

        int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)

        struct tcp_sock *tp = tcp_sk(sk);進(jìn)行了強(qiáng)制類型轉(zhuǎn)換
        struct sk_buff *skb;
           flags = msg->msg_flags;
           ......
        if (copied)
                   tcp_push(sk, flags & ~MSG_M(jìn)ORE, mss_now,
                        TCP_NAGLE_PUSH, size_goal);

        在tcp_sendmsg_locked中,完成的是將所有的數(shù)據(jù)組織成發(fā)送隊(duì)列,這個發(fā)送隊(duì)列是struct sock結(jié)構(gòu)中的一個域sk_write_queue,這個隊(duì)列的每一個元素是一個skb,里面存放的就是待發(fā)送的數(shù)據(jù)。然后調(diào)用了tcp_push()函數(shù)。結(jié)構(gòu)體struct sock如下:

        struct sock{
           ...
        struct sk_buff_h(yuǎn)ead    sk_write_queue;指向skb隊(duì)列的第一個元素
           ...
        struct sk_buff    *sk_send_h(yuǎn)ead;指向隊(duì)列第一個還沒有發(fā)送的元素

        在tcp協(xié)議的頭部有幾個標(biāo)志字段:URG、ACK、RSH、RST、SYN、FIN,tcp_push中會判斷這個skb的元素是否需要push,如果需要就將tcp頭部字段的push置一,置一的過程如下:

        static void tcp_push(struct sock *sk, int flags, int mss_now,
        int nonagle, int size_goal)

        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *skb;
           skb = tcp_write_queue_tail(sk);
        if (!skb)
        return;
        if (!(flags & MSG_M(jìn)ORE) || forced_push(tp))
               tcp_mark_push(tp, skb);
           tcp_mark_urg(tp, flags);
        if (tcp_should_autocork(sk, skb, size_goal)) {
        avoid atomic op if TSQ_THROTTLED bit is already set
        if (!test_bit(TSQ_THROTTLED, &sk->sk_tsq_flags)) {
                   NET_INC_STATS(sock_net(sk), LINUX_M(jìn)IB_TCPAUTOCORKING);
                   set_bit(TSQ_THROTTLED, &sk->sk_tsq_flags);
               }
        It is possible TX completion already happened
                * before we set TSQ_THROTTLED.
               
        if (refcount_read(&sk->sk_wmem_alloc) > skb->truesize)
        return;
           }
        if (flags & MSG_M(jìn)ORE)
               nonagle = TCP_NAGLE_CORK;
           __tcp_push_pending_frames(sk, mss_now, nonagle);

        首先struct tcp_skb_cb結(jié)構(gòu)體存放的就是tcp的頭部,頭部的控制位為tcp_flags,通過tcp_mark_push會將skb中的cb,也就是48個字節(jié)的數(shù)組,類型轉(zhuǎn)換為struct tcp_skb_cb,這樣位于skb的cb就成了tcp的頭部。tcp_mark_push如下:

        static inline void tcp_mark_push(struct tcp_sock *tp, struct sk_buff *skb)

           TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_PSH;
           tp->pushed_seq = tp->write_seq;

        ...
        #define TCP_SKB_CB(__skb)    ((struct tcp_skb_cb *)&((__skb)->cb[0]))
        ...
        struct sk_buff {
           ...    
        char            cb[48] __aligned(8);
           ...struct tcp_skb_cb {
           __u32        seq;         Starting sequence number    
           __u32        end_seq;     SEQ + FIN + SYN + datalen    
           __u8        tcp_flags;     tcp頭部標(biāo)志,位于第13個字節(jié)tcp[13])    
           ......
        };

        然后,tcp_push調(diào)用了__tcp_push_pending_frames(sk, mss_now, nonagle);函數(shù)發(fā)送數(shù)據(jù):

        void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
        int nonagle)

        if (tcp_write_xmit(sk, cur_mss, nonagle, 0,
                      sk_gfp_mask(sk, GFP_ATOMIC)))
               tcp_check_probe_timer(sk);

        發(fā)現(xiàn)它調(diào)用了tcp_write_xmit函數(shù)來發(fā)送數(shù)據(jù):

        static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
        int push_one, gfp_t gfp)

        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *skb;
        unsigned int tso_segs, sent_pkts;
        int cwnd_quota;
        int result;
        bool is_cwnd_limited = false, is_rwnd_limited = false;
           u32 max_segs;
        統(tǒng)計(jì)已發(fā)送的報(bào)文總數(shù)
           sent_pkts = 0;
           ......
        若發(fā)送隊(duì)列未滿,則準(zhǔn)備發(fā)送報(bào)文
        while ((skb = tcp_send_h(yuǎn)ead(sk))) {
        unsigned int limit;
        if (unlikely(tp->repair) && tp->repair_queue == TCP_SEND_QUEUE) {
        "skb_mstamp_ns" is used as a start point for the retransmit timer
                   skb->skb_mstamp_ns = tp->tcp_wstamp_ns = tp->tcp_clock_cache;
                   list_move_tail(&skb->tcp_tsorted_anchor, &tp->tsorted_sent_queue);
                   tcp_init_tso_segs(skb, mss_now);
        goto repair;  Skip network transmission
               }
        if (tcp_pacing_check(sk))
        break;
               tso_segs = tcp_init_tso_segs(skb, mss_now);
               BUG_ON(!tso_segs);
        檢查發(fā)送窗口的大小
               cwnd_quota = tcp_cwnd_test(tp, skb);
        if (!cwnd_quota) {
        if (push_one == 2)
        Force out a loss probe pkt.
                       cwnd_quota = 1;
        else
        break;
               }
        if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now))) {
                   is_rwnd_limited = true;
        break;
               ......
               limit = mss_now;
        if (tso_segs > 1 && !tcp_urg_mode(tp))
                   limit = tcp_mss_split_point(sk, skb, mss_now,
        min_t(unsigned int,
                                     cwnd_quota,
                                     max_segs),
                                   nonagle);
        if (skb->len > limit &&
                   unlikely(tso_fragment(sk, TCP_FRAG_IN_WRITE_QUEUE,
                             skb, limit, mss_now, gfp)))
        break;
        if (tcp_small_queue_check(sk, skb, 0))
        break;
        if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))
        break;
           ......

        tcp_write_xmit位于tcpoutput.c中,它實(shí)現(xiàn)了tcp的擁塞控制,然后調(diào)用了tcp_transmit_skb(sk, skb, 1, gfp)傳輸數(shù)據(jù),實(shí)際上調(diào)用的是__tcp_transmit_skb:

        static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
                         int clone_it, gfp_t gfp_mask, u32 rcv_nxt)

           skb_push(skb, tcp_h(yuǎn)eader_size);
           skb_reset_transport_h(yuǎn)eader(skb);
           ......
        構(gòu)建TCP頭部和校驗(yàn)和
           th = (struct tcphdr *)skb->data;
           th->source        = inet->inet_sport;
           th->dest        = inet->inet_dport;
           th->seq            = htonl(tcb->seq);
           th->ack_seq        = htonl(rcv_nxt);
           tcp_options_write((__be32 *)(th + 1), tp, &opts);
           skb_shinfo(skb)->gso_type = sk->sk_gso_type;
        if (likely(!(tcb->tcp_flags & TCPHDR_SYN))) {
               th->window      = htons(tcp_select_window(sk));
               tcp_ecn_send(sk, skb, th, tcp_h(yuǎn)eader_size);
           } else {
        RFC1323: The window in SYN & SYN/ACK segments
                * is never scaled.
               
               th->window    = htons(min(tp->rcv_wnd, 65535U));
           }
           ......
           icsk->icsk_af_ops->send_check(sk, skb);
        if (likely(tcb->tcp_flags & TCPHDR_ACK))
               tcp_event_ack_sent(sk, tcp_skb_pcount(skb), rcv_nxt);
        if (skb->len != tcp_h(yuǎn)eader_size) {
               tcp_event_data_sent(tp, sk);
               tp->data_segs_out += tcp_skb_pcount(skb);
               tp->bytes_sent += skb->len - tcp_h(yuǎn)eader_size;
           }
        if (after(tcb->end_seq, tp->snd_nxt) || tcb->seq == tcb->end_seq)
               TCP_ADD_STATS(sock_net(sk), TCP_M(jìn)IB_OUTSEGS,
                         tcp_skb_pcount(skb));
           tp->segs_out += tcp_skb_pcount(skb);
        OK, its time to fill skb_shinfo(skb)->gso_{segs|size}
           skb_shinfo(skb)->gso_segs = tcp_skb_pcount(skb);
           skb_shinfo(skb)->gso_size = tcp_skb_mss(skb);
        Leave earliest departure time in skb->tstamp (skb->skb_mstamp_ns)
        Cleanup our debris for IP stacks
           memset(skb->cb, 0, max(sizeof(struct inet_skb_parm),
                          sizeof(struct inet6_skb_parm)));
           err = icsk->icsk_af_ops->queue_xmit(sk, skb, &inet->cork.fl);
           ......

        tcp_transmit_skb是tcp發(fā)送數(shù)據(jù)位于傳輸層的最后一步,這里首先對TCP數(shù)據(jù)段的頭部進(jìn)行了處理,然后調(diào)用了網(wǎng)絡(luò)層提供的發(fā)送接口icsk->icsk_af_ops->queue_xmit(sk, skb, &inet->cork.fl);實(shí)現(xiàn)了數(shù)據(jù)的發(fā)送,自此,數(shù)據(jù)離開了傳輸層,傳輸層的任務(wù)也就結(jié)束了。

        gdb調(diào)試驗(yàn)證如下:

        4.2 接收端

        傳輸層 TCP 處理入口在 tcp_v4_rcv 函數(shù)(位于 linux/net/ipv4/tcp ipv4.c 文件中),它會做 TCP header 檢查等處理。

        調(diào)用 _tcp_v4_lookup,查找該 package 的 open socket。如果找不到,該 package 會被丟棄。接下來檢查 socket 和 connection 的狀態(tài)。

        如果socket 和 connection 一切正常,調(diào)用 tcp_prequeue 使 package 從內(nèi)核進(jìn)入 user space,放進(jìn) socket 的 receive queue。然后 socket 會被喚醒,調(diào)用 system call,并最終調(diào)用 tcp_recvmsg 函數(shù)去從 socket recieve queue 中獲取 segment。

        對于傳輸層的代碼階段,我們需要分析recv函數(shù),這個與send類似,調(diào)用的是__sys_recvfrom,整個函數(shù)的調(diào)用路徑與send非常類似:

        int __sys_recvfrom(int fd, void __user *ubuf, size_t size, unsigned int flags,
        struct sockaddr __user *addr, int __user *addr_len)

           ......
           err = import_single_range(READ, ubuf, size, &iov, &msg.msg_iter);
        if (unlikely(err))
        return err;
           sock = sockfd_lookup_light(fd, &err, &fput_needed);
           .....
           msg.msg_control = NULL;
           msg.msg_controllen = 0;
        Save some cycles and don't copy the address if not needed
           msg.msg_name = addr ? (struct sockaddr *)&address : NULL;
        We assume all kernel code knows the size of sockaddr_storage
           msg.msg_namelen = 0;
           msg.msg_iocb = NULL;
           msg.msg_flags = 0;
        if (sock->file->f_flags & O_NONBLOCK)
               flags |= MSG_DONTWAIT;
           err = sock_recvmsg(sock, &msg, flags);
        if (err >= 0 && addr != NULL) {
               err2 = move_addr_to_user(&address,
                            msg.msg_namelen, addr, addr_len);
           .....

        __sys_recvfrom調(diào)用了sock_recvmsg來接收數(shù)據(jù),整個函數(shù)實(shí)際調(diào)用的是sock->ops->recvmsg(sock, msg, msg_data_left(msg), flags);,同樣,根據(jù)tcp_prot結(jié)構(gòu)的初始化,調(diào)用的其實(shí)是tcp_rcvmsg

        接受函數(shù)比發(fā)送函數(shù)要復(fù)雜得多,因?yàn)閿?shù)據(jù)接收不僅僅只是接收,tcp的三次握手也是在接收函數(shù)實(shí)現(xiàn)的,所以收到數(shù)據(jù)后要判斷當(dāng)前的狀態(tài),是否正在建立連接等,根據(jù)發(fā)來的信息考慮狀態(tài)是否要改變,在這里,我們僅僅考慮在連接建立后數(shù)據(jù)的接收。

        tcp_rcvmsg函數(shù)如下:

        int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
        int flags, int *addr_len)

        ......
        if (sk_can_busy_loop(sk) && skb_queue_empty(&sk->sk_receive_queue) &&
        (sk->sk_state == TCP_ESTABLISHED))
        sk_busy_loop(sk, nonblock);
        lock_sock(sk);
        .....
        if (unlikely(tp->repair)) {
        err = -EPERM;
        if (!(flags & MSG_PEEK))
        goto out;
        if (tp->repair_queue == TCP_SEND_QUEUE)
        goto recv_sndq;
        err = -EINVAL;
        if (tp->repair_queue == TCP_NO_QUEUE)
        goto out;
        ......
        last = skb_peek_tail(&sk->sk_receive_queue);
        skb_queue_walk(&sk->sk_receive_queue, skb) {
        last = skb;
        ......
        if (!(flags & MSG_TRUNC)) {
        err = skb_copy_datagram_msg(skb, offset, msg, used);
        if (err) {
        Exception. Bailout!
        if (!copied)
        copied = -EFAULT;
        break;


        *seq += used;
        copied += used;
        len -= used;
        tcp_rcv_space_adjust(sk);

        <上一頁  1  2  3  4  下一頁>  
        聲明: 本文由入駐維科號的作者撰寫,觀點(diǎn)僅代表作者本人,不代表OFweek立場。如有侵權(quán)或其他問題,請聯(lián)系舉報(bào)。

        發(fā)表評論

        0條評論,0人參與

        請輸入評論內(nèi)容...

        請輸入評論/評論長度6~500個字

        您提交的評論過于頻繁,請輸入驗(yàn)證碼繼續(xù)

        • 看不清,點(diǎn)擊換一張  刷新

        暫無評論

        暫無評論

          人工智能 獵頭職位 更多
          掃碼關(guān)注公眾號
          OFweek人工智能網(wǎng)
          獲取更多精彩內(nèi)容
          文章糾錯
          x
          *文字標(biāo)題:
          *糾錯內(nèi)容:
          聯(lián)系郵箱:
          *驗(yàn) 證 碼:

          粵公網(wǎng)安備 44030502002758號

          主站蜘蛛池模板: 1024在线免费观看| AV色综合| 国产亚洲精品??码| 91视频网站免费观看| 三级片久久| 国产精品成人三级| 激情五月天婷婷| 91性爱视频| 平顶山市| 岛国在线无码| 亚洲综合成人网| 亚洲video| 人妻少妇视频| 中山市| 乌兰察布市| 女人天堂久久| AV一二三| 日韩无码专区| 中文字幕高清在线| 久久精品国产亚洲AV久| 漳平市| 亚洲天堂中文字幕| 亚洲综合无码| 玩弄丰满少妇一二三区| 91小电影| 久久精品区| 丁香五月激情综合| 南康市| 叙永县| 女人AV天堂| 寿宁县| 舟山市| 国产av一区二区三区| 久久无码一区二区三区| 亚洲?变态?欧美?另类?精品| Jizz日本18| 国产精品综合| 国产无码免费| 丰镇市| 怀宁县| 国产精品色哟哟|