- 論壇徽章:
- 0
|
這一節(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 |
|