Download presentation
Presentation is loading. Please wait.
Published by温 冉 Modified 8年之前
1
网络应用程序设计 - unit 07 I/O 模型
2
2 主要内容 I/O 模型 阻塞式 I/O 模型 非阻塞式 I/O 模型 输入输出多路复用 I/O 模型 信号驱动 I/O 模型
3
3 I/O 模型 Linux 系统 I/O 模型 阻塞式 I/O 模型-默认 I/O 模型 非阻塞式 I/O 模型 多路复用 I/O 模型 信号驱动 I/O 模型
4
4 阻塞式 I/O 模型 产生阻塞的原因 linux 进程调度算法-时间片调度算法 每个进程占用 CPU 一个时间片后被挂起 当前运行进程如果需要等待其他系统资源,且 不是非阻塞方式运行,将进入等待状态
5
5 阻塞式 I/O 模型 产生阻塞的函数-读操作 read 、 readv 、 recv 、 recvfrom 和 recvmsg 接收缓冲区无数据 拷贝数据 用户态核心态 read 数据到达 拷贝数据结束 read 返回 阻塞等待数据 状态 切换 状态 切换 TCP 协议以字节为单位, 只要接收缓冲区中出现数据, 进程被唤醒 UDP 协议以数据报为单位, 当完整的数据报到达时,进 程被唤醒 阻塞过程中,对方主机崩 溃,将永远阻塞
6
6 阻塞式 I/O 模型 产生阻塞的函数-写操作 write 、 writev 、 send 、 sendto 和 sendmsg 阻塞等待回 收数据缓冲区 发送缓冲区空间不足 拷贝数据 用户态核心态 write 得到数据确认, 空间足够 拷贝数据结束 write 返回 状态 切换 状态 切换 写操作发生阻塞的几率 低于读操作 TCP 协议写操作在对方 主机崩溃不会永远阻塞, 但需要等待较长时间 UDP 协议写操作永远不 会阻塞
7
7 阻塞式 I/O 模型 产生阻塞的函数-建立连接 connect 启动 3 次握手操作 用户态核心态 connect 连接建立完成 connect 返回 等待握手 操作完成 状态 切换 状态 切换 客户机 TCP 协议接收到服务器 TCP 协议返回的对 SYN 数据段 的确认时,函数 connect 成功 返回 TCP 协议的连接操作至少需要 一个往返时间 UDP 的 connect 操作不产生 连接,因此不阻塞
8
8 阻塞式 I/O 模型 产生阻塞的函数- TCP 协议接受连接 accept 没有已完成连接 创建连接 socket 用户态 核心态 accept 连接建立完成 连接 socket 建立成功 accept 返回 阻塞等待连接 状态 切换 状态 切换
9
9 阻塞式 I/O 模型 阻塞式 I/O 模型的特点 结构简单容易同步 进程可能永远阻塞或 阻塞时间过长 阻塞时进程效率低 示例: server.cpp client.cpp 数据到达 socket1 读阻塞 读数据 socket2 读阻塞 数据到达 读数据
10
10 阻塞式 I/O 模型 阻塞式 I/O 模型的超时控制 调用 alarm 函数设置超时 超时到达时产生 SIGALARM 信号中断 I/O 函数阻塞,对于 4 种产生阻塞的函数 均有效 多次调用 alarm 时,产生的 SIGALARM 信号无法区分是哪一次超时引发的,无 法实现超时控制 示例: alarmio.cpp
11
11 阻塞式 I/O 模型 阻塞式 I/O 模型的超时控制 设置 socket 选项 设置 SO_RCVTIMEO 和 SO_SNDTIMEO 选项 设置了这两个选项之后,所有的读写操作 可以保证在超时范围内返回 只需设置一次选项,对以后的读写操作均 有效 不适用于 accept 和 connect 示例: timeoutio.cpp
12
12 用户态 read read 返 回错误 read read 返回 接收缓冲 区无数据 数据到达 接收缓冲 区有数据 拷贝数 据完成 拷贝数据 核心态 状态 切换 状态 切换 状态 切换 状态 切换 非阻塞式 I/O 模型 非阻塞式 I/O 模型概述 可以设置 socket 为非阻 塞模式 在非阻塞模式 socket 上 进行 I/O 操作时,如果操 作不能完成,将以错误 返回 4 种 I/O 操作在非阻塞式 socket 下均不会阻塞
13
13 非阻塞式 I/O 模型 设置 socket 为非阻塞方式 函数 fcntl int flags; flag=fcntl(sockfd,F_GETFL,0); fcntl(sockfd,F_SETFL,flag|O_NONBLOCK); 函数 ioctl int on=1; ioctl(sockfd,FIONBIO,&on);
14
14 非阻塞式 I/O 模型 非阻塞式 I/O 模型对 4 种 I/O 操作返回的错误 读操作 接收缓冲区无数据时返回 EWOULDBLOCK 写操作 发送缓冲区无空间时返回 EWOULDBLOCK 空间不够时部分拷贝,返回实际拷贝字节数 建立连接 启动 3 次握手,立刻返回错误 EINPROGRESS 服务器客户端在同一主机上 connect 立即返回 成功 接受连接 没有新连接返回 EWOULDBLOCK
15
15 非阻塞式 I/O 模型 检查操作是否可以完成的方式 轮询式 for(;;){ if(read(sockfd,buf,nbytes)<0){ if(errno==EWOULDBLOCK)continue; else{ printf(“read error\n”); break; } else break; }
16
16 非阻塞式 I/O 模型 检查操作是否可以完成的方式 select 函数检查 I/O 是否就绪 // 设置监测的描述符集合 … // 阻塞等待描述符就绪 select( rdset, wrset, exset, …); // 对就绪描述符进行相应操作(读,写, … ) …
17
17 非阻塞式 I/O 模型 非阻塞式 I/O 模型特点 优点 不会产生阻塞 输入方式效率比较 高 缺点 长时间占用 CPU 示例: server.cpp client_n.cpp 数据到达 socket1 读失败 读数据 socket2 读失败 读数据 数据到达
18
18 输入输出多路复用 I/O 模型 多路复用模型 用户态 select select 返回 没有 socket 满足条件 阻塞等待任何一个 socket 就绪 一个或几个 socket 就绪 设置 socket 描述符集合完毕 设置就绪 socket 描述符 核心态 状态 切换 状态 切换
19
19 输入输出多路复用 I/O 模型 socket 描述符就绪条件 读就绪条件 接收缓冲区中的数据量 ≥ 接收下限。 读通道被关闭,收到 FIN 字段 侦听 socket 的完成连接队列不为空 非阻塞式 socket 的 connect 操作过程中 出现错误 默认接收下限为 1 ( TCP 为 1 字节, UDP 为 1 个数据报), 可以用 SO_RCVLOWAT 修改默认值
20
20 输入输出多路复用 I/O 模型 socket 描述符就绪条件 写就绪条件 发送缓冲区中可用空间 ≥ 发送下限。 写通道被关闭。 非阻塞式 socket 的 connect 操作成功 TCP 默认接收下限为 2048 字节,可以用 SO_SNDLOWAT 修改默认值 UDP 协议没有实际的发送缓冲区,其发送缓冲区空间总 是大于发送下限,所以 UDP socket 总是写就绪
21
21 输入输出多路复用 I/O 模型 socket 描述符就绪条件 异常就绪条件(用于带外数据)
22
22 输入输出多路复用 I/O 模型 多路复用 I/O 模型特点 只检查一个 socket 描 述符时和阻塞式 I/O 模 型类似,只是阻塞的 位置不同,但效率低 于阻塞式 I/O 模型 在多个 socket 描述符 上进行 I/O 操作时效率 高于阻塞式 I/O 示例 :client_m.cpp socket1 读第一个 socket 数据 socket2 读第二个 socket 数据 阻塞等待 socket 就绪
23
23 信号驱动 I/O 模型 信号驱动通信过程 用户态 设置 SIGIO 处理函数 read 返回 没有数据 到达 拷贝数据 核心态 状态 切换 状态 切换 信号处理函数 read 数据到达发送信号 SIGIO 执行其 他任务 SIGIO
24
24 信号驱动 I/O 模型 信号驱动 I/O 模型 的主要步骤 1. 设置 SIGIO 信号 处理函数 2. 设置 socket 描述 符所有者 3. 允许这个 socket 进行信号驱动 I/O void sigio_handler(int signo){ … } int main(){ int sockfd; int on=1; … signal(SIGIO,sigio_handler); fcntl(sockfd,F_SETOWN,getpid()); ioctl(sockfd,FIOASYNC,&on); … }
25
25 信号驱动 I/O 模型 信号驱动 I/O 模型特点 等待 I/O 操作可以进行的过程中不用阻塞, 可以执行其他操作 程序结构简单 更适用于 UDP 协议 TCP 协议在很多环节上会产生 SIGIO 信号,难 以区分产生信号的原因 UDP 只在收到数据包或错误时产生 SIGIO 信号 示例: sig_server.cpp,udpclient.cpp
Similar presentations