首页 文学文摘 时政新闻 科技科普 经济法律 健康生活 管理财经 教育教学 文化艺术 社科历史

基于EPOLL机制的Linux网络服务器设计与实现

作者:何凯 来源:电子技术与软件工程

摘 要

笔者在开发一款Linux网络服务器时,经过对多种机制的比较,发现EPOLL机制效率最高,该机制可以满足并发处理海量网络连接的要求,非常适合高性能服务器的设计要求。因此笔者对EPOLL机制进行了深入的研究,并且设计了一款基于EPOLL机制的高效的Linux网络服务器。在实际应用中,该服务器有着稳定、高效的特点。

【关键词】EPOLL Linux服务器

1 引言

笔者在实际工作中,需要开发一款基于Linux的高性能网络服务器,该服务器与前端的多种嵌入式设备通过有线网络或者无线网络连接。在对Linux网络编程技术分析后,最终选择了EPOLL机制。在经过几个月的调试、改进之后,服务器可以准确、稳定、高效的工作。在模拟测试中,该服务器可以稳定的连接与控制前端10万个设备。取得了良好的效果。

2 Linux中I/O多路复用技术分析

Linux上的I/O多路复用技术主要有select模型、poll模型和epoll模型。

select模型最大的缺点是最大并发数限制,因为一个进程所打开的socket FD(文件描述符)是有限制的,默认是1024,因此select模型的最大并发数就被限制了。当然,我们可以通过修改系统参数增大最大并发数,然而由于select的每次调用都会线性扫描全部的socket FD集合。当socket FD增大时,会导致服务器效率线性下降。

poll模型基本上和select在效率上是一样的,select的问题在poll模型中也没有被解决。

epoll模型是目前比较优秀的I/O多路复用模型,首先,epoll没有最大并发连接的限制,上限是整个系统最大可以打开的socket FD数目,这个数远大于2048,一般这个数目和系统内存关系很大,1G内存的机器的最大socket FD数目可以达到10万左右。其次,epoll最大的优点在于它只关心活跃的连接,而跟连接总数无关。因此在实际的网络环境中,epoll的效率会远高于select和poll。

epoll相关的系统调用有:epoll_create, epoll_ctl和epoll_wait。其中epoll_create用来创建一个epoll文件描述符,epoll_ctl用来添加/修改/删除需要侦听的文件描述符及其事件,epoll_wait接收发生在被侦听的描述符上的,用户感兴趣的IO事件。epoll文件描述符用完后,直接用close关闭即可,非常方便。事实上,任何被侦听的文件符只要其被关闭,那么它也会自动从被侦听的文件描述符集合中删除,很是智能。

3 框架设计与系统实现

3.1 系统设计

整个系统分为三个部分:前端探测设备、网络服务器、上位机。

该网络服务器用于连接、监控和控制前端多种嵌入式探测设备,这些探测设备分布于广阔的空间范围,并通过有线网络或者无线网络与网络服务器相连。

网络服务器设计是本文的核心。网络服务器向下连接大量的探测设备,向上连接上位机。网络服务器的框架如图1所示。

多种前端嵌入式探测设备用于探测所在位置的温度、湿度、风速,以及其他意外突发情况,这些嵌入式设备将探测到的数据,以某种协议格式发送给网络服务器,可能是通过有线网络,也有可能通过无线网络。这些数据在网络服务器上存储并转发给上位机显示处理。

探测设备与网络服务器之间,用心跳帧维护链路。有的探测设备会主动发送心跳帧给服务器,有些探测设备则需要网络服务器发送心跳帧询问。对方收到心跳帧后,都给予应答帧。

3.2 服务器实现

(1)将服务器进程设置成实时进程

struct sched_param p;

p.sched_priority = 9;

sched_setscheduler(0,SCHED_RR,&p);

(2)设置服务器时钟,只负责计时,其他事情不做,方式是记录流逝的秒数。使用全局变量ec,必须设置成原子的。在用sigaction函数登记的信号处理函数中可以做的处理是被严格限定的,所以变量ec必须是“volatile sig_atomic_t”类型的全局变量。代码如下:

