linux:25IO复用-select

Linux下的五种IO模型

  1. 阻塞IO(blocking IO)
  2. 非阻塞IO (nonblocking IO)
  3. IO复用(select 和poll) (IO multiplexing)
  4. 信号驱动IO (signal driven IO (SIGIO))
  5. 异步IO (asynchronous IO (the POSIX aio_functions))

前四种都是同步,只有最后一种才是异步IO。

IO复用:将多个描述符统一监听,当任意一个文件描述符🈶️事件发生,都能及时处理

select,poll,epoll都是IO多路复用的机制,IO多路复用就是通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知应用程序进行相应的读写操作。但select,poll,epoll本质上都是同步IO,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步IO则无需自己负责进行读写,异步IO的实现会负责把数据从内核拷贝到用户空间

1.select:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

nfds为fdset集合中最大描述符值加1,

readfds:可读文件描述符

exceeds:异常文件描述符

writefds:可写文件描述符

timeout:参数为超时时间(如果为NULL,则select一直阻塞)

fdset是一个位数组,其大小限制为__FD_SETSIZE(1024),这些参数既是输入参数也是输出参数,可能会被内核修改用于标示哪些描述符上发生了关注的事件,所以每次调用select前都需要重新初始化fdset。该结构会被内核修改,其值为超时剩余的时间。

返回值:

1.>0 返回就绪文件描述符个数

2.==0 超时

3.==-1 出错

select的几大缺点:

(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大

(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大

(3)select支持的文件描述符数量太小了,默认是1024

 

问题:

1.如何将文件描述符分别设置到readfds,writefds,excefds

FD_ZERO(fd_set *set);//清空fdest的所有位

FD_SET(int  fd,fd_set*set);//设置fdest的位fd

FD_CLR(int fd,fd_*set);//清空fdest地位fd

2.如何知道该文件描述符有事件发生(select返回后,如何知道那些文件描述符就绪)

内核仅仅将在fd_set结构体变量中将就绪文件描述符的位修改           1变为0

3.应用程序如何探测就绪文件描述符 

循环 FD_ISSET()   真——就绪

4.每次调用select之前都必须重新设置 readfds writefds exceptfds

代码:

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<string.h>

#include<assert.h>

#include<sys/select.h>

#include<sys/socket.h>

#include<sys/types.h>

#include<arpa/inet.h>

#include<netinet/in.h>

void Init_fds(int *fds ,int len)

{

    int i=0;

    for(;i<len;++i)

    {

        fds[i] = -1;

    }

}



void Insert_fd(int *fds, int fd,int len)

{

    int i= 0;

    for(;i<len;++i)

    {

        if(fds[i] == -1)

        {

            fds[i] = fd;

            break;

        }

    }

}



void Delect_fd(int *fds,int fd,int len)

{

    int i=0;

    for(;i<len;++i)

    {

        if(fds[i] == fd)

        {

            fds[i] = -1;

            break;

        }

    }

}



int main()

{

  //TCP服务器设置 socket   bind    listen 

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

    assert(sockfd != -1);



    struct sockaddr_in ser,cli;

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

    ser.sin_family = AF_INET;

    ser.sin_port = htons(6000);

    ser.sin_addr.s_addr = inet_addr("127.0.0.0.1");



    int res  = bind (sockfd,(struct sockaddr*)&ser,sizeof(ser));

    assert(res !=-1);



    listen(sockfd,5);



    fd_set  readfds;

    int fds[100];

    Init_fds(fds,100);

   //将sockfd添加到fds中

    Insert_fd(fds,sockfd,100);

  //启动while循环

    while(1)

    {

        int maxfd =-1;

       //将fds中的文件描述符设置到readfds上

        FD_ZERO(&readfds);

        int i=0;

        for(;i<100;++i)

        {

            if(fds[i] != -1)

            {

                if(fds[i] > maxfd)

                {

                    maxfd =fds[i];

                }

                FD_SET(fds[i],&readfds);

            }

         }

        //启动select

         int n =select (maxfd+1 ,&readfds,NULL,NULL,NULL);

         if(n<=0)

         {

             printf("select fail\n");

             continue;

         }

       //循环检测文件描述符就绪

         for(i=0;i<100;++i)

         {

             if(fds[i] !=-1 && FD_ISSET(fds[i],&readfds))

             {

                 if(fds[i] == sockfd)

                 {

                     int len =sizeof(cli);

                      //三次握手

                     int c = accept(sockfd, (struct sockaddr*)&cli,&len);

                     if(c<0)

                     {

                         printf("one client link error\n");

                         continue;

                     }



                     Insert_fd(fds, c,100);

                 }

                 else

                 {

                     int fd = fds[i];

                     char buff[128] = {0};

                     int n = recv(fd,buff,127,0);

                   //链接fd

                     if(n<=0)

                     {

                         close(fd);

                         Delete_fd(fds,fd,100);

                         continue;

                     }



                     printf("%d: %s\n",fd ,buff);

                     send(fd,"OK",2,0);

                 }

             }

        }

    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值