[linux] Five IO models and non-blocking IO

1. The concept of IO

We said earlier that IO is actually copying data.
Let me talk about the read interface first:

When the system calls, read/recvthere will be two situations
① No data, blocking and waiting.
② If there is data, read/recv will return after the copy is completed.

The essence of blocking is to wait for resources (buffers) to be ready. And writing data also needs to wait (send buffer is full).

It follows that IO is not just copying data:
IO = waiting for resources to be ready + copying data

The low IO efficiency we are talking about is not the low efficiency of copying, but the long waiting time.
So there is a concept called efficient IO , and its essence is to reduce the waiting time ( the proportion of waiting ).

Two, five models of IO

2.1 Concept

Let me give you an example:

There are a few people fishing now:
Zhang San has been staring at the swim bladder after hooking, doing nothing, waiting for the fish to take the bait.
After Li Si ticked off, he would read a book, look at his swim bladder, and play with his mobile phone.
Wang Wu hung a bell on the fishing rod, and after the hook was hooked, he went about his own business. When the bell rang, he caught the fish without lifting his head.
Zhao Liu has a lot of fishing rods, and after hooking all of them, he keeps traversing to see if any fish are hooked.
Tian Qi does not fish by himself, let others fish, just notify Tian Qi after fishing, and Tian Qi gets the fish directly in the end.

As a bystander, we think that as long as a person waits less time, his fishing efficiency is high.
Judging from this, Zhao Liu's efficiency is the highest, because he has more fishing rods, the probability of the fish being hooked is higher, and the waiting time is less.

Compare the above scenario to a computer:

Zhang San: Blocking IO
Li Si: Non-blocking IO
Wang Wu: Signal-driven IO
Zhao Liu: Multiplexing/multiplexing
Tian Qi: Asynchronous IO
These few people are equivalent to processes , and the people hired by Tian Qi are operating systems , fish It is the data , the fish pond is the kernel space , the swim bladder is the data-ready event , the fishing rod is the file descriptor , and the whole action of fishing is the read/recv call .

  • Blocking IO : The system call will wait until the kernel prepares the data. All sockets are blocked by default.
  • Non-blocking IO : If the kernel has not prepared the data yet, the system call will still return directly, and return the EWOULDBLOCK error code.

Non-blocking IO often requires programmers to repeatedly try to read and write file descriptors in a cyclic manner. This process is called polling , which is a big waste of CPU and is generally only used in specific scenarios.

  • Signal-driven IO : When the kernel prepares data, it uses the SIGIO signal to notify the application to perform IO operations.
  • IO multiplexing : Although it looks similar to blocking IO, the core is that IO multiplexing can wait for the ready status of multiple file descriptors at the same time.

The process is blocked by the select call, and select is only responsible for waiting (no copy capability) . When the file descriptor is ready (when select returns), use other IO class interfaces to complete the copy.

  • Asynchronous IO : The kernel notifies the application program when the data copy is completed (and the signal driver tells the application program when it can start copying data).

In any IO process, there are two steps. The first is waiting , and the second is copying. And in actual application scenarios, the time consumed by waiting is often much higher than the time spent on copying. To make IO more efficient, the core The best way is to minimize the waiting time

2.2 Comparison of five IOs

  • Comparison of blocking IO, non-blocking IO, and signal-driven IO

Blocking IO, non-blocking IO, and signal-driven IO have no difference in IO efficiency (only one fishing rod).
What about other aspects? In other respects, non-blocking IO and signal-driven IO can do more.
The blocking IO and non-blocking IO "fishing" are the same, the difference is the way of waiting .

  • Comparison of blocking IO and non-blocking IO

Blocking IO When the data resource is not ready, the process will be placed in the waiting queue and suspended, and the result can only be returned after getting the result.
Instead of blocking IO, when the data resource is not ready, it will return directly (knowing that the data resource is not ready).

  • Does the signal drive IO wait?

Waiting, but the way of waiting is different.

  • Synchronous IO and asynchronous IO

In addition to asynchronous IO, other kinds of IO are the process of the process itself participating in IO (fishing + etc.), so it is called synchronous IO .
And because Tianqi does not participate in any stage of IO, it is called asynchronous IO .
The so-called synchronization means that when a call is made, the call does not return until the result is obtained. But once the call returns, the return value is obtained; in other words, the caller actively waits for the result of the call.
Asynchronous is the opposite. After the call is issued, the call returns directly, so there is no return result; in other words, when an asynchronous procedure call is issued, the caller will not get the result immediately; but after the call is issued, it is called The caller notifies the caller through status, notification, or handles the call through a callback function.

  • Why is multiplexing IO efficient?

Because the proportion of waiting is reduced .

3. Non-blocking IO

When opening a file, it is opened in a blocking manner by defaultO_NONBLOCK . If you want to open a file in a non-blocking manner, you need to carry the or option when using the open function to open the file O_NDELAY. At this time, you can open the file in a non-blocking manner.

3.1 fcntl file descriptor control

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );

The function of the fcntl function is to control the operation of the file descriptor. It can implement functions such as file locking, non-blocking I/O, and modification of file status flags.

Parameter Description:

fd: The file descriptor that has been opened.
cmd: The operation to be performed.
: variable parameters, the incoming cmd value is different, and the additional parameters are also different.

The five commonly used functions of the fcntl function and their corresponding cmd values ​​are as follows:

Duplicate an existing descriptor (cmd=F_DUPFD).
Get/set file descriptor flags (cmd=F_GETFD or F_SETFD).
Get/set file status flags ( cmd=F_GETFL or F_SETFL ).
Get/set async I/O ownership (cmd=F_GETOWN or F_SETOWN).
Get/set record lock (cmd=F_GETLK, F_SETLK or F_SETLKW).

  • Concrete implementation of non-blocking process

