fcntl函数
一个文件描述符, 默认都是阻塞IO,通过fcntl把阻塞文件描述符改为非阻塞
传入的cmd的值不同, 后面追加的参数也不相同
fcntl函数有5种功能:
- 复制一个现有的描述符(cmd=F_DUPFD).
- 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
- 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
- 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
- 获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).
我们此处只是用第三种功能, 获取/设置文件状态标记, 就可以将一个文件描述符设置为非阻塞.
实现函数SetNoBlock
设置文件描述符为非阻塞,固定写法就是下面的写法
void SetNonBlock(int fd)
{
int fl = fcntl(fd, F_GETFL); //获取文件状态
if (fl < 0)
{
perror("fcntl");
return;
}
fcntl(fd, F_SETFL, fl | O_NONBLOCK); //设置文件状态为非阻塞
}
- 使用F_GETFL将当前的文件描述符的属性取出来(这是一个位图)
- 然后再使用F_SETFL将文件描述符设置回去. 设置回去的同时, 加上一个O_NONBLOCK参数
其实还有另一种做法,就是在open打开文件的时候,就可以设置文件描述符为非阻塞,一般是上面的写法
实例
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <cstdio>
#include <cerrno>
#include <cstring>
using namespace std;
void SetNonBlock(int fd)
{
int fl = fcntl(fd, F_GETFL); //获取文件状态
if (fl < 0)
{
perror("fcntl");
return;
}
fcntl(fd, F_SETFL, fl | O_NONBLOCK); //设置文件状态为非阻塞
cout << " set " << fd << " nonblock done" << endl;
}
int main()
{
char buffer[1024];
SetNonBlock(0);
sleep(1);
while (true)
{
printf("Please Enter# ");
fflush(stdout);
ssize_t n = read(0, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n - 1] = 0;
cout << "echo : " << buffer << endl;
}
else if (n == 0)
{
cout << "read done" << endl;
break;
}
else
{
// 1. 设置成为非阻塞,如果底层fd数据没有就绪,recv/read/write/send, 返回值会以出错的形式返回
// 2. a. 真的出错 b. 底层没有就绪
// 3. 我怎么区分呢?通过errno区分!!!
if (errno == EWOULDBLOCK) //出错码是EWOULDBLOCK,就是底层没有就绪,不是真的出错
{
cout << "0 fd data not ready, try again!" << endl;
// do_other_thing(); //非阻塞返回后进程可以做别的事情,不用阻塞在那
sleep(1);
}
else //真的出错
{
cerr << "read error, n = " << n << "errno code: "
<< errno << ", error str: " << strerror(errno) << endl;
}
}
}
return 0;
}
注意:
1. 设置成为非阻塞,如果底层fd数据没有就绪,recv/read/write/send, 返回值会以出错的形式返回
2. a. 真的出错 b. 底层没有就绪
3. 我怎么区分呢?通过errno区分!!!errno是 EWOULDBLOCK代表底层资源没有就绪,不是真的出错