select、poll、epoll用法

我们先从著名的C10K问题开始探讨,由于早期在网络还不普及的时候,互联网的用户并不是很多,一台服务器同时在线100个用户估计在当时已经算是大型应用了。但是随着互联网的发展,用户群体迅速的扩大,每一个用户都必须与服务器保持TCP连接才能进行实时的数据交互。Facebook这样的网站同一时间的并发TCP连接可能会过亿。这时候问题就来了。

解决这种问题的思路主要有两个:一个是对于每一个连接处理分配一个独立的进程或线程;另一种就是用同一进程或线程来同时处理若干个连接。

第一种解决方案,即来一个TCP链接,就需要分配一个进程/线程。而进程又是操作系统最昂贵的资源,一台机器无法创建很多进程,比如C10K问题就要创建1万个进程,操作系统是无法承受的。

第二中解决方案,即每个进程/线程同时处理多个连接(IO多路复用),我们讨论的select,poll,epoll都是基于这种思路的。

  1. 这种思路最简单的方法是循环挨个处理各个请求,每个连接对应一个socket,当所有的socket的有数据的时候,这种方法是可行的,但当应用读取某个socket的文件数据不ready的时候,整个应用就会阻塞在这里等待该文件句柄,即使别的文件句柄ready,也无法往下处理。
  2. select要解决的就是上面的阻塞问题,思路很简单,如果我在读取文件句柄之前,先查看它的状态,ready了就进行处理,不ready就不进行处理,这样就解决了这个问题了。
    1. select 使用fd_set结构体来告诉内核同时监控多个文件句柄,当其中有文件句柄的状态发生制定变化或超时,则调用返回。之后使用FD_ISSET来捉个查看是哪个文件句柄发生变化。
    2. fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。
    3. fd_set集合可以通过一些宏由人为的来操作,FD_ZERO(fd_set*):清空集合;FD_SET(int, fd_set*):将指定的文件描述符加入到集合;FD_CLR(int, fd_set*):将一个给定的文件描述符从集合中删除;FD_ISSET(int, fd_set*):判断指定的文件描述符是否可以读写。
    4. select函数原型:int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
      1. maxfdp:集合中所有文件描述符的范围,即所有文件描述符的最大值+1;
      2. readfds:指定我们要监视的读变化的文件描述符集合,即我们是否可以从这些文件中读取数据;
      3. writefds:指定我们要监视的写变化的文件描述符集合,即我们是否可以从这些文件中写数据;
      4. errorfds:用来监视文件错误异常;
      5. timeout:select 的超时时间,若将此参数设为NULL,表示阻塞,直到有文件描述符状态发生变化;若将此参数设为0秒0毫秒,表示非阻塞,直接返回,返回值为0表示没有文 件描述符发生变化,返回值为非0表示有文件描述符发生变化;若将此参数设为大于0,表示等待的时间,期间如果有事件发生则返回非0值,如果一直没有事件, 则等待此参数设置的时间,然后返回0。
    5. select例子:
      1. server.c:

        #include <sys/time.h>
        #include <netinet/in.h>
        #include <sys/select.h>
        #include <sys/types.h>
        #include <sys/socket.h>
        #include <unistd.h>
        #include <stdio.h>
        #include <stdlib.h>
        
        int main(){
            int socket1, socket2;
            struct sockaddr_in socket1addr, socket2addr;
            fd_set fds;
            struct timeval timeout = {3, 0};
            char buffer[256] = {0};
        
            socket1 = socket(PF_INET, SOCK_DGRAM, 0);
            //bzero(&socket1addr, sizeof(socket1addr));
            socket1addr.sin_family = AF_INET;
            socket1addr.sin_addr.s_addr = htonl(INADDR_ANY);
            socket1addr.sin_port = htons(36500);
            bind(socket1, (struct sockaddr*)&socket1addr, sizeof(socket1addr));
        
            socket2 = socket(PF_INET, SOCK_DGRAM, 0);
            //bzero(&socket2addr, sizeof(socket2addr));
            socket2addr.sin_family = AF_INET;
            socket2addr.sin_addr.s_addr = htonl(INADDR_ANY);
            socket2addr.sin_port = htons(36501);
            bind(socket2, (struct sockaddr*)&socket2addr, sizeof(socket2addr));
        
            while(1){
                FD_ZERO(&fds);
                FD_SET(socket1, &fds);
                FD_SET(socket2, &fds);
                int maxfdp = (socket1 > socket2) ? (socket1+1) : (socket2 + 1);
                int retval = select(maxfdp, &fds, NULL, NULL, &timeout);
                if(retval == -1){
                    printf("error\n");
                    return -1;
                }else if(retval == 0){
                    continue;
                }else{
                    struct sockaddr_in client;
                    int len = sizeof(client);
                    if(FD_ISSET(socket1, &fds)){
                        recvfrom(socket1, buffer, 256, 0, (struct sockaddr*)&client, &len);
                        printf("%u says:%s\n", ntohs(client.sin_port), buffer);
                    }
                    if(FD_ISSET(socket2, &fds)){
                        recvfrom(socket2, buffer, 256, 0, (struct sockaddr*)&client, &len);
                        printf("%u says:%s\n", ntohs(client.sin_port), buffer);
                    }
                }
        
            }
        }

      2. client.c

        #include <stdio.h>
        #include <string.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        
        int main(int argc, char **argv)
        {
            if(argc < 2){
                printf("usage: %s port\n", argv[0]);
                return -1;
            }
            int sockfd;
            struct sockaddr_in servaddr;
        
            sockfd = socket(PF_INET, SOCK_DGRAM, 0);
        
            bzero(&servaddr, sizeof(servaddr));
            servaddr.sin_family = AF_INET;
            servaddr.sin_port = htons(atoi(argv[1]));
            servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        
            char sendline[100];
            sprintf(sendline, "Hello, world!");
        
            sendto(sockfd, sendline, strlen(sendline), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
        
            close(sockfd);
        
            return 1;
        }

时间: 2024-10-07 15:26:58

select、poll、epoll用法的相关文章

select,poll,epoll用法

http://blog.csdn.net/sunboy_2050/article/details/6126712 select用法 #include <sys/time.h>       #include <sys/types.h>       #include <unistd.h> #include <sys/select.h> int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exc

IO复用一select, poll, epoll用法说明

三种IO复用类型 Select系统调用 #include<sys/select.h> int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* execptfds,struct timeval* timeout); #nfds表示监听的文件描述符总数: #readfds,writefds,execptfds分别表示对应的fd_set类型的集合 可以如下定义:fd_set readfds,writefds,execptfds

几种典型的服务器网络编程模型归纳(select poll epoll)

1.同步阻塞迭代模型 同步阻塞迭代模型是最简单的一种IO模型. 其核心代码如下: bind(srvfd); listen(srvfd); for(;;) { clifd = accept(srvfd,...); //开始接受客户端来的连接 read(clifd,buf,...); //从客户端读取数据 dosomthingonbuf(buf); write(clifd,buf)//发送数据到客户端 } 上面的程序存在如下一些弊端: 1)如果没有客户端的连接请求,进程会阻塞在accept系统调用处

Linux下select&amp;poll&amp;epoll的实现原理(一)

最近简单看了一把Linux linux-3.10.25 kernel中select/poll/epoll这个几个IO事件检测API的实现.此处做一些记录.其基本的原理是相同的,流程如下 先依次调用fd对应的struct file.f_op->poll()方法(如果有提供实现的话),尝试检查每个提供待检测IO的fd是否已经有IO事件就绪 如果已经有IO事件就绪,则直接所收集到的IO事件返回,本次调用结束 如果暂时没有IO事件就绪,则根据所给定的超时参数,选择性地进入等待 如果超时参数指示不等待,则

I/O多路复用之select,poll,epoll简介

一.select 1.起源 select最早于1983年出现在4.2BSD中(BSD是早期的UNIX版本的分支). 它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作. 2.select的优点 目前几乎在所有的平台上支持,具有良好的跨平台支持. 3.select的缺点 单个进程能够监视的文件描述符的数量存在最大限制.默认情况下,在Linux上单个进程能够打开的最

select/poll/epoll on serial port

In this article, I will use three asynchronous conferencing--select, poll and epoll on serial port to transmit data between PC and Raspberry pi. Outline Character device file of serial port Naive serial communication Asynchronous conferencing Select

转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】

下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架,底层在linux基于最新的epoll实现,为了更好的使用,了解其底层原理还是有必要的.下面记录下分别基于Select/Poll/Epoll的echo server实现.Python Select Server,可监控事件数量有限制: 1 2 3 4 5 6 7 8 9 10 11 12 13 14

Linux内核中网络数据包的接收-第二部分 select/poll/epoll

和前面文章的第一部分一样,这些文字是为了帮别人或者自己理清思路的,而不是所谓的源码分析,想分析源码的,还是直接debug源码最好,看任何文档以及书都是下策.因此这类帮人理清思路的文章尽可能的记成流水的方式,尽可能的简单明了. Linux 2.6+内核的wakeup callback机制 Linux 内核通过睡眠队列来组织所有等待某个事件的task,而wakeup机制则可以异步唤醒整个睡眠队列上的task,每一个睡眠队列上的节点都拥有一个 callback,wakeup逻辑在唤醒睡眠队列时,会遍历

Linux统系统开发12 Socket API编程3 TCP状态转换 多路IO高并发select poll epoll udp组播 线程池

[本文谢绝转载原文来自http://990487026.blog.51cto.com] Linux统系统开发12 Socket API编程3 TCP状态转换 多路IO高并发select  poll  epoll udp组播 线程池 TCP 11种状态理解: 1,客户端正常发起关闭请求 2,客户端与服务端同时发起关闭请求 3,FIN_WAIT1直接转变TIME_WAIT 4,客户端接收来自服务器的关闭连接请求 多路IO转接服务器: select模型 poll模型 epoll模型 udp组播模型 线

# 进程/线程/协程 # IO:同步/异步/阻塞/非阻塞 # greenlet gevent # 事件驱动与异步IO # Select\Poll\Epoll异步IO 以及selectors模块 # Python队列/RabbitMQ队列

1 # 进程/线程/协程 2 # IO:同步/异步/阻塞/非阻塞 3 # greenlet gevent 4 # 事件驱动与异步IO 5 # Select\Poll\Epoll异步IO 以及selectors模块 6 # Python队列/RabbitMQ队列 7 8 ############################################################################################## 9 1.什么是进程?进程和程序之间有什么