首先先来看一个阻塞式IO的例子
#include <iostream>
#include <cstdio>
#include <string>
#include <cerrno>
#include <unistd.h>
#include <fcntl.h>
int main()
{
char buffer[1024];
while (true)
{
int n = read(0, buffer, sizeof(buffer));
if (n > 0)
{
buffer[n] = 0;
std::cout << "echo# " << buffer << std::endl;
}
else
{
cout << "read error"<< endl;
}
}
}
注意看上面的代码,我们通过系统调用接口read,从标准输入文件键盘中读取信息。这段代码运行起来之后,如果我们从键盘中输入“aabbcc”,然后按回车键返回,程序就会在屏幕中输出"echo# aabbcc",但是如果我们什么都不输入,进程就会一直在那里卡着,等待我们输入信息。这就是典型的阻塞式IO
下面我们对这段代码进行一点儿小改动
#include <iostream>
#include <cstdio>
#include <string>
#include <cerrno>
#include <unistd.h>
#include <fcntl.h>
void SetNonBlock(int fd)
{
// 获取fd对应文件的状态标志
int fl = fcntl(fd, F_GETFL);
// fcntl 是系统调用,用于操作文件描述符的各种属性
// F_GETFL 是命令参数,表示 “获取文件状态标志”
// 返回值 fl 是一个整数,包含了该文件描述符当前的所有状态标志(如读写模式、阻塞状态等)
if (fl < 0)
{
perror("fcntl");
return;
}
// 将fd指向文件的IO方式设置为非阻塞模式
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
// F_SETFL 是命令参数,表示 “设置文件状态标志”
// fl | O_NONBLOCK 表示 “保留原有的状态标志,同时添加 O_NONBLOCK 标志”
// O_NONBLOCK 是关键标志,它将文件描述符设置为非阻塞模式
}
int main()
{
std::string tips = "Please Enter# ";
char buffer[1024];
// 将fd=0对应文件的IO方式设置为非阻塞模式
SetNonBlock(0);
while (true)
{
write(0, tips.c_str(), tips.size());
int n = read(0, buffer, sizeof(buffer));
if (n > 0)
{
buffer[n] = 0; // 将字符串的最后一个字符置为'\0',方便输出
std::cout << "echo# " << buffer << std::endl;
}
else
{
cout << "read error"<< n << endl;
}
sleep(1);
}
}
之后我们将这段代码运行起来,我们啥也不输入。运行的结果就是下面这样的

然后我们中间键盘输入几个数据,然后按一下回车,结果如下

到这里我们就差不多能理解非阻塞IO的意思了。我现在有一个问题。就是为什么当键盘的输入缓冲区中没有数据时,read系统调用的返回值是-1呢?我记得之前描述里面说文件读取失败的时候,才会返回-1啊, 你这现在底层数据没就绪,从某种角度上说不算失败啊。失败的情况你比如说指定的文件不存在或者是文件格式出错,像这样的情况,read系统调用也是会返回-1的。那假如说我最后拿到的read的返回值是-1。我怎么能够确定是因为什么原因导致文件读取失败的呢?
答案是看errno!errno 中存储的是最近一次系统调用或库函数执行失败时设置的错误状态码,我们可以通过errno 来判断出错的原因具体是什么

下面我们就引入errno,来看看read返回-1的原因到底是啥
#include <iostream>
#include <cstdio>
#include <string>
#include <cerrno>
#include <unistd.h>
#include <fcntl.h>
void SetNonBlock(int fd)
{
int fl = fcntl(fd, F_GETFL);
if (fl < 0)
{
perror("fcntl");
return;
}
fcntl(fd, F_SETFL, fl | O_NONBLOCK); // O_NONBLOCK:让该fd,以非阻塞方式进行工作
}
int main()
{
std::string tips = "Please Enter# ";
char buffer[1024];
// 将fd=0对应文件的IO方式设置为非阻塞模式
SetNonBlock(0);
while (true)
{
write(0, tips.c_str(), tips.size());
// 非阻塞,如果我们不做输入,数据不就绪,以出错形式返回!!
// read不是有读取失败(-1)吗?失败vs底层数据没就绪 -> 底层数据没就绪,不算失败
// 如果是-1, 失败vs底层数据没就绪我们后续的做法是不同的!
// read->-1,失败vs底层数据没就绪->需要区分的必要性的!
// errno表示:更详细的出错原因,最近一次调用,出错的时候的出错码
int n = read(0, buffer, sizeof(buffer));
if (n > 0)
{
buffer[n] = 0;
std::cout << "echo# " << buffer << std::endl;
}
else if (n == 0)
{
std::cout << "read file end!" << std::endl;
break;
}
else
{
// EAGAIN 11 /* Try again */
// EWOULDBLOCK EAGAIN /* Operation would block */
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
// 做其他事情呢?
std::cout << "底层数据,没有就绪" << std::endl;
sleep(1);
continue;
}
else if (errno == EINTR)
{
std::cout << "被中断,从新来" << std::endl;
sleep(1);
continue;
}
else
{
std::cout << "read error: " << n << ", errno: " << errno << std::endl;
}
}
}
}
通过检测我们就可以进一步看到。系统调用返回值为-1的原因是底层数据没有就绪,接收缓冲区中没数据

总结

1091

被折叠的 条评论
为什么被折叠?



