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模型的主要内容,如果未能解决你的问题,请参考以下文章