C/C++linux 后台架构开发体系:高并发网络IO模型

本文深入探讨了高并发网络IO模型,包括传统模型的缺陷、多进程/线程模型的问题以及非阻塞和IO多路复用的改进。重点介绍了IO多路复用的三种实现——select、poll和epoll,特别是epoll的高效特性,以及在高并发场景下的优势。最后讨论了Reactor模式的不同变体,以应对不同性能需求和并发挑战。
摘要由CSDN通过智能技术生成

今天我们聊一下高并发下的网络 IO 模型

高并发即我们所说的 C10K(一个 server 服务 1w 个 client),C10M,写出高并发的程序相信是每个后端程序员的追求,高并发架构其实有一些很通用的架构设计,如无锁化,缓存等,今天我们主要研究下高并发下的网络 IO 模型设计,我们知道不管是 Nginx,还是 Redis,Kafka,RocketMQ 等中间件,都能轻松支持非常高的 QPS,其实它们背后的网络 IO 模型设计理念都是一致的,所以了解这一块对我们了解设计出高并发的网络 IO 框架具体重要意义,本文将会从以下几个方面来循序渐近地向大家介绍如何设计出一个高并发的网络 IO 框架

  • 传统网络 IO 模型的缺陷
  • 针对传统网络 IO 模型缺陷的改进
  • 多线程/多进程
  • 阻塞改为非阻塞
  • IO 多路复用
  • Reactor 的几种模型介绍

传统网络 IO 模型的缺陷

我们首先来看下传统网络 IO 模型有哪些缺陷,主要看它们的阻塞点有哪些。我们用一张图来看下客户端和服务端的基于 TCP 的通信流程

服务端的伪代码如下

listenSocket = socket(); //调用socket系统调用创建一个主动套接字
bind(listenSocket);  //绑定地址和端口
listen(listenSocket); //将默认的主动套接字转换为服务器使用的被动套接字,也就是监听套接字
while (1) { //循环监听是否有客户端连接请求到来
   connSocket = accept(listenSocket); //接受客户端连接
   recv(connsocket); //从客户端读取数据,只能同时处理一个客户端
   send(connsocket); //给客户端返回数据,只能同时处理一个客户端
}

可以看到,主要的通信流程如下

  1. server 创建监听 socket 后,执行 bind() 绑定 IP 和端口,然后调用 listen() 监听,代表 server 已经准备好接收请求了,listen 的主要作用其实是初始化半连接和全连接队列大小
  2. server 准备好后,client 也创建 socket ,然后执行 connect 向 server 发起连接请求,这一步会被阻塞,需要等待三次握手完成,第一次握手完成,服务端会创建 socket(这个 socket 是连接 socket,注意不要和第一步的监听 socket 搞混了),将其放入半连接队列中,第三次握手完成,系统会把 socket 从半连接队列摘下放入全连接队列中,然后 accept 会将其从全连接队列中摘下,之后此 socket 就可以与客户端 socket 正常通信了,默认情况下如果全连接队列里没有 socket,则 accept 会阻塞等待三次握手完成

经过三次握手后 client 和 server 就可以基于 socket 进行正常的进程通信了(即调用 write 发送写请求,调用 read 执行读请求),但需要注意的是 read,write 也很可能会被阻塞,需要满足一定的条件读写才会成功返回,在 LInux 中一切皆文件,socket 也不例外,每个打开的文件都有读写缓冲区,如下图所示

对文件执行 read(),write() 的具体流程如下

  1. 当执行 read() 时,会从内核读缓冲区中读取数据,如果缓冲区中没有数据,则会阻塞等待,等数据到达后,会通过 DMA 拷贝将数据拷贝到内核读缓冲区中,然后会唤醒用户线程将数据从内核读缓冲区拷贝到应用缓冲区中
  2. 当执行 write() 时,会将数据从应用缓冲区拷贝到内核写缓冲区,然后再通过 DMA 拷贝将数据从写缓冲区发送到设备上传输出去,如果写缓冲区满,则 write 会阻塞等待写缓冲区可写

经过以上分析,我们可以看到传统的 socket 通信会阻塞在 connect,accept,read/write 这几个操作上,这样的话如果 server 是单进程/线程的话,只要 server 阻塞,就不能再接收其他 client 的处理了,由此可知传统的 socket 无法支持 C10K

针对传统网络 IO 模型缺陷的改进

接下来我们来看看针对传统 IO 模型缺陷的改进,主要有两种

  1. 多进程/线程模型
  2. IO 多路程复用

多进程/线程模型

如果 server 是单进程,阻塞显然会导致 server 无法再处理其他 client 请求了,那我们试试把 server 改成多进程的?只要父进程 accept 了 socket ,就 fork 一个子进程,把这个 socket 交给子进程处理,这样就算子进程阻塞了,也不影响父进程继续监听和其他子进程处理连接

程序伪代码如下

while(1) {
  connfd = accept(listenfd);  // 阻塞建立连接
  // fork 创建一个新进程
  if (fork() == 0) {
    // accept 后子进程开始工作
    doWork(connfd);
  }
}
void doWork(connfd) {
  int n = read(connfd, buf);  // 阻塞读数据
  doSomeThing(buf);  // 利用读到的数据做些什么
  close(connfd);     // 关闭连接,循环等待下一个连接
}

通过这种方式确实解决了单进程 server 阻塞无法处理其他 client 请求的问题,但众所周知 fork 创建子进程是非常耗时的,包括页表的复制,进程切换时页表的切换等都非常耗时,每来一个请求就创建一个进程显然是无法接受的

