Linux学习之 多路复用

Linux学习之 多路复用

 

一.多路复用基本概念

IO多路复用是指内核一旦发现进程的一个或者多个IO条件准备读取,它就通知该进程。

在I/O编程过程中,当需要同时处理多个客户端接入请求时,可以利用多线程或者I/O多路复用技术进行处理I/O多路复用技术通过把多个I/O的阻塞复用到同一个select的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求。

二.多路复用的优点:

与传统的多线程/多进程模型比,I/O多路复用的优势有:

系统开销小,系统不需要创建新的额外进程或者线程,也不需要维护这些进程和线程的运行,降底了系统的维护工作量,节省了系统资源

 

.多路复用的应用场景

1. 服务器需要同时处理多个处于监听状态或者多个连接状态的套接字。

2. 服务器需要同时处理多种网络协议的套接字。

 

四.多路复用的函数

1.intselect(int  numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval  *timeout);

函数参数:

(1)第一个参数maxfdp1指定待测试的描述字个数,它的值是待测试的最大描述字加1(因此把该参数命名为maxfdp1),描述字0、1、2...maxfdp1-1均将被测试,因为文件描述符是从0开始的。

(2)中间的三个参数readset、writeset和exceptset指定我们要让内核测试读、写和异常条件的描述字。如果对某一个的条件不感兴趣,就可以把它设为空指针。struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符,可通过以下四个宏进行设置:

voidFD_ZERO(fd_set*fdset);           //清空集合

voidFD_SET(int fd, fd_set *fdset);   //将一个给定的文件描述符加入集合之中

voidFD_CLR(int fd, fd_set *fdset);   //将一个给定的文件描述符从集合中删除

intFD_ISSET(int fd, fd_set *fdset);   // 检查集合中指定的文件描述符是否可以读写 

(3)timeout告知内核等待所指定描述字中的任何一个就绪可花多少时间。其timeval结构用于指定这段时间的秒数和微秒数。

structtimeval{

longtv_sec;   //seconds

         long tv_usec;  //microseconds

     };

这个参数有三种可能:

(1)永远等待下去:仅在有一个描述字准备好I/O时才返回。为此,把该参数设置为空指针NULL。

(2)等待一段固定时间:在有一个描述字准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微秒数。

(3)根本不等待:检查描述字后立即返回,这称为轮询。为此,该参数必须指向一个timeval结构,而且其中的定时器值必须为0。

返回值:返回总的位数这些位对应已准备好的描述符,否则返回-1

 

select()函数实现IO多路复用的步骤

1)清空描述符集合

2)建立需要监视的描述符与描述符集合的关系

3)调用select函数

4)检查监视的描述符判断是否已经准备好

5)对已经准备好的描述符进程IO操作

 

五.多路复用的应用:

问题:采用管道函数创建有名管道,使用select函数替代使用poll函数实现多路复用: 创建两个有名管道,获取3个文件描述符(2个管道1个标准输入),然后初始化读文件描述符,select监视文件描述符的文件读写,管道1输出到屏幕上,管道2输出到屏幕上,标准输入‘Q’来进行判读是否退出。

设计流程:


编程代码:

#include <fcntl.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <string.h>  
#include <time.h>  
#include <errno.h>  
   
#define FIFO1 "in1"  
#define FIFO2 "in2"  
#define MAX_BUFFER_SIZE 1024  //缓冲区大小  
#define IN_FILES 3               //多路复用输入文件数目  
#define TIME_DELAY 60           //超时秒数  
   
#define MAX(a,b) ((a > b) ? (a) : (b))  
   
int main(void)  
{  
    int fds[IN_FILES];      //管道描述符  
    int i;  
    int res;  
    int real_read;  
    int maxfd;  
   
    char buf[MAX_BUFFER_SIZE];  
   
    struct timeval tv;  
   
    fd_set inset;  
    fd_set tmp_inset;           //文件描述符集  
   
    fds[0] = 0;          //终端的文件描述符  
   
    if(access(FIFO1,F_OK) == -1)         //创建两个有名管道  
    {  
        if((mkfifo(FIFO1,0666) < 0) && (errno != EEXIST))  
        {  
            printf("Cannot creat fifo1 file!\n");  
   
            exit(1);  
        }  
    }  
   
    if(access(FIFO2,F_OK) == -1)  
    {  
        if((mkfifo(FIFO2,0666) < 0) && (errno != EEXIST))  
        {  
            printf("Cannot creat fifo2 file\n");  
   
            exit(1);  
        }  
    }  
   
    if((fds[1] = open(FIFO1,O_RDONLY | O_NONBLOCK)) < 0)   //以只读非阻塞的方式打开两个管道文件  
    {  
        printf("open in1 error!\n");  
   
        return 1;  
    }  
   
    if((fds[2] = open(FIFO2,O_RDONLY | O_NONBLOCK)) < 0)  
    {  
        printf("open in2 error!\n");  
   
        return 1;  
    }  
   
    maxfd = MAX(MAX(fds[0],fds[1]),fds[2]);  //取出两个文件描述符中的较大者  
   
    //初始化读集inset,并在读文件描述符集中加入相应的描述集  
    FD_ZERO(&inset);     //将insert清零,使集合中不含任何fd  
    for(i = 0; i < IN_FILES; i++)  
    {  //将fds[i]加入inset集合  
        FD_SET(fds[i],&inset);  
    }  
   
    FD_SET(0,&inset);  
   
    tv.tv_sec = TIME_DELAY;   //设置超时60s  
    tv.tv_usec = 0;  
//循环测试该文件描述符是否准备就绪,并调用selelct()函数对相关文件描述符做相应的操作  
while(FD_ISSET(fds[0],&inset) || FD_ISSET(fds[1],&inset) || FD_ISSET(fds[2],&inset))  
    {    //文件描述符集的备份,以免每次都进行初始化  
        tmp_inset = inset;  
        res = select(maxfd+1,&tmp_inset,NULL,NULL,&tv);  
   
        switch(res)  
        {  
            case -1:  
                {  
                    printf("Select error!\n");  
   
                    return 1;  
                }  
                break;  
   
            case 0:  
                {  
                    printf("Time out!\n");  
   
                    return 1;  
                }  
                break;  
            default:  
                {  
                    for(i = 0; i < IN_FILES; i++)  
                    {  
                        if(FD_ISSET(fds[i],&tmp_inset))  
                        {  
                            memset(buf,0,MAX_BUFFER_SIZE);  
   
                            real_read = read(fds[i],buf,MAX_BUFFER_SIZE);  
   
                            if(real_read < 0)  
                            {  
                                if(errno != EAGAIN)  
                                {  
                                    return 1;  
                                }  
                            }  
                            else if(!real_read)  //已到达文件尾  
                            {  
                                close(fds[i]);  
   
                                FD_CLR(fds[i],&inset);  
                            }  
                            else  
                            {  
                                if(i == 0)  
                                {   //主程序终端控制  
                                    if((buf[0] == 'q') || (buf[0] == 'Q'))  
                                    {  
                                        return 1;  
                                    }  
                                }  
                                else  
                                {   //显示管道输入字符串  
                                    buf[real_read] = '\0';  
   
                                    printf("%s",buf);  
                                }  
                            }  
                        }  
                    }  
                }  
             break;  
        }  
    }  
   
    return 0;  


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值