百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Linux高性能服务器技术总结

gudong366 2025-06-29 21:39 4 浏览

1 服务器简介

服务器是提供计算服务的设备, 由于服务器需要响应用户请求,因此在处理能力、稳定性、安全性、可扩展性、可管理性等方面提出了较高要求。随着虚拟化技术的进步, 云服务器(ECS) 已经快速的在国内普及开来, 其管理方式比物理服务器更简单高效。用户可迅速创建或释放任意多台云服务器, 帮助企业降低开发运维的难度和整体 IT 成本, 使整个研发周期更专注于核心业务的创新。在网络环境下,根据服务器提供的服务类型不同,分为文件服务器、 数据库服务器、应用程序服务器、 WEB 服务器等。

此次学习总结的主要内容:

  • 如何处理多个客户端连接。
  • 探讨面对百万千万级客户端连接时的性能优化。
  • 服务器如何高效处理并发的数据。
  • 深度分析大数据通信时, Linux 内核瓶颈。
  • 如何攻克瓶颈

2 I/O复用技术

2.1 循环方式

当服务器有多个网络连接需要看管,那么循环遍历打开的网络连接的列表,来判断是否有要读取的数据。
缺点:

  • 速度缓慢(必须遍历所有的网络连接)
  • 效率低(处理一个连接时可能发生阻塞,妨碍其他网络连接的检查和处理)
    示例:
typedef struct ClientInfo{
int client_fd;
string client_ip;
}ClientInfo;//客户端结构体

std::deque<ClientInfo> m_client1;//客户端队列 1
std::deque<ClientInfo> m_client2;//客户端队列 2
void MServer::ClientHandel(std::deque<ClientInfo> *client){
	char data[1024] = {0};
	int len = 0;
	for(int i = 0; i < client->size(); ++i){
		//当没有数据可读时, 发生阻塞
		len = read(client->at(i).client_fd, data, sizeof data);
		//处理数据
		bzero(data,sizeof data);//清空缓存
	}
}

2.2 select 方式

select 首先将第二三四个参数指向的 fd_set 拷贝到内核,对每个被 SET 的描述符进行poll,记录在临时结果中(fdset),如果有事件发生, select 会将临时结果写到用户空间并返回。

缺点:

select 返回后,需要逐一检查描述符是否被 SET(事件是否发生)。(select 支持的

文件描述符数量太小了,默认是 1024)。

示例:

void MServer::ClientHandel(std::deque<ClientInfo> *client){
	char data[1024] = {0};
	fd_set input;// fdset 记录 poll 结果
	int len = 0;
	int retval = 0;
	FD_ZERO(&input);//清空记录
	for(int i = 0; i < client->size(); ++i){
		FD_SET(client->at(i).client_fd, &input);
		retval = select(client->at(i).client_fd + 1, &input, NULL, NULL, NULL);
		//检测事件是否发生
		if(retval > 0 && FD_ISSET(client->at(i).client_fd, &input)){
			//读取数据
			len = read(client->at(i).client_fd, data, sizeof data);
			//处理数据
			bzero(data,sizeof data);
		}
		//处理其他事情
	}
}

2.3 poll方式

poll 与 select 不同,通过一个 pollfd 数组向内核传递需要关注的事件,故没有描述符个数的限制, pollfd 中的 events 字段和 revents 分别用于标示关注的事件和发生的事件,故 pollfd 数组只需要被初始化一次。 poll 的实现机制与 select 类似,其对应内核中的 sys_poll,只不过poll 向内核传递 pollfd 数组,然后对 pollfd 中的每个描述符进行 poll,相比处理 fdset 来说, poll 效率更高。

缺点:

poll 需要对 pollfd 中的每个元素检查其 revents 值,来得知事件是否发生。

示例:

