高级IO

几种IO模型优缺点:

阻塞型:当资源临时不可获得时,调用者进程阻塞等待。节省系统资源,运行效率低。

非阻塞型(轮询):当资源不可获得时,系统调用出错返回,影响浪费系统资源,运行效率高。

多路IO复用型:既节省系统资源,服务效率又高

异步IO:
                信号驱动IO:在不干扰主进程运行的情况下实现异步访问IO;
                异步IO:类似于信号驱动IO,依赖底层驱动。
信号驱动I/O和异步I/O的主要区别在于,信号驱动I/O是由内核通知我们何时可以启动一个I/O操作,此时数据仍在内核空间。而异步I/O有内核通知我们IO操作何时完成,此时数据已经拷贝到了我们的用户空间。

上述五种不同I/O模型的比较:前四种I/O模型主要区别在于第一阶段,有的阻塞,有的不阻塞。第二阶段基本相同:在数据从内核空间拷贝到调用者的用户空间时,进程阻塞与读操作。而异步IO的两个阶段都没有阻塞,在数据拷贝完成后,才会让内核通知我们。

一、非阻塞IO





二、多路复用IO

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
功能:实现IO多路转接,先构造一张存有多个描述符的表,然后这个函数阻塞,并且指示内核等待当多个描述中的任意一个准备好时,该函数才返回。
参数:
nfds:要检查的描述符数量,其值为三个描述符表中最大描述符编号的值。然后再加1.
readfds:在调用函数以前,将此值设置为要让内核测试 读是否准备好的描述符集。当函数返回时,这个值已经改变,用于指示哪些描述符字已经准备好读。可以用FD_ISSET来测试。
writefds: 在调用函数以前,将此值设置为 要让内核测试 写是否准备好的描述符集。 当函数返回时,这个值已经改变,用于指示哪些描述符字已经准备好写。可以用FD_ISSET来测试。
 exceptfds 在调用函数以前,将此值设置为 要让内核测试异常条件的描述符集。 当函数返回时,这个值已经改变,用于指示哪些描述符字异常。可以用FD_ISSET来测试。
                    timeout:要等待的时间
                              struct    timeval
  {
                                    long  tv_sec;   表示要等待的秒数
                                    long  tv_usec;  表示要等待的微秒数
                                 } ; 
timeout==NULL时,此函数永远等待,只有当指定描述符中的任意一个准备好或捕捉到一个信号时才返回。如果捕捉到一个信号。select返回-1
timeout == 0时,完全不等待,立即返回。    
timeout >0时,等待指定的秒数或微秒数。当指定时间已经超时时立即返回,如果超时时还没有一个描述符准备好,则返回值是0.
 
返回值:
  • 返回值-1表示出错。例如在所指定的描述符都没有准备好时捕捉到了一个信号,此时出错返回-1,这种情况下,不修改其中任何描述符集。
  • 返回值0表示所有描述符都没有准备好,而且已经超时。此时,所有描述符集都被清为0.
  • 返回值正值表示已经准备好的描述符个数,是这三个描述符集中已准备好的描述符之和。
注:
  • 有三个描述符集,readfds、writefds、exceptfds,每个描述符集存放在fd_set数据类型中,这种数据类型中的每一位对应一个描述符。所以内核每次检查对该类型中的每一位(也就是每个描述符)进行检查,来判断当前有没有准备好的描述符。所以nfds实质上就是定义了一个内核要检查的范围(也就是要检查的描述符数量)。因为描述符时从0开始,所以nfds为描述符表中的最大描述符编号加一。
  • 描述符在什么条件下准备好
对于普通文件描述符基本不会阻塞,但涉及到进程间或网络间通信时,便会阻塞,这时必须引起对select返回“准备好”的条件说得明确些
            1、下列四个条件任何一个满足时,套接字准备好读:
                    a、可接收缓冲区的数据字节数大于0时,即有可读的数据时,将不阻塞。对于套接口而言,可以使用套接口选项SO_RCVLOWAT来设置一个低潮限度,当接收数据缓冲区的字节数大于等于此低潮限度时,套接口读操作将不阻塞并返回。
                    b、在面向连接的通信中,当连接到读端的写端关闭时,读操作将不阻塞且返回0. UDP不是面向连接的,所以不会返回。
                    c、当读操作出现错误时,这时读操作将不阻塞且返回错误。
                    d、调用accept,套接口是一个监听套接口且还有未完成的连接。
            2、下列三个条件任何一个满足时。套接口准备好写:
                    a、发送缓冲区仍有可用空间时,将不阻塞。 对于套接口而言,可以使用套接口选项SO_SNDLOWAT来设置一个低潮限度,当发送数据缓冲区的字节数小于等于此低潮限度时,套接口读操作将不阻塞并返回。实际上UDP套接口没有发送缓冲区,内核只是拷贝应用进程数据并将其向协议栈的下层传递。因此一个阻塞UDP套接口上的输出操作不会阻塞。
                    b、在面向连接的通信中,当连接到写端的读端关闭时,写操作将产生信号SIGPIPE,终止进程。
                    c、当写操作出现错误时,这时读操作将出错返回。
            3、如果一个套接口存在带外数据或者仍处于带外标记,那它有异常条件待 处理
            注意当一个套接口出错时,它被select标记为即可读又可写。

            通过调用以下四个宏函数来处理fd_set描述符集:
                  void FD_CLR(int fd, fd_set *set);         将描述符fd从描述符集中清除。若fd在描述符集中则返回非0值,否则返回0
                  void FD_SET(int fd, fd_set *set);           将描述符fd添加到描述符集中
                  int  FD_ISSET(int fd, fd_set *set);        测试描述符fd是否被置位
                   void FD_ZERO(fd_set *set);                 将描述符集清0


