亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊(cè) 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 1063 | 回復(fù): 0
打印 上一主題 下一主題

內(nèi)核中的TCP的追蹤分析-7-TCP(IPV4)的socket接收連接 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2008-11-11 09:37 |只看該作者 |倒序?yàn)g覽

這一節(jié)我們開始分析如何接收TCP的socket的連接請(qǐng)求,象在Unix的socket分析章節(jié)一樣我們先看練習(xí)中的用戶界面
accept(server_sockfd, (struct sockaddr *)&client_address, client_len);
然后進(jìn)入內(nèi)核的系統(tǒng)調(diào)用函數(shù)中,這個(gè)過程請(qǐng)朋友們參考
http://blog.chinaunix.net/u2/64681/showart_1329029.html
 的詳細(xì)過程,我們直接從
err = sock->ops->accept(sock, newsock, sock->file->f_flags);
這部分開始入手分析TCP的socket是如何執(zhí)行的,這里會(huì)進(jìn)入inet_stream_ops中執(zhí)行,可能有些朋友是直接閱讀本文的,最好是看一下前面的章節(jié)理清是如何進(jìn)入這個(gè)函數(shù)的,我們這里不再重復(fù)了。
const struct proto_ops inet_stream_ops = {
    。。。。。。
    .accept         = inet_accept,
。。。。。。
};
我們?cè)俅慰匆幌耡f_inet.c中的這個(gè)數(shù)據(jù)結(jié)構(gòu),很顯然進(jìn)入了inet_accept()函數(shù)
int inet_accept(struct socket *sock, struct socket *newsock, int flags)
{
    struct sock *sk1 = sock->sk;
    int err = -EINVAL;
    struct sock *sk2 = sk1->sk_prot->accept(sk1, flags, &err);
    if (!sk2)
        goto do_err;
    lock_sock(sk2);
    BUG_TRAP((1  sk2->sk_state) &
         (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE));
    sock_graft(sk2, newsock);
    newsock->state = SS_CONNECTED;
    err = 0;
    release_sock(sk2);
do_err:
    return err;
}
進(jìn)入這個(gè)函數(shù)的時(shí)候已經(jīng)找到了我們前面建立的socket結(jié)構(gòu),而newsock是我們新分配建立的socket結(jié)構(gòu),我們看到上面函數(shù)中執(zhí)行了
struct sock *sk2 = sk1->sk_prot->accept(sk1, flags, &err);
進(jìn)而進(jìn)入了鉤子函數(shù)中執(zhí)行,參考
http://blog.chinaunix.net/u2/64681/showart_1360583.html
 那里的struct proto tcp_prot結(jié)構(gòu)變量可以看到