std::vector<struct pollfd> pollfds;
void MServer::ClientHandel(std::deque<ClientInfo> *client){
	int nready = 0;
	int len = 0;
	char data[1024] = {0};
	//初始化 pollfds 容器
	for(int i = 0; i < client->size(); ++i){
		struct pollfd pfd;
		pfd.fd = client->at(i).client_fd;//设置 pollfd
		pfd.events = POLLIN;//设置 pollin 事件
		pfd.revents = 0;//设置没有任何事件返回,置为零pollfds.push_back(pfd);
	}
	while(1){
		nready = poll(&*pollfds.begin(), pollfds.size(), -1);//负数表示无限等待,直到发生事
		件才返回
		for(PollFdList::iterator it = pollfds.begin(); it != pollfds.end() && nready > 0; ++it){ //遍历查看 fd 产生的事件
			if (it->revents & POLLIN){
				len = read(it->fd, buf, sizeof data);
				//处理数据
				bzero(data,sizeof data);
			}
		}
		//处理其他事情
	}
}

相关视频推荐

linux下的epoll实战揭秘——支撑亿级IO的底层基石

90分钟了解Linux内存架构,numa的优势,slab的实现,vmalloc原理

为什么dpdk越来越受欢迎,看完以后,让人醍醐灌顶

学习地址:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

2.4 epoll 方式

epoll 与 select、 poll 不同,其不用每次调用都向内核拷贝事件描述信息,在第一次调用后,事件信息就会与对应的 epoll 描述符关联起来。其次, epoll 不是通过轮询,而是通过在等待的描述符上注册回调函数,当事件发生时,回调函数负责把发生的事件存储在就绪事件链表中,最后写到用户空间。

epoll 返回后,该参数指向的缓冲区中即为发生的事件,对缓冲区中每个元素进行处理即可,而不需要像 poll、 select 那样进行轮询检查。

示例:

void MServer::ClientHandel(std::deque<ClientInfo> *client){
	int wait_fds;//事件产生的数量
	int i = 0;
	int len = 0;
	char data[1024] = {0};
	int epoll_fd = epoll_create(1024);//创建 epoll
	for(i = 0; i < client->size(); ++i){
		struct epoll_event ev;
		ev.events = EPOLLIN | EPOLLET;//设置触发事件的类型
		ev.data.fd = client->at(i).client_fd;
		//向 epoll 中增加 client_fd
		if( epoll_ctl( epoll_fd, EPOLL_CTL_ADD, client->at(i).client_fd, &ev ) < 0 ){
			printf("Epoll Error : %d\n", errno);
			exit( EXIT_FAILURE );
		}
		}struct epoll_event evs[1024];//epoll 事件缓存区
		while(1){
			if( ( wait_fds = epoll_wait( epoll_fd, evs, 0, -1 ) ) == -1 ){
			break;
		}
		for( i = 0; i < wait_fds; ++i){
			len = read( evs[i].data.fd, data, sizeof data);
			//处理数据
			bzero(data,sizeof data);
		}
	}
}

3 多线程方式

多线程技术也可以处理高并发的客户端连接,因为在服务器中可以创建大量的线程来监视连接。
缺点:
多线程技术则不太适合处理长连接,因为建立一个线程 linux 中会消耗栈空间, 当产生大量的连接后, 会导致系统内存消耗殆尽。
示例:

typedef struct ClientInfo{
	int client_fd;
	pthread_t pid;
	bool pthread_enlable;
}ClientInfo;
std::deque<ClientInfo> client;//客户端队列
void MServer::ClientHandel(){
	int i = 0;
		//创建多线程处理连接
	for(i = 0; i < client.size(); ++i){
		if(pthread_create(&client[i].pid, NULL, ClientPthread, &client[i]) != 0){
			client[i].pthread_enlable = true;
		}
	}
	//等待线程结束
	for(i = 0; i < client.size(); ++i){
		if(client[i].pthread_enlable) 
			pthread_join(client[i].pid, NULL);
	}
}//end func ClientHandel

void *MServer::ClientPthread(void *arg){
	char data[1024] = {0};
	int len = 0;
		while(1){
		len = read(((MServer*)arg)->client_fd, data, sizeof data);
		//处理数据
		bzero(data,sizeof data);//清空缓存
	}
	pthread_exit(NULL);
}

