首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux tcp/ip 源码分析 - listen

Linux tcp/ip 源码分析 - listen

作者头像
KINGYT
发布2019-05-30 19:18:20
1.5K0
发布2019-05-30 19:18:20
举报

// net/socket.c SYSCALL_DEFINE2(listen, int, fd, int, backlog) { struct socket *sock; ... int somaxconn; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn; if ((unsigned int)backlog > somaxconn) backlog = somaxconn; ... if (!err) err = sock->ops->listen(sock, backlog); ... } return err; }

方法描述

1. 根据fd获取socket。

2. 如果用户提供的backlog值大于somaxconn,则把backlog值修改为somaxconn。

somaxconn值可以用sysctl命令修改,也可以通过下面的方式查看当前值

➜ ~ cat /proc/sys/net/core/somaxconn 128

3. 调用sock->ops->listen(sock, backlog)继续执行具体的listen逻辑。

由第一篇文章我们可以知道,sock->ops->listen指向的方法为inet_listen。

// net/ipv4/af_inet.c int inet_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; ... old_state = sk->sk_state; ... if (old_state != TCP_LISTEN) { ... err = inet_csk_listen_start(sk, backlog); ... } sk->sk_max_ack_backlog = backlog; err = 0; ... return err; } EXPORT_SYMBOL(inet_listen);

由该方法可以看到,即使socket已经为listen状态,还是可以对其调用listen方法的,只不过此种情况只会修改backlog值,而不影响其他。

该方法又调用了inet_csk_listen_start方法,继续看下。

// net/ipv4/inet_connection_sock.c int inet_csk_listen_start(struct sock *sk, int backlog) { struct inet_connection_sock *icsk = inet_csk(sk); struct inet_sock *inet = inet_sk(sk); ... reqsk_queue_alloc(&icsk->icsk_accept_queue); sk->sk_max_ack_backlog = backlog; sk->sk_ack_backlog = 0; ... sk_state_store(sk, TCP_LISTEN); if (!sk->sk_prot->get_port(sk, inet->inet_num)) { inet->inet_sport = htons(inet->inet_num); ... err = sk->sk_prot->hash(sk); if (likely(!err)) return 0; } ... return err; } EXPORT_SYMBOL_GPL(inet_csk_listen_start);

方法描述

1. 初始化icsk->icsk_accept_queue队列。

tcp建立连接完成之后,对应的sock就会放到这个队列中,之后accept方法再从这个队列中获取。

2. 初始化最大backlog和当前backlog值。

3. 设置sock状态为TCP_LISTEN。

4. 调用sk->sk_prot->get_port(sk, inet->inet_num)方法做 listening transition 验证。

该方法的逻辑在第二篇文章中有讲。

5. 调用sk->sk_prot->hash(sk)方法将sk放到全局listening的hashtable中。

由第一篇文章我们可以知道,sk->sk_prot->hash指向的方法为inet_hash。

// net/ipv4/inet_hashtables.c int inet_hash(struct sock *sk) { int err = 0; if (sk->sk_state != TCP_CLOSE) { ... err = __inet_hash(sk, NULL); ... } return err; } EXPORT_SYMBOL_GPL(inet_hash);

该方法又调用了__inet_hash,继续看下

// net/ipv4/inet_hashtables.c int __inet_hash(struct sock *sk, struct sock *osk) { struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo; struct inet_listen_hashbucket *ilb; int err = 0; ... ilb = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)]; ... if (IS_ENABLED(CONFIG_IPV6) && sk->sk_reuseport && sk->sk_family == AF_INET6) hlist_add_tail_rcu(&sk->sk_node, &ilb->head); else hlist_add_head_rcu(&sk->sk_node, &ilb->head); ... return err; } EXPORT_SYMBOL(__inet_hash);

方法描述

1. 根据sk的端口等信息,找到其所属的listening_hash中的bucket。

hashinfo->listening_hash为存放listen sock的hashtable,在对sk进行hash后,根据hash值,找到其所属的bucket列表,ilb变量用来指向这个bucket列表的头。

2. 将sk添加到这个列表中。

3. 返回err状态给上层。

完。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linux内核及JVM底层相关技术研究 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档