当前位置:首页 > 嵌入式 > 嵌入式软件
[导读] 作者一直认为,从应用程序到框架再到系统,使用每一个代码是一回事理解。使用“今天,作者将研究服务器端套接字的功能。准确地说,它是bind(基于linux3.10)。

作者一直认为,从应用程序到框架再到系统,使用每一个代码是一回事理解。使用“今天,作者将研究服务器端套接字的功能。准确地说,它是bind(基于linux3.10)。

一个最简单的Server端例子

众所周知,一个Server端Socket的建立,需要socket、bind、listen、accept四个步骤。 

代码如下:

首先我们通过socket系统调用创建了一个socket,其中指定了SOCK_STREAM,而且最后一个参数为0,也就是建立了一个通常所有的TCP Socket。在这里,我们直接给出TCP Socket所对应的ops也就是操作函数。 

如果你想知道上图中的结构是怎么来的,可以看下笔者以前的博客:

 

bind系统调用

bind将一个本地协议地址(protocol:ip:port)赋予一个套接字。例如32位的ipv4地址或128位的ipv6地址+16位的TCP活UDP端口号。

好了,我们直接进入Linux源码调用栈吧。

inet_bind

inet_bind这个函数主要做了两个操作,一是检测是否允许bind,而是获取可用的端口号。这边值得注意的是。如果我们设置需要bind的端口号为0,那么Kernel会帮我们随机选择一个可用的端口号来进行bind!

让我们看下inet_bind的流程 

值得注意的是,由于对于<1024的端口号需要CAP_NET_BIND_SERVICE,我们在监听80端口号(例如启动nginx时候),需要使用root用户或者赋予这个可执行文件CAP_NET_BIND_SERVICE权限。

我们的bind允许绑定到0.0.0.0即INADDR_ANY这个地址上(一般都用这个),它意味着内核去选择IP地址。对我们最直接的影响如下图所示: 

然后,我们看下一个比较复杂的函数,即可用端口号的选择过程inet_csk_get_port (sk->sk_prot->get_port)

inet_csk_get_port

第一段,如果bind port为0,随机搜索可用端口号

直接上源码,第一段代码为端口号为0的搜索过程

由于,我们在使用bind的时候很少随机端口号(在TCP服务器来说尤其如此),这段代码笔者就注释一下。一般只有一些特殊的远程过程调用(RPC)中会使用随机Server端随机端口号。

第二段,找到端口号或已经指定

判断端口号是否冲突

在上述源码中,判断端口号时否冲突的代码为

上面代码的逻辑如下图所示: 

SO_REUSEADDR和SO_REUSEPORT

上面的代码有点绕,笔者就讲一下,对于我们日常开发要关心什么。 我们在上面的bind里面经常见到sk_reuse和sk_reuseport这两个socket的Flag。这两个Flag能够决定是否能够bind(绑定)成功。这两个Flag的设置在C语言里面如下代码所示:

在原生JAVA中

在Netty(Netty版本 >= 4.0.16且Linux内核版本>=3.9以上)中,可以使用SO_REUSEPORT。

SO_REUSEADDR

在之前的源码里面,我们看到判断bind是否冲突的时候,有这么一个分支

如果sk2(即已bind的socket)是TCP_LISTEN状态或者,sk2和新sk两者都没有设置_REUSEADDR的时候,可以判断为冲突。

我们可以得出,如果原sock和新sock都设置了SO_REUSEADDR的时候,只要原sock不是Listen状态,都可以绑定成功,甚至ESTABLISHED状态也可以! 

这个在我们平常工作中,最常见的就是原sock处于TIME_WAIT状态,这通常在我们关闭Server的时候出现,如果不设置SO_REUSEADDR,则会绑定失败,进而启动不来服务。而设置了SO_REUSEADDR,由于不是TCP_LISTEN,所以可以成功。 

这个特性在紧急重启以及线下调试的非常有用,建议开启。

SO_REUSEPORT

SO_REUSEPORT是Linux在3.9版本引入的新功能。

我们看下一般的Reactor线程模型, 

明显的其单线程listen/accept会存在瓶颈(如果采用多线程epoll accept,则会惊群,加WQ_FLAG_EXCLUSIVE可以解决一部分),尤其是在采用短链接的情况下。 鉴于此,Linux增加了SO_REUSEPORT,而之前bind中判断是否冲突的下面代码也是为这个参数而添加的逻辑:

这段代码让我们在多次bind的时候,如果设置了SO_REUSEPORT的时候不会报错,也就是让我们有个多线程(进程)bind/listen的能力。如下图所示: 

而开启了SO_REUSEPORT后,代码栈如下:

直接在内核层面做负载均衡,将accept的任务分散到不同的线程的不同socket上(Sharding),毫无疑问可以多核能力,大幅提升连接成功后的socket分发能力。

Nginx已经采用SO_REUSEPORT

Nginx在1.9.1版本的时候引入了SO_REUSEPORT,配置如下:

 

总结

Linux有一个非常复杂的内核源代码,希望能对读者有所帮助。

换一批

延伸阅读

[嵌入式软件] Linux Kernel 5.9.1 及更早版本发现数据泄露

Linux Kernel 5.9.1 及更早版本发现数据泄露

Linux内核最近发现了一系列错误,这些错误允许攻击者在没有经验证据的情况下获得访问机密数据和现有帐户的权限。 以下是漏洞详情: 漏洞详情 1.CVE-2020-26088 严重程度:高 net / nfc...

关键字: Linux kernel 漏洞

[嵌入式软件] Linux内核社区接受了SM2国密算法被

Linux内核社区接受了SM2国密算法被

制定了大量的密码管理方案和方案,并制定了大量的密码管理方案和规范。 随着近年来外部的国际贸易冲突和技术封锁,内部互联网的快速发展,IoT 领域的崛起,以及金融领域的变革愈演愈烈。摆脱对国外技术和产品的过度依赖,建设行业网络安全...

关键字: 算法 sm2 Linux

[嵌入式大杂烩] 从串口驱动到Linux驱动模型

点击上方「嵌入式大杂烩」,「星标公众号」第一时间查看嵌入式笔记! 原文:https://www.jianshu.com/p/3a9013b9569c 作者:Linker...

关键字: 嵌入式 编程 Linux

[嵌入式软件] Linux 5.8 内核,全局主题切换,全新组件上线

Linux 5.8 内核,全局主题切换,全新组件上线

10月23日,Yuqilin团队宣布ubuntukylin开源版本20.10正式发布。 20.10 是优麒麟发布的第 16 个版本,提供 9 个月的技术支持,与 Ubuntu 20.10、Lubuntu 20.10、Xubun...

关键字: 主题 优麒麟 Linux

[嵌入式分享] V4L2的管道驱动程序是如何被Xilinx Linux 理解的

V4L2的管道驱动程序是如何被Xilinx Linux 理解的

Xilinx提供了完整的V4L2的驱动程序,Xilinx V4L2 driver。处于最顶层的驱动程序是V4L2框架的视频管道(Video pipeline)驱动程序,也叫桥驱动程序(bridge driver),主要代码在文件xil...

关键字: Xilinx v4l2 管道驱动程序 Linux

嵌入式软件

15674 篇文章

关注

发布文章

技术子站

关闭