多线程 + I/O 复用技术,使用一个线程负责监听一个端口和描述符是否有读写事件产生,
再将事件分发给其他的工作线程处理数据。
模型架构:

这种架构主要是基于单线程 I/O 多路复用(select/poll/epoll),达到高并发效果,同时避免了多线程 I/O 来回切换的各种开销,而基于线程池的多工作者线程,进一步提高业务处理能力和避免产生过多线程。

4 CPU多核并行计算

程序的线程是指能同时并发执行的逻辑单元的个数,是通过时间片分配算法实现的;

CPU 的线程是指将 CPU 的指令执行过程(取指、译指、执行、 访存、写数)做出流水线从而提高并发度的方法。

并行计算和多线程的区别:

  • 并行计算比多线程具有更高的 CPU 利用率,因此效率相对更高。
  • 并行计算是利用 CPU 的多核进行计算,而多线程是利用 CPU 一个核在不同时间段内进行计算。
  • 并行计算是多个线程运行在多核 CPU 上,多线程是多线程运行在单核 CPU 上。

综合上述得出多线程并不能真正提高数据处理能力, 其局限于单核 CPU 的性能, 当服务器需要进行大量的数据运算(如图形处理、 复杂的算法) 时考虑多核并行计算。

5 深度分析内核性能

5.1 中断处理

当网络中大量数据包到来时,会产生频繁的硬件中断请求,这些硬件中断可以打断之前较低优先级的软中断或者系统调用的执行过程,如果这种打断频繁的话,将会产生较高的性能开销。

5.2 内存拷贝

正常情况下,一个网络数据包从网卡到应用程序需要经过如下的过程:数据从网卡通过 DMA (直接存储器访问) 等方式传到内核开辟的缓冲区,然后从内核空间拷贝到用户态空间,在 Linux 内核协议栈中,这个耗时操作甚至占到了数据包整个处理流程的 57.1%。

5.3 上下文切换

频繁到达的硬件中断和软中断都可能随时抢占系统调用的运行,这会产生大量的上下文切换开销。另外,在基于多线程的服务器设计框架中,线程间的调度也会产生频繁的上下文切换开销,同样,锁竞争的耗能也是一个非常严重的问题。

5.4 局部性失效

如今主流的处理器都是多个核心的,这意味着一个数据包的处理可能跨多个 CPU 核心,比如一个数据包可能中断在 cpu0,内核态处理在 cpu1,用户态处理在 cpu2,这样跨多个核心,容易造成 CPU 缓存失效,造成局部性失效。

5.5 内存管理

传统服务器内存页为 4K,为了提高内存的访问速度,避免 cache miss,可以增加 cache 中映射表的条目,但这又会影响 CPU 的检索效率。综合以上问题,可以看出内核本身就是一个非常大的瓶颈所在, 解决方案就是想办法绕过内核。

6 高性能网络框架DPDK

DPDK 为 Intel 处理器架构下用户空间高效的数据包处理提供了库函数和驱动的支持,它不同于 Linux 系统以通用性设计为目的,而是专注于网络应用中数据包的高性能处理。

DPDK 官网: https://www.dpdk.org/

DPDK 架构图:

Linux 内核网络数据流程:

  1. 硬件中断--->取包分发至内核线程--->软件中断--->内核线程在协议栈中处理包--->处理
  2. 完毕通知用户层
  3. 用户层收包-->网络层--->逻辑层--->业务层

DPDK 网络数据流程:

  1. 硬件中断--->放弃中断流程
  2. 用户层通过设备映射取包--->进入用户层协议栈--->逻辑层--->业务层

下面就具体看看 dpdk 做了哪些突破?

UIO (用户空间的 I/O 技术)的加持, dpdk 能够绕过内核协议栈,本质上是得益于 UIO技术,通过 UIO 能够拦截中断,并重设中断回调行为,从而绕过内核协议栈后续的处理流程。

相关推荐

梦幻诛仙12职业1亿元宝版本架设教程(包含资源下载)

架设教程:1;上传脚本zx到bin文件夹,给权限chmod-R777/bin2;输入zx安装宝塔面板输入对应序号,中途输入Y继续3;安装好之后,会给个宝塔地址及账号密码,复制地址...

