UNP卷一学习笔记:I/O模型

Posted printfnothing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UNP卷一学习笔记:I/O模型相关的知识,希望对你有一定的参考价值。

UNIX下可用的5种I/0模型:

(1)阻塞式I/O   (2)非阻塞式I/O   (3)I/O复用   (4)信号驱动I/O(SIGIO)    (5)异步I/O

前4种属于同步操作,第5种属于异步操作。

下面以钓鱼(套接字输入操作)为例说明这5种模型:

                         

完成钓鱼需要两步骤:

完成套接字输入需要两步骤:

a.等待鱼上钩   

a.等待数据准备好

b.钓起鱼放入桶内 

b.从内核向进程复制数据

                 

(1)阻塞式I/O                              

小明拿着鱼竿等待鱼上钩,上钩前小明除了一直盯着浮标,不做任何事,鱼上钩后将鱼钓起放入桶内。

(2)非阻塞I/O

小明拿着鱼竿等待鱼上钩,上钩前小明低着头玩手机,每隔一段时间抬头看浮标,若鱼上钩就将其钓起放入桶内,若没有则又低头玩手机,每隔一段时间抬头看浮标,直到鱼上钩。

(3)I/O复用

小明嫌一根鱼竿钓鱼不过瘾,于是带了好几根鱼竿去钓鱼。他将这些鱼竿的线都抛出,并用工具固定好鱼竿,然后等着鱼上钩,一有鱼竿鱼上钩,就钓起鱼放入桶内。

(4)信号驱动式I/O

小明带了女友小红一块去钓鱼,他让小红守着鱼竿而且一有鱼就叫他,自己却跑树下玩手机,鱼上钩后,小红叫了小明,小明过去钓起鱼放入桶内。

(5)异步I/O

这次小明为了锻炼小红的技术,让小红自己一人守着鱼竿,鱼上钩了也让小红自己钓起放入桶内,小明自己一直坐旁边玩手机。


select函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒它。

#include<sys/select.h>
#include<sys/time.h>
int select(int maxfdp1,fd_set*readset,fd_set* writeset,fd_set* exceptset,\\
const struct timeval*timeout);
//返回:若有就绪描述符则为其数目,若超时则为0,若出错则为-1

struct timeval
  long tv_sec;     //秒
  long tv_usec;    //微妙


timeout设置:

a.永远等带下去:仅在任意一个描述符准备好I/O时才返回。

b.等待一段固定时间:在任意一个描述符准备I/O时返回,但是不能超过由该参数指定的时间。

c.根本不等待:检查描述符后立即返回,这称为轮询。timeout中的值均设为0。

fd_set是一个描述符集,通常是一个整数数组,其中每个整数中的每一位对应一个描述符。参数readset、writeset、excepetset分别是让内核测试的读、写和异常条件描述符。描述符打开状态置1,关闭状态置0。select返回后,打开的描述符状态会变为0。

参数maxfdp1指定待测试的描述符个数,描述符从0开始,最大描述数量FD_SETSIZE一般为1024。

描述符就绪条件:

条件

可读

可写

异常

有数据可读(read,recv)

发送方发送FIN

监听套接字准备好新连接(accept)

 

 

 

 

 

 

有空间可写(write,send)

写进程关闭

 

 

 

 

套接字产生待处理错误

 

TCP带外数据

 

 


select函数的缺点:

(1)select的最大描述符数受限制,默认为FD_SETSIZE=1024,对于高并发的服务器来说这个数量明显不够,如果想要修改这个上限,不得不对整个unp相关的文件进行重新编译。

(2)在调用select函数时,需要把三个读、写、异常的fd_set集合往内核拷贝,这会引起很大的开销。

(3)在内核中select函数需要不断轮询所有的fd_set集中的描述符,以此来确定就绪的描述符,这也会引起很大开销。

(在同一时刻,如果只有很少一部分描述符就绪,select函数的效率就会因为整个描述符的数量增加而线性下降)

epoll系列函数:

int epoll_create(int size);//设置可以管理的描述符数量上限,并返回管理它们的描述符
int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);
//为epfd中的描述符fd编辑(op:注册,修改,删除)要内核监听的事件event
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
//等待事件的产生,类似于select()调用。成功返回:就绪的事件数目,超时返回0

相比于select函数, epoll系列函数的优点

(1)允许用户自己设置描述符数目的上限,而且这个上限可以远远大于FD_SETSIZE。

(2)epoll_wait函数不需要去轮询所有监听的描述符,当某一描述符就绪时,内核会通过回调机制,将就绪的描述符插入队列中,epoll_wait函数只需判断该队列是否为空就行,不用遍历整个描述符集合,这样大大减少了系统开销。

(3)无论是select函数还是epoll都需要将描述符集合从用户空间拷贝到内核中,等描述符就绪又要将事件从内核空间拷贝到用户空间,epoll采用了mmap加速内核与用户空间的消息传递,mmap是内核与用户空间共用的一块内存,因此可以避免不必要的内存拷贝。

shutdown函数:

#include<sys/socket.h>
int shutdown(int sockfd,int howto);
//返回:若成功返回0,出错则返回-1;

howto参数:

a.SHUT_RD(0):关闭连接的读这一半——套接字中不再有数据可接收,而且套接字接收缓冲区中的现有数据都被丢弃。

b.SHUT_WR(1):关闭连接的这一半——当前留在套接字发送缓冲区中的数据都被发送掉,后跟TCP的正常连接终止序列。

c.SHUT_RDWR(2):关闭的连接的读写两半——等价于第一次调用SHUT_RD,第二次调用SHUT_WR。

shutdown 与 close的区别:

(1)close把描述符引用计数减一,仅在该计数变为0时才关闭套接字;shutdown可以不管引用计数就激发TCP的正常连接终止序列。

(2)close终止TCP双方的读写数据传送(四次挥手),shutdown允许只关闭一方。

以上是关于UNP卷一学习笔记:I/O模型的主要内容,如果未能解决你的问题,请参考以下文章

UNP卷一学习笔记:高级I/O函数

UNP卷一学习笔记:高级I/O函数

UNP卷一学习笔记:TCP状态

UNP卷一学习笔记:基本TCP套接字

UNP卷一学习笔记:POSIX信号处理

UNP卷一学习笔记:POSIX信号处理