为了节省进程创建的开销,于是有人提出把多进程改成多线程,创建线程(使用 pthread_create)的开销确实小了很多,但同样的,线程与进程一样,都需要占用堆栈等资源,而且碰到阻塞,唤醒等都涉及到用户态,内核态的切换,这些都极大地消耗了性能

由此可知采用多进程/线程的方式并不可取

画外音: 在 Linux 下进程和线程都是用统一的 task_struct 表示,区别不大,所以下文描述不管是进程还是线程区别都不大

阻塞改为非阻塞

既然多进程/多线程的方式并不可取,那能否将进程的阻塞操作(connect,accept,read/write)改为非阻塞呢,这样只要调用这些操作,如果相应的事件未准备好,就立马返回 EWOULDBLOCK 或 EAGAIN 错误,此时进程就不会被阻塞了,使用 fcntl 可以可以将 socket 设置为非阻塞,以 read 为例伪代码如下

connfd = accept(listenfd);
fcntl(connfd, F_SETFL, O_NONBLOCK);
// 此时 connfd 变为非阻塞,如果数据未就绪,read 会立即返回
int n = read(connfd, buffer) != SUCCESS; 

read 的非阻塞操作流程图如下

非阻塞read

这样的话调用 read 就不会阻塞等待而会马上返回了,也就实现了非阻塞的效果,不过需要注意的,我们这里说的非阻塞并非严格意义上的非阻塞,这里的非阻塞只是针对网卡数据拷贝到内核缓冲区这一段,如果数据就绪后,再执行 read 此时依然是阻塞的,此时用户进程会占用 CPU 去把数据从内核缓冲区拷贝到用户缓冲区中,可以看到这种模式是同步非阻塞的,这里我们简单解释下阻塞/非阻塞,同步/非同步的概念

  • 阻塞/非阻塞指的是在数据从网卡拷贝到内核缓冲区期间,进程能不能动
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux C/C++后台架构开发是一门非常具有前景的技能,在互联网和各种电子设备中都得到广泛应用。因此,这门课程的成长体系很重要,能够立足于现有技术发展趋势,不断完善内容,使学员能够跟随市场需求进行技术升级。 首先,课程应该注重基础知识的讲解,包括Linux操作系统的基础知识、C/C++编程语言的基础知识等,这是后续学习的基础。其次,应该注重实战训练,通过项目的实践来加深对知识的理解,并促进学员的技能提升。同时,要合理结合课程的理论知识和实践操作,培养学员的动手能力和实际应用技能。 除此之外,课程还应该关注行业技术变化的趋势,不断更新课程内容,讲解新技术的应用,使学员能够跟随技术的发展趋势提升自己。同时,要注重培养学员的团队合作能力和创新能力,帮助他们更好地适应团队工作和市场需求。 总之,Linux C/C++后台架构开发成长体系课程需要注重基础知识的讲解、实战训练、行业技术变化的跟进和团队合作与创新能力的培养。通过这些方面的努力,才能使学员掌握实用的技术,具备市场竞争力,并有能力适应未来技术的发展趋势。 ### 回答2: 作为一种开源的操作系统,Linux的应用广泛,尤其在服务器端,被广泛应用于Web服务器、数据库服务器等。因此,当今各大企业都需要专业的Linux后台架构开发人员来维护服务器的安全、稳定和高效运行。 而C/C++作为一种高效、可靠的编程语言,被广泛应用于Linux系统编程,尤其是在高性能、实时应用和底层驱动方面。因此,具备C/C++编程能力的Linux后台架构开发人员具有较高的市场竞争力。 在成长体系课程方面,专业的培训机构可以提供基础和高级的C/C++编程语言学习,以及Linux系统编程相关知识扎实的培训。而在课程设置和教学方式上,应该采取理论与实践相结合的方式,让学员在编程实践中逐渐掌握并理解相关知识。同时,在应用开发的过程中,特别是在搭建后台架构时,需要学会合理设计系统架构,选择合适的开发工具和技术,并能有效管理和维护系统。 此外,针对行业发展趋势和技术更新,培训机构应当不断更新课程内容,结合最新的技术趋势,为学员提供更具有竞争力的技术挑战,并开展多种实战项目实践。让学员在实践中提升自己的技能和实践经验,不断提升自身的职业竞争力。 ### 回答3: Linux C/C++后台架构开发是一个广受欢迎的领域,无论是大型互联网企业还是中小型企业都需要有相应的开发团队和技术人才。开发人员需要具备扎实的C/C++编程基础,熟悉Linux操作系统的运行机制和性能优化,了解分布式系统架构网络通信协议等知识,以及掌握一定的数据库开发和管理经验。 针对此领域的开发人员,成长体系课程可以提供以下培训内容: 一、Linux操作系统原理:Linux系统的运行机制,常用命令和工具的使用方法,文件系统和进程管理等。 二、C/C++编程:C/C++基础语法和编程规范,数据结构和算法,内存管理和锁机制等。 三、分布式系统架构:分布式系统的概念和架构,通信协议和数据传输方式,分布式存储和计算等。 四、网络通信:TCP/IP协议栈和网络编程,HTTP、Websocket等常用协议的使用和封装。 五、数据库开发和管理:常见数据库的概念和使用方法,SQL语言编写,数据库的设计和优化等。 通过以上培训内容的学习和实践,开发人员可以逐渐掌握Linux C/C++后台架构开发的技能和经验,不断提升能力和水平。同时,课程还将涉及团队协作和项目管理等方面的知识,培养学员的软技能和团队意识。最终,学员能够独立完成复杂的后台开发任务,为企业创造更大的价值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值