Java零基础入门,科普Java你应该了解什么

最近很多人问我想学Java但是一点基础没有,网上看了一堆学习线路图还是无从下手。今天耗时3小时整理了一套保姆级的Java入门教程,建议收藏按照线路图一点点学习。一、Java的概况Java是1995年6...

01.Java发展历史(java发展历史简要)

1.Java发展历史Java由SunMicrosystems公司(现为Oracle公司)的JamesGosling及其团队在1991年开发,最初命名为"Oak",后改名为"...

Ubuntu16.04.1安装Java8(ubuntu终端安装java)

上篇文章讲解了怎么在Windows下安装Java8《Windows10安装Java8》,这里讲解下怎么在Linux下安装Java。由于之前已经安装了Ubuntu16.04.1《VmwareWorkst...

性能测试能力提升-JVM GC监控和优化

一、背景接着上一篇的知识:性能测试能力提升-JVMGC原理,本篇文章,我们将主要介绍JVMGC监控和优化相关的知识:命令行方式监控GC图形化方式监控GC什么时候需要开始GC优化?GC优化的目的GC...

Spring Boot Jar 包秒变 Docker 镜像实现多环境部署

你是否在互联网大厂后端开发工作中,遇到过这样的困扰?当完成一个SpringBoot项目开发,准备将Jar包部署到不同环境时,却发现各个环境依赖不同、配置复杂,部署过程繁琐又容易出错,不仅耗费...

「JDK 11」关于 Java 模块系统,看这一篇就够了

继2014年3月Java8发布之后,时隔4年,2018年9月,Java11如期发布,其间间隔了Java9和Java10两个非LTS(LongTermSupp...

对Java学习的10条建议(对java的认识和理解)

不少Java的初学者一开始都是信心满满准备迎接挑战,但是经过一段时间的学习之后,多少都会碰到各种挫败,以下北风网就总结一些对于初学者非常有用的建议,希望能够给他们解决现实中的问题。Java编程的准备:...

JAVA入门教程-第1章 概述(java入门指南)

大道至简-JAVA入门教程在本教程中,你将学习Java语言的基础知识。Java基础内容涵盖:Java基础概念、Java词法结构、Java数组、Java流程控制、Java字符串、Java...

推荐一款Java音频视频编码器,很赞

Jave2是什么JAVE2(Java音频视频编码器)库是ffmpeg项目上的Java包装器。开发人员可以利用JAVE2将音频和视频文件从一种格式转码为另一种格式。在示例中,您可以将AVI文件转换为MP...

【JAVA教程】JAVA入门及开发环境安装

一、Java开发环境概述Java开发需要三个核心组件:JDK(JavaDevelopmentKit)-Java开发工具包,开发、编译、调试Java程序JRE(JavaRuntimeE...

Windows和Linux环境下的JDK安装教程

JavaDevelopmentKit(简称JDK),是Java开发的核心工具包,提供了Java应用程序的编译、运行和开发所需的各类工具和类库。它包括了JRE(JavaRuntimeEnviro...

记Tomcat优化方案(tomcat优化的几种方法)

Tomcat服务吞吐量评估方案问题:评估方案在一台8核16G的linux服务器上,使用tomcat容器部署服务。在正常情况下如何评估这个tomcat服务可处理的连接数,即服务的吞吐量,请在正常情况下考...

JVM GC诡异问题排查,k8s差点害死我……

前言本文将通过一个真实的生产环境案例,详细展示如何系统性地排查和解决JVM垃圾收集问题。这个案例涵盖了从问题发现、分析诊断到最终解决的完整过程,对于理解JVM调优实战具有重要的参考价值。系统背景我们的...

Thorium Reader - 功能强大的跨平台免费电子书阅读器

在日常阅读日益普及的今天,选择一款合适的电子书阅读器至关重要。ThoriumReader作为一款独具特色的阅读应用,正逐渐在众多同类产品中崭露头角,为用户带来了卓越的阅读体验。  跨平台的便捷性...