struct proto tcp_prot = {
    。。。。。。
    .accept            = inet_csk_accept,
    。。。。。。
};
很顯然是執(zhí)行的inet_csk_accept()函數(shù)
struct sock *inet_csk_accept(struct sock *sk, int flags, int *err)
{
    struct inet_connection_sock *icsk = inet_csk(sk);
    struct sock *newsk;
    int error;
    lock_sock(sk);
    /* We need to make sure that this socket is listening,
     * and that it has something pending.
     */
    error = -EINVAL;
    if (sk->sk_state != TCP_LISTEN)
        goto out_err;
    /* Find already established connection */
    if (reqsk_queue_empty(&icsk->icsk_accept_queue)) {
        long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
        /* If this is a non blocking socket don't sleep */
        error = -EAGAIN;
        if (!timeo)
            goto out_err;
        error = inet_csk_wait_for_connect(sk, timeo);
        if (error)
            goto out_err;
    }
    newsk = reqsk_queue_get_child(&icsk->icsk_accept_queue, sk);
    BUG_TRAP(newsk->sk_state != TCP_SYN_RECV);
out:
    release_sock(sk);
    return newsk;
out_err:
    newsk = NULL;
    *err = error;
    goto out;
}
象往常敘述的一樣首先是在sock中取得struct inet_connection_sock結(jié)構(gòu),然后判斷一下sock的狀態(tài)是否已經(jīng)處于監(jiān)聽狀態(tài),如果沒有處于監(jiān)聽狀態(tài)的話就不能接收了,只好出錯(cuò)返回了。接著是檢查icsk中的icsk_accept_queue請(qǐng)求隊(duì)列是否為空,因?yàn)槲覀兙毩?xí)中還未啟動(dòng)客戶端程序,所以此時(shí)還沒有連接請(qǐng)求到來,這個(gè)隊(duì)列現(xiàn)在是空的,所以進(jìn)入if語句,sock_rcvtimeo()是根據(jù)是否允許“阻塞”即等待,而取得sock結(jié)構(gòu)中的sk_rcvtimeo時(shí)間值,然后根據(jù)這個(gè)值進(jìn)入inet_csk_wait_for_connect()函數(shù)中
static int inet_csk_wait_for_connect(struct sock *sk, long timeo)
{
    struct inet_connection_sock *icsk = inet_csk(sk);
    DEFINE_WAIT(wait);
    int err;
    /*
     * True wake-one mechanism for incoming connections: only
     * one process gets woken up, not the 'whole herd'.
     * Since we do not 'race & poll' for established sockets
     * anymore, the common case will execute the loop only once.
     *
     * Subtle issue: "add_wait_queue_exclusive()" will be added
     * after any current non-exclusive waiters, and we know that
     * it will always _stay_ after any new non-exclusive waiters
     * because all non-exclusive waiters are added at the
     * beginning of the wait-queue. As such, it's ok to "drop"
     * our exclusiveness temporarily when we get woken up without
     * having to remove and re-insert us on the wait queue.
     */
    for (;;) {
        prepare_to_wait_exclusive(sk->sk_sleep, &wait,
                     TASK_INTERRUPTIBLE);
        release_sock(sk);
        if (reqsk_queue_empty(&icsk->icsk_accept_queue))
            timeo = schedule_timeout(timeo);
        lock_sock(sk);
        err = 0;
        if (!reqsk_queue_empty(&icsk->icsk_accept_queue))
            break;
        err = -EINVAL;
        if (sk->sk_state != TCP_LISTEN)
            break;
        err = sock_intr_errno(timeo);
        if (signal_pending(current))
            break;
        err = -EAGAIN;
        if (!timeo)
            break;
    }
    finish_wait(sk->sk_sleep, &wait);
    return err;
}
函數(shù)首先是調(diào)用了宏來聲明一個(gè)等待隊(duì)列
#define DEFINE_WAIT(name)                        \
    wait_queue_t name = {                        \
        .private    = current,                \
        .func        = autoremove_wake_function,        \
        .task_list    = LIST_HEAD_INIT((name).task_list),    \
    }
關(guān)于等待隊(duì)列的具體概念我們留在以后專門的章節(jié)中論述,這里可以看出是根據(jù)當(dāng)前進(jìn)程而建立的名為wait的等待隊(duì)列,接著函數(shù)中調(diào)用了
void
prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
    unsigned long flags;
    wait->flags |= WQ_FLAG_EXCLUSIVE;
    spin_lock_irqsave(&q->lock, flags);
    if (list_empty(&wait->task_list))
        __add_wait_queue_tail(q, wait);
    /*
     * don't alter the task state if this is just going to
      * queue an async wait queue callback
     */
    if (is_sync_wait(wait))
        set_current_state(state);
    spin_unlock_irqrestore(&q->lock, flags);
}
上面函數(shù)我們已經(jīng)在
http://blog.chinaunix.net/u2/64681/showart_1329029.html
 那一節(jié)中看到過了比較詳細(xì)的分析了,這個(gè)函數(shù)與我們所說的等待隊(duì)列部分內(nèi)容是密切相關(guān)的,我們只簡(jiǎn)單的敘述一下,函數(shù)中主要是將我們上面建立的等待隊(duì)列插入到這里的sock結(jié)構(gòu)中的sk_sleep所指定的等待隊(duì)列頭中,此后再次調(diào)用reqsk_queue_empty()函數(shù)檢查一下icsk_accept_queue是否為空,如果還為空就說明沒有連接請(qǐng)求到來,開始睡眠等待了,schedule_timeout()我們先放一放,這個(gè)函數(shù)與時(shí)鐘密切相關(guān),所以在以后再看,這里是根據(jù)我們上面得到的定時(shí)時(shí)間來進(jìn)入睡眠的。當(dāng)從這個(gè)函數(shù)返回時(shí),再次鎖住sock防止其他進(jìn)程打擾,然后這里還是判斷一下icsk_accept_queue是否為空,如果還為空的話就要跳出for循環(huán)了,醒來后還要檢查一下是否是因?yàn)樾盘?hào)而醒來的,如果有信號(hào)就要處理信號(hào)signal_pending(),這個(gè)函數(shù)在以后的信號(hào)內(nèi)容敘述,最后如果睡眠的時(shí)間已經(jīng)用完了也會(huì)跳出循環(huán),跳出循環(huán)后就要將這里的等待隊(duì)列從sock中的sk_sleep中摘鏈。
我們回到inet_csk_accept()函數(shù)中繼續(xù)往下看,如果這時(shí)隊(duì)列icsk_accept_queue不為空,即有連接請(qǐng)求到來怎么辦呢,繼續(xù)看下面的代碼