First call the fcntl function to obtain the file status flag corresponding to the file descriptor (this is a bitmap), and at this time, the cmd value passed in when the fcntl function is called F_GETFL. Add a non-blocking flag to
the obtained file status flagO_NONBLOCK and set it back.

void setNoBlock(int fd) 
{
    
     
	 int fl = fcntl(fd, F_GETFL); 
	 if (fl < 0) 
	 {
    
     
	 	perror("fcntl");
	 	return; 
	 }
	 fcntl(fd, F_SETFL, fl | O_NONBLOCK); 
}

3.2 Reading standard input with non-blocking polling

Let's take a look at the case of blocking input:

int main()
{
    
    
	char buf[1024];
	while(1)
    {
    
    
        std::cout << "[input]# ";
        fflush(stdout);
        ssize_t s = read(0, buf, sizeof buf - 1);
        if(s > 0)
        {
    
    
            // 正常读取
            buf[s] = '\0';
            std::cout << "[echo]# " << buf << std::endl;
        }
        else if(s == 0)
        {
    
    
            // 输入完了
            std::cout << "read end" << std::endl;
            break;
        }
        else
        {
    
    
            // -1
        }
    }
	return 0;
}

insert image description here

You can see that if we don't enter, it will block and wait.
Typing [Ctrl + d]is the end of typing:
insert image description here


Next look at non-blocking :

bool setNonBlock(int fd)
{
    
    
    int fl = fcntl(fd, F_GETFL);
	if (fl < 0)
    {
    
    
		std::cerr << "fcntl: " << strerror(errno) << std::endl;
		return false;
	}
	fcntl(fd, F_SETFL, fl | O_NONBLOCK);
	return true;
}

int main()
{
    
    
    setNonBlock(0);
	char buf[1024];
	while(1)
    {
    
    
        std::cout << "[input]# ";
        fflush(stdout);
        ssize_t s = read(0, buf, sizeof buf - 1);
        if(s > 0)
        {
    
    
            // 正常读取
            buf[s] = '\0';
            std::cout << "[echo]# " << buf << std::endl;
        }
        else if(s == 0)
        {
    
    
            // 输入完了
            std::cout << "read end" << std::endl;
            break;
        }
        else
        {
    
    
            // -1
        }
        sleep(1);
    }
	return 0;
}

insert image description here
You can see a phenomenon that I input mine, it prints its.

So we can perform other tasks while not typing .

typedef std::function<void()> func_t;

void TaskA()
{
    
    
    std::cout << "TaskA" << std::endl;
}

void TaskB()
{
    
    
    std::cout << "TaskB" << std::endl;
}

void TaskC()
{
    
    
    std::cout << "TaskC" << std::endl;
}

void ExecOther(std::vector<func_t>& v)
{
    
    
    for(auto& func : v)
    {
    
    
        func();
    }
}

int main()
{
    
    
    std::vector<func_t> cbs;// 回调方法
    cbs.push_back(TaskA);
    cbs.push_back(TaskB);
    cbs.push_back(TaskC);
    setNonBlock(0);
	char buf[1024];
	while(1)
    {
    
    
        std::cout << "[input]# ";
        fflush(stdout);
        ssize_t s = read(0, buf, sizeof buf - 1);
        if(s > 0)
        {
    
    
            // 正常读取
            buf[s] = '\0';
            std::cout << "[echo]# " << buf << std::endl;
        }
        else if(s == 0)
        {
    
    
            // 输入完了
            std::cout << "read end" << std::endl;
            break;
        }
        else
        {
    
    
            // -1
        }
        // 执行其他任务
        ExecOther(cbs);
        sleep(1);
    }
	return 0;
}

insert image description here

  • When the return value of read is -1, how to distinguish whether it is an error or there is no data at the bottom layer?

Observing the above code, both read errors and no data at the bottom will return -1, so how to distinguish them?
By error code .

else
{
    
    
    // -1
    std::cout << "errno: " << strerror(errno) << std::endl;
}

insert image description here
Indicates that the resource is not ready . ,

When the read function reads standard input in a non-blocking manner , when the underlying data is not ready, the read function returns in the form of an error, and the error code at this time will be set to EAGAINor EWOULDBLOCK.

In addition, calling the read function may be interrupted by other signals before reading the data . At this time, the readEINTR function will also return in the form of an error. read .

int main()
{
    
    
    std::vector<func_t> cbs;// 回调方法
    cbs.push_back(TaskA);
    cbs.push_back(TaskB);
    cbs.push_back(TaskC);
    setNonBlock(0);
	char buf[1024];
	while(1)
    {
    
    
        std::cout << "[input]# ";
        fflush(stdout);
        ssize_t s = read(0, buf, sizeof buf - 1);
        if(s > 0)
        {
    
    
            // 正常读取
            buf[s] = '\0';
            std::cout << "[echo]# " << buf << std::endl;
        }
        else if(s == 0)
        {
    
    
            // 输入完了
            std::cout << "read end" << std::endl;
            break;
        }
        else
        {
    
    
            // -1
            if(errno == EAGAIN)
            {
    
    
                // 底层没有数据
                // 执行其他任务
                ExecOther(cbs);
            }
            else if(errno == EINTR)
            {
    
    
                // 被信号中断
                continue;
            }
            else
            {
    
    
                std::cout << "errno: " << strerror(errno) << std::endl;
                break;
            }
        }
        sleep(1);
    }
	return 0;
}

Guess you like

Origin blog.csdn.net/qq_66314292/article/details/131860820