static volatile sig_atomic_t ec = 0;

static void sig1sec_handler(int sig)

{

++ec;

}

下面代码,在主程序中设置并启动定时器,设定每一秒钟触发一次信号,该定时器信号导致函数sig1sec_handler的执行,该函数使得全局变量ec自增一。

signal(SIGALRM, &sig1sec_handler);

struct itimerval t;

t.it_interval.tv_sec = 1;

t.it_interval.tv_usec = 0;

t.it_value = t.it_interval;

setitimer(ITIMER_REAL, &t, NULL);

(3)设置进程的最大打开文件限制,相当于设置进程能够管理的TCP链接数量。设置数量限制为10万。

int cn = 100000;

struct rlimit rlim, new;

new.rlim_cur = new.rlim_max = cn;

while (setrlimit(RLIMIT_NOFILE, &new)!=0) {

cn -= 1000;

new.rlim_cur = new.rlim_max = cn;

}

(4)捕获SIGPIPE信号。如果向收到RST的TCP链路写入数据,进程会收到SIGPIPE信号而终止进程,SIGPIPE信号的缺省处理方式就是终止进程,现在改成忽略处理。

signal(SIGPIPE, SIG_IGN);

(5)核心代码。

Step1:建立epoll系统。

epfd = epoll_create(10000)

Step2:处理和时钟(定时器)有关的事情。根据当前的计时,处理一切有关时间的事情。每次定时器信号都会使得进程从epoll_wait中出来,所以该函数在信号发生后有执行的机会。先把计时的秒数读取出来。如果秒数为零,则返回。对于每一个有效的TCP链路,将心跳帧放到各自的发送队列中排队。如果心跳帧发过3次,仍然没有收到应答帧,则关闭并且删除该TCP链路。

Step3:此时进程会进入睡眠。等待探测设备TCP链接的连入,或者链路有写入读取的要求,或者其他请求。epoll_wait函数也会因为信号返回,包括定时器信号,这个时候会跳到Step2。

struct epoll_event events[10000];

n = epoll_wait(epfd, events, 10000, -1);

Step4:函数返回后,events[]数组中保存n个有效的事件。这些事件有几种情况:

如果是有新链路接入,则用accept函数获取这个新的链路,并且用epoll_ctl函数把这个新链路放入epoll中进行监控,当然,这个新链路必须设置成非阻塞的。

如果是输入EPOLLIN事件,则读取相应的数据,进行处理。

如果是输出EPOLLOUT事件,说明链路的输出缓冲区变空了,下面可以再发送数据了。

如果是其他进程的通信管道信息,则与其他进行进行通信。

跳到Step2,以此构成循环。

4 测试

测试用服务器的配置:

CPU:Intel Xeon 2.4G

内存:4G

网卡:千兆

测试用客户机的配置:

CPU:Intel Core 2.66G

内存:4G

网卡:千兆

由于本方案是特定的服务器,使用了用户自定义的用户网络协议,因此业界通用的一些测试程序不能很好的完成测试。鉴于此,笔者自己采用epoll机制编写了测试程序。模拟多用户链接服务器,进而模拟海量用户连接服务器,服务器都能稳定的运行,并且符合项目各项要求。

参考文献

[1]吴旭东.高性能Linux网络服务器设计与实现[J].电脑编程技巧与维护,2011,20(2):89-90.

[2]王明路,王希敏,王哲.嵌入式系统中池式内存分配方法的分析[J].计算机与数字工程,2008,36(2):57-61.

[3]孙晓辉,王劲林,陈晓.实时系统中的动态内存分配算法[J].计算机工程,2008,34(8):80-84.

作者简介

何凯(1981-),男,河北省黄骅市人。硕士学位。现为黄骅市职业技术教育中心讲师。主要研究方向为计算机应用技术。

作者单位

黄骅市职业技术教育中心 河北省黄骅市 061100