Linux事件驱动编程:深入解析poll机制 (linux中的poll)

在Linux内核的事件驱动编程中,有一个非常重要的机制,它就是poll机制。poll机制能够帮助开发者在高效地处理I/O事件的同时,减少CPU的消耗,提高程序的运行效率。本文将从以下几个方面深入解析Linux内核中poll机制的实现原理及使用方法:

1、基本概念

2、原理分析

3、使用方法

4、示例分析

1、基本概念

poll机制是一种事件驱动的I/O多路复用机制,它主要通过等待内核发生某些事件而触发应用程序的执行。poll机制可以同时监听多个文件描述符(即socket句柄),当有事件发生时,便将相应的事件状态通知给应用程序。

2、原理分析

poll机制的实现原理与select机制非常相似,不同的是,poll机制会通过一个struct pollfd类型的数组将需要监听的文件描述符传递给内核。在应用程序中,如果需要监听某个文件描述符,就需要将该描述符的相关信息(如文件描述符、等待的事件类型等)注册到struct pollfd类型的结构体中,然后将该结构体添加到pollfd数组中。在这个数组中,每个元素对应一个文件描述符,同时还有需要监听的事件的相关信息。当内核发现某个文件描述符上有一个或多个事件处于正等待状态时,便会通知到应用程序。

在poll机制中,应用程序可以使用poll函数进行文件描述符的监听。poll函数的原型如下:

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

其中,fds数组就是需要监听的文件描述符数组,nfds表示fds数组中元素的数量,timeout用于设置等待事件的超时时间(单位是毫秒)。

poll函数返回的是一个整型值,表示发生了事件的文件描述符的数量。如果返回值为-1,则表示poll函数调用失败。

3、使用方法

poll机制的使用非常简单,需要进行如下几个步骤:

1)使用open打开需要监听的文件(如socket文件)

2)将文件描述符(如socket fd)添加到struct pollfd数组中

3)使用poll函数监听struct pollfd数组

4)根据poll函数的返回值,处理已经发生事件的文件描述符

示例代码如下:

#include

#include

#include

#include

#include

#include

#include

#define MAX_EVENTS 10

#define SERVER_PORT 9999

int mn()

{

struct pollfd fds[MAX_EVENTS];

memset(fds, 0, sizeof(fds));

int listener_fd = socket(AF_INET, SOCK_STREAM, 0);

if(listener_fd

perror(“socket error”);

return 1;

}

int reuseaddr = 1;

setsockopt(listener_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr));

struct sockaddr_in server_addr;

memset(&server_addr, 0, sizeof(server_addr));

server_addr.sin_family = AF_INET;

server_addr.sin_port = htons(SERVER_PORT);

server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

if(bind(listener_fd, (struct sockaddr *)&server_addr, sizeof(server_addr))

perror(“bind error”);

return 1;

}

if(listen(listener_fd, 10)

perror(“listen error”);

return 1;

}

fds[0].fd = listener_fd;

fds[0].events = POLLIN | POLLPRI;

while(1) {

int result = poll(fds, MAX_EVENTS, 5000);

if(result

perror(“poll error”);

break;

}

if(result == 0) {

printf(“poll timeout\n”);

continue;

}

for(int i = 0; i

int fd = fds[i].fd;

short events = fds[i].events;

short revents = fds[i].revents;

if(fd

continue;

}

if(revents & POLLIN || revents & POLLPRI) {

if(fd == listener_fd) {

struct sockaddr_in client_addr;

socklen_t client_len = sizeof(client_addr);

int client_fd = accept(listener_fd, (struct sockaddr *)&client_addr, &client_len);

printf(“accept a client: %s:%d\n”, inet_ntoa(client_addr.sin_addr), client_addr.sin_port);

fds[i].fd = client_fd;

fds[i].events = POLLIN | POLLPRI;

}

else {

char buf[1024];

int len = recv(fd, buf, sizeof(buf), 0);

if(len > 0) {

buf[len] = 0;

printf(“recv: %s\n”, buf);

}

else if(len == 0) {

printf(“client closed\n”);

fds[i].fd = -1;

}

else {

perror(“recv error”);

fds[i].fd = -1;

}

}

}

}

}

close(listener_fd);

return 0;

}

在这个示例中,我们通过创建一个TCP连接的听者(listener),然后将listener的文件描述符添加到pollfd数组中。每次事件发生时,就根据revents的值来判断是listener fd上有新的TCP连接请求,还是已有的TCP连接上有数据到来。根据处理的不同,做出响应的处理。

4、示例分析

在这个示例中,我们通过使用poll函数对TCP连接文件描述符进行监听,实现了服务端的消息接收和响应。我们使用socket函数创建一个TCP监听者,并将其添加到fds数组中;然后使用poll函数不断地监听fds数组。

如果在poll函数调用时,发现有事件已经发生时,我们遍历fds数组,判断是否有listener fd上有新的TCP连接请求。如果确实有请求,我们调用accept函数,接受该请求,将返回的client_fd添加到fds数组中。如果已有的TCP连接上有数据到达,我们调用recv函数,进行数据读取,并处理读取到的数据。

如果发现TCP连接已经被关闭或者发生了错误,我们将该文件描述符从fds数组中删除,避免不必要的事件监听。通过这种方法,我们实现了TCP连接的并发处理,同时避免了CPU的过度使用,提高了程序的运行效率。

相关问题拓展阅读:

Linux中select poll和epoll的区别

============select、poll、epoll之间的区别=================

select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,

可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),

能够通知程序进行相应的读写操作,读写过程是阻塞的,

select的几大缺点:监视的文件描述符的数量存在更大限制

1 单个进程可监视的fd数量被限制

2 需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大

3 对socket进行扫描时是线性扫描

//select的3个缺点:1 连接数受限 2 查找配对速度慢 3 数据由内核拷贝到用户态。

poll的实现和select非常相似,pollfd并没有更大数量限制(但是数量过大后性能也是会下降)

select、poll的内部实现机制相似,它没有更大连接数的限制,原因是它是基纯闷于链肢念表来存储的,

但是同样有一个缺点:大量的fd的数组被整体复制于用户态和内核地址空间之间,

而不管这样的复制是不是有意义。它将用户传入的数组拷贝到内核空间,

然后查询每个fd对应的设备状态,如果做饥弯设备就绪则在设备等待队列中加入一项并继续遍历,这过程经历了多次无谓的遍历

epoll是对select和poll的改进

epoll:IO的效率不会随着监视fd的数量的增长而下降。epoll不同于select和poll轮询的方式,

而是通过每个fd定义的回调函数来实现的。只有就绪的fd才会执行回调函数。

一般情况下fd数量较少的时候poll略优于epoll,但是当fd增大到某个阈值时,

poll性能急剧下降。而epoll始终保持的稳定的性能。

希望采纳

Linux中select poll和epoll的区别

1、epoll处理是事件触发,而poll是轮训方空世式; 2、打开的FDset限制:poll是1024.,epoll无限制; 3、罩态poll系统调用数目增大时性能下降快物亏源

关于linux中的poll的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。


数据运维技术 » Linux事件驱动编程:深入解析poll机制 (linux中的poll)