2.3 Listenシステム呼び出し
C/Sモードでは、サーバプロセスはbindシステム呼び出しを呼び出さなければならない.そうすれば、対外的にサービスを提供するアドレスを決定することができる.クライアントプロセスはbindを呼び出すことができ、これはクライアントが要求を送信する際に使用するソースIPアドレスとポートを決定するためである.listenシステム呼び出しサーバプロセスは、クライアントプロセスにとってこの関数は不要です.Listenシステム呼び出しはいったい何をしたのですか.
Listenシステム呼び出しの関数プロトタイプ:
sockfdはsocketファイル記述子であり、backlogは処理待ちのリクエストを保存するキューの最大長である.対応するカーネルコードを見てみましょう.
sock->ops->listen指向inet_Listen関数:
222-233行はTFO(TCP Fast Open)機能に関連しており、この機能は後章で詳細に分析されている.
239行に設定された数値は、「3回の握手が完了し、サーバ側プロセスがacceptシステム呼び出しを行うのを待っている接続の最大数」を意味し、これらの接続が存在するキューは「backlogキュー」と呼ぶことができる.
Listenシステム呼び出しの主な機能はinet_csk_listen_start実装:
75行コードの機能はbacklogキューを初期化することです.
43行、49行、67行からqueue->listen_optが指すメモリのサイズはsizeof(struct listen_sock)+nr_table_Entries*sizeof(struct request_sock*)、struct listen_sockの定義は次のとおりです.
queue->listen_がわかりますopt->syn_tableはbacklogサイズのポインタ配列であり、syn_tableはSYNリクエスト情報を保存するために使用されます.
inet_に戻るcsk_listen_start関数.sk->sk_prot->get_port指向inet_csk_get_port関数.listenを呼び出す前にbindシステム呼び出しを呼び出すとsk->sk_が呼び出されましたprot->get_portは一度、bindとlistenの間にタイムウィンドウがあるため、ここで再び呼び出されます.プロセスは、このタイムウィンドウでsocketのプロパティ(sk->reuseなど)を変更する可能性があるため、ポートを再確認して使用可能であることを確認する必要があります.sk->sk_prot->hashが指す関数はinet_hash(IPv 4)またはtcp_v 6_hash(IPv 6)であり、socketをlisten hashテーブルに追加する役割を果たします.TCPv 4(つまりIPv 4のTCPを使用)およびTCPv 6(すなわちIPv 6を用いたTCP)のhashテーブルは同一である.
Listenシステム呼び出し機能の概要:
1、server端TFO機能を設定する;
2、acceptを初期化するqueue;その中のlisten_opt->syn_tableはyn queueであり、3回の握手完了を待つSYNリクエストの情報を保存するために使用される.rskq_accept_headとrskq_accept_tailは、3回の握手が完了し、acceptシステム呼び出しの接続を待つ情報を保存するために使用される.
3、bindシステム呼び出しでバインドされたアドレスが利用可能かどうかを再確認する.
4、sokcetをlisten hashテーブルに追加し、SYN要求が来たときに接続検索を行う.
以上の機能は、server側に必須の2 client側は不要であるため、server側プロセスはlistenシステム呼び出しを呼び出さなければならず、client側は不要である.
Listenシステム呼び出しの関数プロトタイプ:
int listen(int sockfd, int backlog);
sockfdはsocketファイル記述子であり、backlogは処理待ちのリクエストを保存するキューの最大長である.対応するカーネルコードを見てみましょう.
1526 SYSCALL_DEFINE2(listen, int, fd, int, backlog)
1527 {
1528 struct socket *sock;
1529 int err, fput_needed;
1530 int somaxconn;
1531
1532 sock = sockfd_lookup_light(fd, &err, &fput_needed);
1533 if (sock) {
1534 somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
1535 if ((unsigned int)backlog > somaxconn)
1536 backlog = somaxconn;
1537
1538 err = security_socket_listen(sock, backlog);
1539 if (!err)
1540 err = sock->ops->listen(sock, backlog);
1541
1542 fput_light(sock->file, fput_needed);
1543 }
1544 return err;
1545 }
sock->ops->listen指向inet_Listen関数:
195 int inet_listen(struct socket *sock, int backlog)
196 {
197 struct sock *sk = sock->sk;
198 unsigned char old_state;
199 int err;
...
214 if (old_state != TCP_LISTEN) {
...
222 if ((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) != 0 &&
223 inet_csk(sk)->icsk_accept_queue.fastopenq == NULL) {
224 if ((sysctl_tcp_fastopen & TFO_SERVER_WO_SOCKOPT1) != 0)
225 err = fastopen_init_queue(sk, backlog);
226 else if ((sysctl_tcp_fastopen &
227 TFO_SERVER_WO_SOCKOPT2) != 0)
228 err = fastopen_init_queue(sk,
229 ((uint)sysctl_tcp_fastopen) >> 16);
230 else
231 err = 0;
232 if (err)
233 goto out;
234 }
235 err = inet_csk_listen_start(sk, backlog);
236 if (err)
237 goto out;
238 }
239 sk->sk_max_ack_backlog = backlog; // backlog , listen ,
240 err = 0;
241
242 out:
243 release_sock(sk);
244 return err;
245 }
222-233行はTFO(TCP Fast Open)機能に関連しており、この機能は後章で詳細に分析されている.
239行に設定された数値は、「3回の握手が完了し、サーバ側プロセスがacceptシステム呼び出しを行うのを待っている接続の最大数」を意味し、これらの接続が存在するキューは「backlogキュー」と呼ぶことができる.
Listenシステム呼び出しの主な機能はinet_csk_listen_start実装:
751 int inet_csk_listen_start(struct sock *sk, const int nr_table_entries)
752 {
753 struct inet_sock *inet = inet_sk(sk);
754 struct inet_connection_sock *icsk = inet_csk(sk);
755 int rc = reqsk_queue_alloc(&icsk->icsk_accept_queue, nr_table_entries); // backlog accept_queue
756
757 if (rc != 0)
758 return rc;
759
760 sk->sk_max_ack_backlog = 0;
761 sk->sk_ack_backlog = 0;
762 inet_csk_delack_init(sk);
763
764 /* There is race window here: we announce ourselves listening,
765 * but this transition is still not validated by get_port().
766 * It is OK, because this socket enters to hash table only
767 * after validation is complete.
768 */
769 sk->sk_state = TCP_LISTEN; // TCP listen
770 if (!sk->sk_prot->get_port(sk, inet->inet_num)) {
771 inet->inet_sport = htons(inet->inet_num);
772
773 sk_dst_reset(sk);
774 sk->sk_prot->hash(sk);
775
776 return 0;
777 }
778
779 sk->sk_state = TCP_CLOSE;
780 __reqsk_queue_destroy(&icsk->icsk_accept_queue);
781 return -EADDRINUSE;
782 }
75行コードの機能はbacklogキューを初期化することです.
40 int reqsk_queue_alloc(struct request_sock_queue *queue,
41 unsigned int nr_table_entries)
42 {
43 size_t lopt_size = sizeof(struct listen_sock);
44 struct listen_sock *lopt;
45
46 nr_table_entries = min_t(u32, nr_table_entries, sysctl_max_syn_backlog);
47 nr_table_entries = max_t(u32, nr_table_entries, 8);
48 nr_table_entries = roundup_pow_of_two(nr_table_entries + 1);
49 lopt_size += nr_table_entries * sizeof(struct request_sock *);
50 if (lopt_size > PAGE_SIZE)
51 lopt = vzalloc(lopt_size);
52 else
53 lopt = kzalloc(lopt_size, GFP_KERNEL);
54 if (lopt == NULL)
55 return -ENOMEM;
56
57 for (lopt->max_qlen_log = 3;
58 (1 << lopt->max_qlen_log) < nr_table_entries;
59 lopt->max_qlen_log++); // socket SYN
60
61 get_random_bytes(&lopt->hash_rnd, sizeof(lopt->hash_rnd));
62 rwlock_init(&queue->syn_wait_lock);
63 queue->rskq_accept_head = NULL;
64 lopt->nr_table_entries = nr_table_entries;
65
66 write_lock_bh(&queue->syn_wait_lock);
67 queue->listen_opt = lopt;
68 write_unlock_bh(&queue->syn_wait_lock);
69
70 return 0;
71 }
43行、49行、67行からqueue->listen_optが指すメモリのサイズはsizeof(struct listen_sock)+nr_table_Entries*sizeof(struct request_sock*)、struct listen_sockの定義は次のとおりです.
94 struct listen_sock {
95 u8 max_qlen_log;
96 u8 synflood_warned;
97 /* 2 bytes hole, try to use */
98 int qlen;
99 int qlen_young;
100 int clock_hand;
101 u32 hash_rnd;
102 u32 nr_table_entries;
103 struct request_sock *syn_table[0];
104 };
queue->listen_がわかりますopt->syn_tableはbacklogサイズのポインタ配列であり、syn_tableはSYNリクエスト情報を保存するために使用されます.
inet_に戻るcsk_listen_start関数.sk->sk_prot->get_port指向inet_csk_get_port関数.listenを呼び出す前にbindシステム呼び出しを呼び出すとsk->sk_が呼び出されましたprot->get_portは一度、bindとlistenの間にタイムウィンドウがあるため、ここで再び呼び出されます.プロセスは、このタイムウィンドウでsocketのプロパティ(sk->reuseなど)を変更する可能性があるため、ポートを再確認して使用可能であることを確認する必要があります.sk->sk_prot->hashが指す関数はinet_hash(IPv 4)またはtcp_v 6_hash(IPv 6)であり、socketをlisten hashテーブルに追加する役割を果たします.TCPv 4(つまりIPv 4のTCPを使用)およびTCPv 6(すなわちIPv 6を用いたTCP)のhashテーブルは同一である.
Listenシステム呼び出し機能の概要:
1、server端TFO機能を設定する;
2、acceptを初期化するqueue;その中のlisten_opt->syn_tableはyn queueであり、3回の握手完了を待つSYNリクエストの情報を保存するために使用される.rskq_accept_headとrskq_accept_tailは、3回の握手が完了し、acceptシステム呼び出しの接続を待つ情報を保存するために使用される.
3、bindシステム呼び出しでバインドされたアドレスが利用可能かどうかを再確認する.
4、sokcetをlisten hashテーブルに追加し、SYN要求が来たときに接続検索を行う.
以上の機能は、server側に必須の2 client側は不要であるため、server側プロセスはlistenシステム呼び出しを呼び出さなければならず、client側は不要である.