F-Stack之kqueue封装为epoll介绍

F-Stack是一个全用户态的高性能的网络接入开发包,基于DPDK、FreeBSD协议栈、微线程接口等,适用于各种需要网络接入的业务,用户只需要关注业务逻辑,简单的接入F-Stack即可实现高性能的网络服务器。

F-Stack中使用的FreeBSD协议栈的高性能异步事件通知的API是kqueue,而Linux系统上则是我们熟悉的epoll,大量的Linux网络server都是基于epoll事件通知机制,为降低已有服务器接入F-Stack的修改难度,F-Stack协议栈实现了把kqueue封装为epoll接口,提供的API如下:

  • int ff_epoll_create(int size):创建epoll fd,底层实际调用FreeBSD协议栈的kern_kqueue()接口
  • int ff_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event):epoll事件操作函数,添加关心的fd和事件到epoll fd中,底层实际调用了FreeBSD协议栈的kern_kevent()接口
  • int ff_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout):epoll事件操作通知函数,底层实际调用了FreeBSD协议栈的kern_kevent()接口
  • int ff_epoll_close(int epfd):epoll fd的关闭函数,底层调用kern_close()

比较核心的ff_epoll_ctl()和ff_epoll_wait()代码实现如下:

	int ff_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
	{
		if (!event && op != EPOLL_CTL_DEL) {
	        ff_os_errno(ff_EINVAL);
			return -1;
		}
	
		struct kevent kev[3];
		if (op == EPOLL_CTL_ADD){
			EV_SET(&kev[0], fd, EVFILT_READ,
				EV_ADD | (event->events & EPOLLIN ? 0 : EV_DISABLE), 0, 0, NULL);
			EV_SET(&kev[1], fd, EVFILT_WRITE,
				EV_ADD | (event->events & EPOLLOUT ? 0 : EV_DISABLE), 0, 0, NULL);
			EV_SET(&kev[2], fd, EVFILT_USER, EV_ADD,
				    event->events & EPOLLRDHUP ? 1 : 0, 0, NULL);		
		} else if (op == EPOLL_CTL_DEL) {
			EV_SET(&kev[0], fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
			EV_SET(&kev[1], fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
			EV_SET(&kev[2], fd, EVFILT_USER, EV_DELETE, 0, 0, NULL);
		} else if (op == EPOLL_CTL_MOD) {
			EV_SET(&kev[0], fd, EVFILT_READ,
			    event->events & EPOLLIN ? EV_ENABLE : EV_DISABLE, 0, 0, NULL);
			EV_SET(&kev[1], fd, EVFILT_WRITE,
			    event->events & EPOLLOUT ? EV_ENABLE : EV_DISABLE, 0, 0, NULL);
			EV_SET(&kev[2], fd, EVFILT_USER, 0,
			    NOTE_FFCOPY | (event->events & EPOLLRDHUP ? 1 : 0), 0, NULL);		
		} else {
			ff_os_errno(ff_EINVAL);
			return -1;
		}
	
		return ff_kevent(epfd, kev, 3, NULL, 0, NULL);
	}

ff_epoll_ctl()核心是把Linux epoll的事件EPOLLIN、EPOLLOUT(其他的暂未支持)转成成FreeBSD的事件标EVFILT_READ、EVFILT_WRITE、EVFILT_USER。

	int ff_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
	{
		if (!events || maxevents < 1) {
			ff_os_errno(ff_EINVAL);
			return -1;
		}
		
		struct kevent *evlist = malloc(sizeof(struct kevent)*maxevents, M_DEVBUF, M_ZERO|M_NOWAIT);
		if(NULL == evlist){
			ff_os_errno(ff_EINVAL);
			return -1;		
		}
		memset(evlist, 0, sizeof(struct kevent)*maxevents);
		
		int ret = ff_kevent(epfd, NULL, 0, evlist, maxevents, NULL);
		if (ret == -1) {
			free(evlist, M_DEVBUF);
			return ret;
		}
	
		unsigned int event_one = 0;
		for (int i = 0; i < ret; ++i) {
			event_one = 0;
			if (evlist[i].filter & EVFILT_READ) {
				event_one |= EPOLLIN;
			} 
			if (evlist[i].filter & EVFILT_WRITE) {
				event_one |= EPOLLOUT;
			}
	
			if (evlist[i].flags & EV_ERROR) {
				event_one |= EPOLLERR;
			}
	
			if (evlist[i].flags & EV_EOF) {
				event_one |= EPOLLIN;		
			}
			events[i].events   = event_one;
			events[i].data.fd  = evlist[i].ident;
		}
		
		free(evlist, M_DEVBUF);
		return ret;
	}

ff_epoll_wait()的核心就是struct kevent结构和struct epoll_event的转换,把kqueue返回的fd和事件都封装到struct epoll_event结构中,返回给调用者。

一个实际的Server DEMO代码如下:

	int loop(void *arg)
	{
	    /* Wait for events to happen */
	
	    int nevents = ff_epoll_wait(epfd,  events, MAX_EVENTS, 0);
	    int i;
	
	    for (i = 0; i < nevents; ++i) {	
	        /* Handle new connect */
	        if (events[i].data.fd == sockfd) {
	            int nclientfd = ff_accept(sockfd, NULL, NULL);
	            assert(nclientfd > 0);
	            /* Add to event list */
	    	    ev.data.fd = nclientfd;
	    	    ev.events  = EPOLLIN;
	    	    assert(ff_epoll_ctl(epfd, EPOLL_CTL_ADD, nclientfd, &ev) == 0);
	            //fprintf(stderr, "A new client connected to the server..., fd:%d\n", nclientfd);
	        } else { 
	            if (events[i].events & EPOLLERR ) {
	                /* Simply close socket */
	        	ff_epoll_ctl(epfd, EPOLL_CTL_DEL,  events[i].data.fd, NULL);
	                ff_close(events[i].data.fd);
	                //fprintf(stderr, "A client has left the server...,fd:%d\n", events[i].data.fd);
	            } else if (events[i].events & EPOLLIN) {
	                char buf[256];
	                size_t readlen = ff_read( events[i].data.fd, buf, sizeof(buf));
	                //fprintf(stderr, "bytes are available to read..., readlen:%d, fd:%d\n", readlen,  events[i].data.fd);
	        	if(readlen > 0){
	                    ff_write( events[i].data.fd, html, sizeof(html));
	        	} else {
	        	    ff_epoll_ctl(epfd, EPOLL_CTL_DEL,  events[i].data.fd, NULL);
	                    ff_close( events[i].data.fd);
	                    //fprintf(stderr, "A client has left the server...,fd:%d\n", events[i].data.fd);		
	        	}
	            } else {
	                fprintf(stderr, "unknown event: %8.8X\n", events[i].events);
	            }
	        }
	    }
	}

实际运行结果:

F-Stack 的 github 主页源代码:https://github.com/F-Stack/f-stack

原文发布于微信公众号 - FStack(F-Stack)

原文发表时间:2017-06-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏乐沙弥的世界

启用用户进程跟踪

仅仅需要标识该会话并为该会话启用跟踪(专用模式为一对一模式,即一个用户进程对应一个服务器进程)

9720
来自专栏landv

SQLSERVER异机备份 T-SQL

47270
来自专栏青青天空树

springboot整合mybatis(xml+注解)

​ 刚毕业的第一份工作是java开发,项目中需要用到mybatis,特此记录学习过程,这只是一个简单demo,mybatis用法很多不可能全部写出来,有更复杂的...

12220
来自专栏一枝花算不算浪漫

MyBatis学习总结(八)——Mybatis3.x与Spring4.x整合

43490
来自专栏依乐祝

.NET Core实战项目之CMS 第五章 入门篇-Dapper的快速入门看这篇就够了

上篇文章我们讲了如在在实际项目开发中使用Git来进行代码的版本控制,当然介绍的都是比较常用的功能。今天我再带着大家一起熟悉下一个ORM框架Dapper,实例代码...

8600
来自专栏菩提树下的杨过

内存数据库到底有多快?

并发量太高的应用中(比如10分钟内插入300w条记录),数据库往往难堪重负,在没有银子实现服务器集群/负载均衡/分布式存储的情况下,可以尝试一下把数据库做一个临...

211100
来自专栏乐沙弥的世界

使用 resource_limit 及 profile 限制用户连接

      数据库性能是一个永恒的话题,那就是如何使用更少的资源以达到更高效的性能。Oracle系统参数RESOURCE_LIMIT是一个用于控制用户对于数据库...

12310
来自专栏Hadoop实操

如何使用StreamSets实现Oracle中变化数据实时写入Kudu

1K50
来自专栏乐沙弥的世界

Oracle 基于用户管理恢复的处理

Oracle支持多种方式来管理数据文件的备份与恢复来保证数据库的可靠与完整。除了使用RMAN工具以及第三方备份与恢复工具之外,基于

6520
来自专栏沃趣科技

复制状态与变量记录表 | performance_schema全方位介绍

不知不觉中,performance_schema系列快要接近尾声了,今天将带领大家一起踏上系列第六篇的征程(全系共7个篇章),在这一期里,我们将为大家全面讲解p...

20230

扫码关注云+社区

领取腾讯云代金券