非阻塞IO

非阻塞IO

fcntl

一个文件描述符, 默认都是阻塞IO。fcntl可以将某个文件描述符设置为非阻塞IO,先看一下文档介绍。

非阻塞IO_第1张图片

传入的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)。

我们此处只是用第三种功能, 获取/设置文件状态标记, 就可以将一个文件描述符设置为非阻塞。

实现函数SetNonBlock

基于fcntl函数,我们实现一个SetNonBlock函数,将文件描述符设置为非阻塞。

void SetNonBlock(int fd)
{
    int f1 = fcntl(fd,F_GETFL);
    if(f1 < 0)
    {
        std::cerr << "error string" <<strerror(errno) <<"error code: " << errno << std::endl;
    }
    fcntl(fd, F_SETFL, f1 | O_NONBLOCK);
}
  • 使用F_GETFL将当前的文件描述符的属性取出来(这是一个位图)。
  • 然后再使用F_SETFL将文件描述符设置回去。设置回去的同时,加上一个O_NONBLOCK参数

实现:

轮询方式读取标准输入,同时还可以执行其他任务。

#include 
#include 
#include 
#include 
#include 
#include 
#include 

void PrintLog()
{
    std::cout << "这是一个打印日志的例程" << std::endl;
}
void OperMySQL()
{
    std::cout << "这是一个操作数据库的例程" << std::endl;
}
void CheckNet()
{
    std::cout << "这是一个检测网络的例程" << std::endl;
}

using func_t = std::function<void (void)>;
std::vector<func_t> funcs;

void LoadTask()
{
    funcs.push_back(PrintLog);
    funcs.push_back(OperMySQL);
    funcs.push_back(CheckNet);
}

void SetNonBlock(int fd)
{
    int f1 = fcntl(fd,F_GETFL);
    if(f1 < 0)
    {
        std::cerr << "error string" <<strerror(errno) <<"error code: " << errno << std::endl;
    }
    fcntl(fd, F_SETFL, f1 | O_NONBLOCK);
}

void HandlerAllTask()
{
    for(const auto& func : funcs)
    {
        func();
    }
}

int main()
{ 
    char buffer[128];
    SetNonBlock(0);
    LoadTask();
    while(true)
    {
        printf(">> ");
        fflush(stdout);
        ssize_t n = read(0, buffer, sizeof(buffer)-1);// 阻塞在这里,等+拷贝
        // 1. 读取成功
        if(n > 0)
        {
            buffer[n-1] = 0;
            std::cout << "echo # " << buffer << std::endl;
        }
        // 2. 读取结束
        else if(n == 0)
        {
            std::cout << "end file" << std::endl;
            break;
        }
        // 3. 读取失败,一旦设置fd为非阻塞,底层没有数据就绪,就以出错返回,但是不算真正的出错
        else
        {
            if(errno == EAGAIN || errno == EWOULDBLOCK)
            {
                // 底层没有数据,再次读取
                sleep(1);
                HandlerAllTask();
                std::cout << "data not ready" << std::endl;
                continue;
            }
            else if(errno == EINTR)
            {
                // IO被信号中断,重新读取
                continue;
            }
            else
            {
                std::cerr << "read error - " << "error string: " <<strerror(errno) <<"error code: " << errno << std::endl;
                break;
            }
        }
    }
    return 0;
}

你可能感兴趣的:(Linux,知识,c++,开发语言,linux)