int pselect(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, const struct timespec *timeout,const sigset_t *sigmask);
除以下几点外,此函数和select相同:
  • select的超时用timeval结构指定,但pselect使用timespec结构,timespec结构以秒和纳秒表示超时,而非微妙和秒。
  • pselect的超时声明为const,保证了调用pselect不会改变此值
  • 对于pselect可使用信号屏蔽字,若sigmask为空,那么在信号有关方面,pselect的运行状况和select相同。否则,sigmask指向一个信号屏蔽字,在调用pselect时,以原子方式安装该信号屏蔽字。在返回时恢复以前的信号屏蔽字。


int poll(struct pollfd *fds, nfds_t nfds, int timeout);
功能:
实现IO多路转接, poll函数用于监测多个等待事件,若事件未发生,进程睡眠,放弃CPU控制权,若监测的任何一个事件发生,poll将唤醒睡眠的进程,并判断是什么等待事件发生,执行相应的操作。poll函数退出后,struct pollfd变量的所有值被清零,需要重新设置。
          参数:
                       fds: 指定了一个被监视的文件描述符,一般设置为一个结构体数组,指向结构体数组的第一个元素的指针。
                         struct  pollfd  {  
          int         fd ;           /* file descriptor */  描述符编号
          short     events ;   /* requested events to watch */  要关心的描述符状态,其值详细见下图。
                    short     revents ;   /* returned events witnessed */  返回时,内核设置revents,以说明描述符发生了什么
     }   ;
                      nfds:说明了结构体数组中的个数,即描述符个数。
                      timeout:
        当 timeout==-1 时,此函数永远等待,只有当指定描述符中的任意一个准备好或捕捉到一个信号时才返回。如果捕捉到一个信号。select返回-1
                  当timeout == 0时,完全不等待,立即返回。    
        当timeout >0时,等待指定的毫秒。当指定时间已经超时时立即返回,如果超时时还没有一个描述符准备好,则返回值是0.
返回值 :
  • 返回值-1表示出错。
  • 返回值0表示所有描述符都没有准备好,而且已经超时。此时,所有描述符集都被清为0.
  • 返回值正值表示已经准备好的描述符个数
  
注意,第三部分的三个常值在events中是不能设置的,但是当相应条件存在时就在revents中返回。



三、异步IO

使用异步I/O(SIGIO)需要进程执行以下三个步骤:
1、给SIGIO信号注册处理函数。
2、设置要接受SIGIO信号的进程ID或进程组ID。以命令F_SETOWN调用fcntl函数来设置进程或进程组ID。
3、激活套接口的信号驱动

注:
对于管道文件和普通文件等用OPEN打开的文件,当以只读方式打开时,对描述符执行写操作会引起SIGIO信号。当以只写方式打开时,对描述符执行读操作,会引起SIGIO信号。(读写操作必须已经完成,即数据已经全部被读取到内核空间或者从内核空间将数据全部取出,才会发生信号)

UDP套接口上的SIGIO信号,当下述事件发生时产生SIGIO信号:
  • 数据包到达套接口
  • 套接口上发生异步错误

TCP套接口上的SIGIO信号,当下述事件发生时产生SIGIO信号:
  • 信号驱动对TCP套接口几乎时没用的,原因是该信号产生过于频繁,并且该信号并没有告诉我们发生了什么事件。下列条件均可在TCP套接口上产生SIGIO信号。
  • 在监听套接口上有一个连接请求已经完成
  • 发起来一个连接拆除请求
  • 一个连接请求拆除请求已经完成
  • 一个连接的一半已经关闭
  • 数据到达了套接口
  • 数据已从套接口上发出(即输出缓冲区有空闲空间)
  • 发生了一个异步错误


















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值