Linux 中系统调用 select,poll,epoll 优缺点总结

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,可以实现水平触发和边缘触发的切换。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lylhw13_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值