newsk = reqsk_queue_get_child(&icsk->icsk_accept_queue, sk);
這里看到是進(jìn)入了reqsk_queue_get_child函數(shù)中
static inline struct sock *reqsk_queue_get_child(struct request_sock_queue *queue,
                         struct sock *parent)
{
    struct request_sock *req = reqsk_queue_remove(queue);
    struct sock *child = req->sk;
    BUG_TRAP(child != NULL);
    sk_acceptq_removed(parent);
    __reqsk_free(req);
    return child;
}
函數(shù)中首先是調(diào)用了reqsk_queue_remove()從隊(duì)列中摘下一個(gè)已經(jīng)到來的request_sock結(jié)構(gòu)
static inline struct request_sock *reqsk_queue_remove(struct request_sock_queue *queue)
{
    struct request_sock *req = queue->rskq_accept_head;
    BUG_TRAP(req != NULL);
    queue->rskq_accept_head = req->dl_next;
    if (queue->rskq_accept_head == NULL)
        queue->rskq_accept_tail = NULL;
    return req;
}
很明顯上面函數(shù)中是從隊(duì)列的rskq_accept_head摘下一個(gè)已經(jīng)到來的request_sock這個(gè)結(jié)構(gòu)是從客戶端請(qǐng)求連接時(shí)掛入的,reqsk_queue_get_child()函數(shù)在這里把request_sock中載運(yùn)的sock結(jié)構(gòu)返回到inet_csk_accept中的局部變量newsk使用。而sk_acceptq_removed是遞減我們服務(wù)器端sock中的sk_ack_backlog。然后__reqsk_free釋放掉request_sock結(jié)構(gòu);氐絠net_csk_accept函數(shù)中,然后返回我們獲得的客戶端送來的sock結(jié)構(gòu)。就象我們?cè)趗nix的socket章節(jié)敘述的那樣,接著返回到sys_accept()函數(shù)中,具體的過程請(qǐng)看
http://blog.chinaunix.net/u2/64681/showart_1329029.html
 我們?cè)诰毩?xí)中看到需要獲得客戶端的地址,在那個(gè)章節(jié)中我們又走到了
newsock->ops->getname(newsock, (struct sockaddr )address, &len, 2)
這要看我們客戶端傳送過來的newsock結(jié)構(gòu)中的鉤子結(jié)構(gòu)了,很明顯我們因?yàn)槭侵饕槍?duì)的tcp的socket,所以這里仍舊進(jìn)入
struct proto tcp_prot = {
    .name            = "TCP",
    .owner            = THIS_MODULE,
    .close            = tcp_close,
    .connect        = tcp_v4_connect,
    .disconnect        = tcp_disconnect,
    .accept            = 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,
    .recvmsg        = tcp_recvmsg,
    .backlog_rcv        = tcp_v4_do_rcv,
    .hash            = inet_hash,
    .unhash            = inet_unhash,
    .get_port        = inet_csk_get_port,
    .enter_memory_pressure    = tcp_enter_memory_pressure,
    .sockets_allocated    = &tcp_sockets_allocated,
    .orphan_count        = &tcp_orphan_count,
    .memory_allocated    = &tcp_memory_allocated,
    .memory_pressure    = &tcp_memory_pressure,
    .sysctl_mem        = sysctl_tcp_mem,
    .sysctl_wmem        = sysctl_tcp_wmem,
    .sysctl_rmem        = sysctl_tcp_rmem,
    .max_header        = MAX_TCP_HEADER,
    .obj_size        = sizeof(struct tcp_sock),
    .twsk_prot        = &tcp_timewait_sock_ops,
    .rsk_prot        = &tcp_request_sock_ops,
    .h.hashinfo        = &tcp_hashinfo,
#ifdef CONFIG_COMPAT
    .compat_setsockopt    = compat_tcp_setsockopt,
    .compat_getsockopt    = compat_tcp_getsockopt,
#endif
};
但是在tcp_prot結(jié)構(gòu)中我們并沒有看到對(duì)應(yīng)的鉤子函數(shù),所以需要與客戶端的connect連接結(jié)合起來看,明天我們將敘述那里的過程,到時(shí)再加來看這里是如何把地址賦值給練習(xí)中的client_address地址結(jié)構(gòu)變量。sys_accept()函數(shù)余下的過程就完全與unix的socket連接過程完全一樣了,我們不重復(fù)了,請(qǐng)朋友們看
http://blog.chinaunix.net/u2/64681/showart_1329029.html
 結(jié)尾部分。


本文來自ChinaUnix博客,如果查看原文請(qǐng)點(diǎn):http://blog.chinaunix.net/u2/64681/showart_1404746.html
您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國(guó)互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請(qǐng)注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP