网络编程:IO多路复用(五个IO模型)

   1. 定义:线程或进程同时监测若干个文件描述符是否可以执行IO操作的能力
    
   2. 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标的输入、中断信号等等事件,再比如web服务器如nginx,需要同时处理来来自N个客户端的事件。
    
    逻辑控制流在时间上的重叠叫做 并发
    
    而CPU单核在同一时刻只能做一件事情,一种解决办法是对CPU进行时分复用(多个事件流将CPU切割成多个时间片,不同事件流的时间片交替进行)。在计算机系统中,我们用线程或者进程来表示一条执行流,通过不同的线程或进程在操作系统内部的调度,来做到对CPU处理的时分复用。这样多个事件流就可以并发进行,不需要一个等待另一个太久,在用户看起来他们似乎就是并行在做一样。
    
    2. 使用并发处理的成本:
    线程/进程创建成本
    CPU切换不同线程/进程成本 Context Switch
    多线程的资源竞争
    
    有没有一种可以在单线程/进程中处理多个事件流的方法呢?一种答案就是 IO多路复用

    因此IO多路复用解决的本质问题是在用更少的资源完成更多的事(read\write)

IO模型
    1、阻塞IO  
    2、非阻塞IO    EAGAIN  忙等待 errno(提高效率)
    3、信号驱动IO  SIGIO 用的相对少(了解)
    4、并行模型    进程,线程
    5、IO多路复用  select、poll、epoll(效率相对高些) 

一、阻塞io

二、非阻塞io

 非阻塞IO ===》在阻塞IO的基础上 调整其为 不再阻塞等待。
 在程序执行阶段调整文件的执行方式为非阻塞:
        fcntl() ===>动态调整文件的阻塞属性

    #include <unistd.h>
    #include <fcntl.h>

    int fcntl(int fd, int cmd, ... /* arg */ );
    功能:修改指定文件的属性信息。
    参数:fd    要调整的文件描述符
              cmd 要调整的文件属性宏名称
               ...   可变长的属性值参数
    返回值:成功  不一定,看cmd
                  失败  -1;

    eg:修改文件的非阻塞属性:
        int flag ;
        flag  = fcntl(fd,F_GETFL,0);  ///获取fd文件的默认属性flag变量中
        flag  = flag | O_NONBLOCK;  ///将变量的值调整并添加非阻塞属性
        fcntl(fd,F_SETFL,flag);   ///将新属性flag设置到fd对应的文件生效。

        以上代码执行后的阻塞IO将变成非阻塞方式。

 fifo_w.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc, char *argv[])
{
    int ret = mkfifo("myfifo",0666);    
    if(-1 == ret)
    {
        //如果是管道文件已存在错误,让程序继续运行
        if(EEXIST== errno)
        {

        }else 
        {
            perror("mkfifo");
            exit(1);
        }
    }
    //open 会阻塞,等到另一端读段打开,解除阻塞
    int fd = open("myfifo",O_WRONLY);
    if(-1 == fd)
    {
        perror("open");
        exit(1);
    }
    while(1)
    {
        char buf[256]="hello,fifo,test";
        write(fd,buf,strlen(buf));
        sleep(3);
    }
    close(fd);

    return 0;
}

fifo_r.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    int ret = mkfifo("myfifo",0666);    
    if(-1 == ret)
    {
        //如果是管道文件已存在错误,让程序继续运行
        if(EEXIST== errno)
        {
        
        }else 
        {
            perror("mkfifo");
            exit(1);
        }
    }

    int fd = open("myfifo",O_RDONLY);
    if(-1 == fd)
    {
        perror("open");
        exit(1);
    }
    int flag = fcntl(fd,F_GETFL);
    flag = flag|O_NONBLOCK  ; 
    fcntl(fd,F_SETFL,flag);

    flag = fcntl(0,F_GETFL);
    fcntl(0,F_SETFL,flag|O_NONBLOCK);
    while(1)
    {
        char buf[256]={0};
        if(read(fd,buf,sizeof(buf))>0)
        {
            printf("fifo %s\n",buf);
        }
        bzero(buf,sizeof(buf));
        if(fgets(buf,sizeof(buf),stdin))
        {
            printf("terminal:%s\n",buf);
        }
    }
    close(fd);
    //remove("myfifo");

    return 0;
}

三、信号驱动io

文件描述符需要追加 O_ASYNC 标志
设备有io事件可以执行时内核发送SIGIO信号

 1)追加标志
        int flag ;
        flag  = fcntl(fd,F_GETFL,0);
        fcntl(fd,F_SETFL,flag | O_ASYNC);    
 2)设置信号接收者
        fcntl(fd,F_SETOWN,getpid());//常用设置
3)对信号进行捕获
        signal(SIGIO,myhandle);

fifo_w.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc, char *argv[])
{
    int ret = mkfifo("myfifo",0666);    
    if(-1 == ret)
    {
        //如果是管道文件已存在错误,让程序继续运行
        if(EEXIST== errno)
        {

        }else 
        {
            perror("mkfifo");
            exit(1);
        }
    }
    //open 会阻塞,等到另一端读段打开,解除阻塞
    int fd = open("myfifo",O_WRONLY);
    if(-1 == fd)
    {
        perror("open");
        exit(1);
    }

    while(1)
    {
        char buf[256]="hello,fifo,test";
        write(fd,buf,strlen(buf));
        sleep(3);
    }
    close(fd);

    return 0;
}

 fifo_r.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
int fd;
void handle(int num)
{
    char buf[256]={0};
    read(fd,buf,sizeof(buf));
    printf("fifo %s\n",buf);

}
int main(int argc, char *argv[])
{
    signal( SIGIO,handle);
    int ret = mkfifo("myfifo",0666);    
    if(-1 == ret)
    {
        //如果是管道文件已存在错误,让程序继续运行
        if(EEXIST== errno)
        {
        
        }else 
        {
            perror("mkfifo");
            exit(1);
        }
    }

    fd = open("myfifo",O_RDONLY);
    if(-1 == fd)
    {
        perror("open");
        exit(1);
    }
    int flag = fcntl(fd,F_GETFL);
    fcntl(fd,F_SETFL ,flag|O_ASYNC);

    fcntl(fd,F_SETOWN, getpid() );
    while(1)
    {
        char buf[256]={0};
        fgets(buf,sizeof(buf),stdin);
        printf("terminal:%s\n",buf);
    }
    close(fd);
    //remove("myfifo");

    return 0;
}

四、并行

五、io多路复用

1.阻塞版

select循环服务器 ===> 用select函数动态检测 有数据流动的文件描述符
    #include <sys/select.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>

    int select(int nfds, fd_set *readfds, fd_set *writefds,
                
fd_set *exceptfds,struct timeval *timeout);
    功能:完成指定描述符集合中有效描述符的动态检测。
              该函数具有阻塞等待功能,在函数执行完毕后,目标测试集合中将只保留最后有数据的描述符。

    参数:nfds 描述符的上限值,一般是链接后描述符的最大值+1
               readfds 只读描述符集
              writefds 只写描述符集
              exceptfds 异常描述符集
      以上三个参数都是 fd_set * 的描述符集合类型
             timeout  检测超时 如果是NULL表示一直检测不超时

    返回值:超时  0
                  失败 -1
                  成功 >0

        为了配合select函数执行,有如下宏函数:
        void FD_CLR(int fd, fd_set *set);
        功能:将指定的set集合中编号为fd的描述符号删除

        int  FD_ISSET(int fd, fd_set *set);
        功能:判断值为fd的描述符是否在set集合中,如果则返回,否则返回假。

        void FD_SET(int fd, fd_set *set);
        功能:指定的fd描述符添加到set集合中

        void FD_ZERO(fd_set *set);
        功能:指定的set集合所有描述符删除。 

2.超时版

3.epoll

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值