IO多路复用的三种实现:poll

系统调用poll()执行的任务同select()很相似。两者间主要的区别在于我们要如何指定待检查文件描述符。在select()中,我们提供三个集合,在每个集合中标明我们感兴趣的文件描述符。而在poll()中我们提供一列文件描述符,并在每个文件描述符上标明我们感兴趣的事件。参数fds列出了我们需要poll()来检查的文件描述符。该参数为pollfd结构体数组,其定义如下。

IO多路复用的三种实现:poll_第1张图片

 

 poll参数

参数nfds制定了数组fds中元素的个数。数据类型nfds_t实际为无符号整型。

 pollfd结构体中的events和revents字段都是位掩码。调用者初始化events来指定需要为描述符fd

做检查的事件。当poll()返回时,revents被设定以此来表示该文件描述符上事件发生的事件。

下表列出了可能会出现在events和revents字段中的位掩码。

IO多路复用的三种实现:poll_第2张图片

 

poll的Linux实现:

尽管被定义为不同的位掩码,POLLIN和POLLRDNORM 是同义词。

尽管被定义为不同的位掩码,POLLOUT和POLLWRNORM是同义词。

一般来说POLLRDBAND是不被使用的,也就是说它在events 字段中被忽略,也不会设定到revents 中去。

尽管在特定情形下可用于对套接字的设定,POLLWRBAND并不会传达任何有用的信息。(不会出现当 POLLOUT 和POLLWRNORM 没有设定,而设定了POLLWRBAND的情况。)

必须定义_XOPEN_ _SOURCE 测试宏,这样才能在头文件中得到常量POLLRDNORM、POLLRDBAND、 POLLWRNORM 以及POLLWRBAND的定义。

POLLRDHUP 是Linux 专有的标志位,从2.6.17 版内核以来就一直存在。要在头文件中得到它的定义,必须定义_ GNU_ _SOURCE测试宏。

如果指定的文件描述符在调用po11()时关闭了,则返回POLLNVAL。

总结以上要点,po110真正关心的标志位就是POLLIN、POLLOUT、 POLLPRI、POLLRDHUP、POLLHUP 以及POLLERR。 我们下文中以更详尽的方式讨论这些标志位的意义。

关于timeout和在select中是同样的使用方式

 poll的返回值:

返回-1表示有错误发生。一种可能的错误是EINTR,表示该调用被一个信号处理例程中断。

返回0表示该调用在任意一个文件描述符称为就绪态之前就超时了。

返回正整数表示有1个或多个文件描述符处于就绪态了。返回值表示数组fds中拥有非零revetns字段的pollfd结构体数量。

注意select同poll返回正整数时的细小差别。如果一个文件描述符在返回的描述符集合中出现了不止一次,系统调用select会将同一个文件描述符统计多次。而系统调用poll返回的是就绪态的文件描述个数,且一个文件描述只会统计一次,就算再相应的revents字段中设定了多个位掩码也是如此。

select和poll的区别:

select ()所使用的数据类型fd_ set 对于被检查的文件描述符数量有-一个上限限制(FD_ SETSIZE) 。在Linux下,这个上限值默认为1024, 修改这个上限需要重新编译源文件。与之相反,po11()对于 被检查的文件描述符数量本质上是没有限制的。

由于select()的参数fd_ set同时也是保存调用结果的地方,如果要在循环中重复调用select()的话,我们必须每次都要重新初始化fd_ set。而po11()通过独立的两个字段events (针对输入) 和revents (针对输出)来处理,从而避免每次都要重新初始化参数。

select ()提供的超时精度(微秒)比po110提供的超时精度(毫秒)高。(这两个系统调用的超时精度都受软件时钟粒度的限制。)

如果其中一个被检查的文件描述符关闭了,通过在对应的revents 字段中设定POLLNVAL标记,po11 ()会准确告诉我们是哪一个文件描述符关闭了。与之相反,select() 只会返回-1,并设错误码为EBADF。 通过在描述符上执行I/0系统调用并检查错误码,让我们自己来判断哪个文件描述符关闭了。通常这些区别都不重要,因为应用程序一般都会 自己跟踪已经关闭的文件描述符。

select和poll存在的问题:

每次调用select或poll内核都必须检查所有被指定的文件描述符,看他们是否处于就绪态状态。当检查大量文件描述符且处于活跃的文件描述符又比较少时,检查耗费的时间将大大超过接下来的处理,效率比较低。

每次调用select或poll时,程序都必须传递一个表示所有需要被检查的文件描述的数据结构到内核,内核检查过描述符后,修改这个数据结构并返回给程序。(此外,对于select来说,我们还必须在每次调用前初始化这个数据结构。)对于poll来说,随着待检查的文件描述符数量的增加,传递给内核的数据结构大小也会随之增加。当检查大量文件描述符时,从用户空间到内核空间来回拷贝这个数据结构将占用大量的CPU时间。对于select来说,这个数据结构的大小固定为FD——SETSIZE,与待检查的文件描述数量无关。

利用poll单进程实现可多客户端连接的服务器代码:

#include
#include
#include                                                              
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define SERPORT 8000
#define SERIP "192.168.6.174"


int main(int argc, char* argv[])
{
    int lfd = socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in seraddr,cliaddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(SERPORT);
    int dst;
    inet_pton(AF_INET,SERIP,(void*)&dst);
    seraddr.sin_addr.s_addr = dst ;

    int ret = bind(lfd,(struct sockaddr*)&seraddr,sizeof(seraddr));
    listen(lfd,64);

    socklen_t addrlen = sizeof(cliaddr);

    char buf[1024];
    char clip[32];
    struct pollfd events[1025];
    int i;
    for(i = 0;i<1025;i++){
        events[i].fd = -1;
    }
    //将lfd交给poll监听
    int maxi;
    events[0].fd = lfd;
    events[0].events = POLLIN;
    maxi = 0;
    while(1){
        int pret = poll(events, maxi+1, -1);
        if(pret < 0){
            perror("pol error");
            exit(1);
        }
        if(events[0].revents&POLLIN){
            int  cfd = accept(lfd,(struct sockaddr*)&cliaddr,&addrlen);
            if(cfd<0){
                perror("accept error");
                exit(1);
            }
            //网络字节序整形IP地址转化成一个本地字节序点分十进制的字符窜IP
            inet_ntop(AF_INET,&cliaddr.sin_addr,clip,sizeof(clip));
            printf("clien IP=%s,PORT=%d connect ok\n",clip,ntohs(cliaddr.sin_port));
            printf("与客户端建立连接\n");
            for(i = 1; i<1025; i++){
                if(events[i].fd == -1){
                    events[i].fd = cfd;
                    events[i].events = POLLIN;
                    break;
                }
            }
            if(i>maxi);
            maxi = i;
            if(--pret == 0);
            continue;
        }
        for(i = 1;i

你可能感兴趣的:(linux网络编程,linux,服务器,网络,信息与通信)