我是靠谱客的博主 细心电源,最近开发中收集的这篇文章主要介绍Linux 中系统调用 select,poll,epoll 优缺点总结selectpollepoll,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

select

示例

函数原型

typedef struct {
	unsigned long fds_bits [__FDSET_LONGS];
} fd_set;

int select(int ndfs, fd_set *readfds, fd_set *writefds, fd_set *exceptefds, struct timeval *timeout);

使用示例:

fd_set readfds,allfds;
FD_ZERO(&allfds);
FD_SET(1, &allfds);
...
for ( ; ; ) {
	readfds = allfds;
	nready = select(maxfds + 1, &readfds, NULL, NULL, NULL);
	if (nready > 0) {
		for (int i = 0; i <= maxfds; ++i) {
			if (FD_ISSET(i, &readfds)) {
				...
			}
		}
	}
}

缺点:

从上面可以看出:

  1. 无论关注多少个描述符,都要使用固定大小的 fd_set,数据太小时存在空间浪费,但是数据太多时,又存在最大描述符个数的限制,一般描述符总数个数为 1024。
  2. 每次函数调用从用户空间到内核空间,结果返回从内核空间到用户空间都要传递整个 fd_set,而不管有多少个感兴趣的描述符及激活的描述符。
  3. 结果返回后,还要遍历范围内所有的描述符以确定哪些描述符可用。
  4. 因为 fd_set 同时也是结果保存的地方,因此需要每次初始化感兴趣的描述符。
  5. 内核中的实现方式是轮询所有的描述符。

poll

示例

函数原型:

struct pollfd {
	int fd;
	short events;
	short revents;
}

int  poll(struct pollfd fds[], nfds_t nfds, int timeout);

使用示例:

struct pollfd client[OPEN_MAX];
client[0].fd = listenfd;
client[0].events = POLLRDNORM;
...
for ( ; ; ) {
	nready = poll(client, maxi, INFTIM);
	if (nready > 0) {
		for (int i=0; i < maxi; ++i) {
			if (client[i].events & POLLRDNORM) {
				...
			}
		} 
	}
}

优点:

相比于 select 的改进:

  1. 没有最大描述符个数的限制。
  2. 只用传递感兴趣的描述符,有利于提高在内核中轮询的效率。
  3. 不需要每次初始化感兴趣的描述符。

缺点:

  1. 每次函数调用,依旧需要从用户态和内核态之间传递所有描述符
  2. 函数返回后,依旧需要遍历所有描述符,以确定哪些描述符可用。
  3. 内核中依旧需要轮询所有描述符

epoll

示例

函数原型:

struct epoll_event {
	uint32_t events;
	epoll_data_t data;
}

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev);
int epoll_wait(int epfd, struct epoll_event *evlist, int maxevents; int timeout);

使用示例:

#define MAX_EVENTS 5

struct epoll_event ev;
struct epoll_event evlist[MAX_EVENTS];	# 每次函数调用返回的结果,设置一个较小数目

int epfd = epoll_create(1);
if (epfd == -1)
	error();

ev.data.fd = fd;
ev.events = EPOLLIN;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, ev) == -1)
	error();

for ( ; ; ) {
	nready = epoll_wait(epfd, evlist, MAXEVENTS, -1);
	if (nready > 0) {
		for (int i = 0; i < nready; ++i) {
			if (evlist[i].events & EPOLLIN) {
				...
			}
		}
	}
}

优点

  1. 感兴趣的描述符只用通过 epoll_ctl 添加一次,不再需要每次函数调用中传递。
  2. 返回的结果规模更小,里面只包含已经激活的描述符,一方面减轻从内核态返回数据的负担,另一方面不需要进行大规模遍历。
  3. epoll_wait 内核中使用回调函数替代轮询,提高效率。
  4. 通过设置 EPOLLONESHOT,可以实现水平触发和边缘触发的切换。

最后

以上就是细心电源为你收集整理的Linux 中系统调用 select,poll,epoll 优缺点总结selectpollepoll的全部内容,希望文章能够帮你解决Linux 中系统调用 select,poll,epoll 优缺点总结selectpollepoll所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部