C语言-IO模型
标签:关心 创建 拷贝 darwin 情况下 轮询 read 系统 语言
IO模型
在UNIX/Linux下主要有4种I/O模型
- 阻塞I/O(最常用)
- 非阻塞I/O(可防止进程阻塞在I/O操作上,需要轮询)
- I/O多路复用(允许同时对多个I/O进行控制)
- 信号驱动I/O(一种异步通讯模型)
阻塞I/O模式
- 阻塞I/O模式是最普遍使用的I/O模式,大部分程序使用的都是阻塞模式的I/O
- 缺省情况下,套接字建立后所处于的模式就是阻塞I/O模式
- 很多读写函数在调用过程中会发生阻塞
- 读操作-read、recv、recvfrom
- 写操作-write、send
- 其他操作-accept、connect
读阻塞-这里以read函数为例
- 进程调用read函数从套接字上读取数据,当套接字的接受缓冲区中还没有数据可读,函数read将会发生阻塞
- 它会一直阻塞下去,等待套接字的接受缓冲区中有数据可读
- 经过一段时间后,缓冲区内接受到数据,于是内核便去唤醒该进程,通过read访问这些数据
- 如果在进程阻塞的过程中,对方发生故障,那这个进程将永远阻塞下去
写阻塞
- 在写操作时发生阻塞的情况要比读操作少,主要发生要写入的缓冲区的大小小于要写入的数据量的情况下
- 这时,写操作不进行任何拷贝工作,将发生阻塞
- 一旦发送缓冲区内有足够的空间,内核将唤醒进程,将数据从用户缓冲区中拷贝到相应的发送数据缓冲区
- UDP不用等待确认,没有实际的发送缓冲区,所以UDP协议中不存在发送缓冲区慢的情况;也就是说UDP套接字上执行的写操作永远不会阻塞
非阻塞I/O模型
- 当我们将一个套接字设置为非阻塞模式,我们相当于告诉了系统内核:“当我请求的I/O操作不能够马上完成,你想让我的进程进行休眠等待的时候,不要这么做,请马上返回一个错误给我
- 当一个应用程序使用了非阻塞的套接字,它需要使用一个循环来不停地测试是否一个文件描述符有数据可读(称作polling)
- 应用程序不停的polling 内核来检查是否I/O操作已经就绪。这是一个极浪费CPU资源的操作
- 这种模式使用中不普遍
非阻塞模式的实现-fcntl() 函数
- 当你一开始建立一个套接字描述符的时候,系统内核将其设置为阻塞I/O模式
- 可以使用fcntl()设置一个套接字的标志位O_NONBLOCK 来实现非阻塞
- 代码实现
#include int flag = 0;
// 获取到当前的设置
flag = fcntl(sockfd,F_SETFL,0);
flag |=O_NONBLOCK;
// 设置回去
fcntl(sockfd,F_SETFL,flag);
或者使用 ioctl()函数
#include int b_on = 1;
ioctl(sockfd, FIONBIO, &b_on);
多路复用
- 应用程序中同时处理多路输入输出流,若采用阻塞模式,将得不到预期的目的
- 若采用非阻塞模式,对多个输入输出进行轮询,但又太浪费CPU时间
- 若设置多进程,分别处理一条数据通路,将新产生进程的同步于通讯问题,使程序变得更加复杂
- 比较好的方式是使用I/O多路复用,基本思想是
- 先构造一张有关描述符的表,然后调用一个函数。当这些文件描述符中的一个或多个以及准备好进行I/O时函数才返回。
- 函数返回时告诉进程那个描述符已就绪,可以进行I/O操作
#include select.h>
int fd_num = -1; // 一般使用一个计数器计算集合中的文件描述符数量
void FD_ZERO(fd_set *fdset); // 将集合清零
void FD_SET(int fd, fd_set *fdset); // 将关心的文件描述符加入到集合中
void FD_CLR(int fd, fd_set *fdset); // 将某个文件描述符从集合中清除
int FD_ISSET(int fd, fd_set *fdset); // 判断fd是否在set集合中
int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds,
fd_set *restrict errorfds, struct timeval *restrict timeout);
// nfds 传入 fd_num+1 表示最大集合数
// 这三个集合如果需要传入 不需要则可以传NULL
// readfds 读集合
// writefds 写集合
// errorfds 异常集合
// timeout 超时等待
// struct timeval
/*
_STRUCT_TIMEVAL
{
__darwin_time_t tv_sec; /* seconds */ 秒
__darwin_suseconds_t tv_usec; /* and microseconds */ 毫秒
};
*/
Demo:多路复用模型
#include select.h>
#include int main(int argc, char *argv[]) {
fd_set rset; // 创建读集合
int max = -1;
int fd = -1; // 句柄
struct timeval timeout;
// socket 连接省略...
// bind 省略...
// listen 省略...
// 如果循环是要一直执行如下操作
FD_ZERO(&rset); // 将集合清零
FD_SET(fd, &rset); // 将创建好的fd加入到读集合中
max++; // 计数器+1
// 依次将其他连接的fd加入... 省略
// 设置超时时间 为5s
timeout.tv_sec = 5;
timeout.tv_usec = 0;
// 使用select监控
select(max + 1, &rset, NULL, NULL, &timeout);
// 依次判断select监控后的rset
// 例如:fd
// 现在的rset集合中存放的是已经就绪的描述符 所以需要依次判断 手上的描述符是否存在于集合中
if (FD_ISSET(fd, &rset)) {
// 已经准备就绪 做一些该做的操作
}
// if (FD_ISSET(fd2,&rset)){
// // 已经准备就绪 做一些该做的操作
// }
// ...
}
C语言-IO模型
标签:关心 创建 拷贝 darwin 情况下 轮询 read 系统 语言
原文地址:https://www.cnblogs.com/binHome/p/12864446.html
文章来自:
搜素材网的
编程语言模块,转载请注明文章出处。
文章标题:
C语言-IO模型
文章链接:http://soscw.com/index.php/essay/46457.html
评论