Linux--IO模型与高级IO重要概念

诡异森林。 2024-10-09 11:07:02 阅读 98

什么是IO?

IO是指计算机系统与外部世界进行数据交换的过程。在计算机中,IO通常用于与外部设备通信,这些设备包括键盘、鼠标、打印机、显示器、网络等。通过IO操作,计算机系统可以接收来自外部设备的输入数据,也可以将处理后的数据输出到外部设备。

在网络通信上,主要表示在计算机与计算机之间能通过互联网来进行数据交换,从而实现远程数据与资源的共享。

五种IO模型

阻塞IO

在这里插入图片描述

用户线程在发起IO请求后会阻塞,直到IO操作完成(内核数据准备好)并返回结果。这种方式简单但效率低下,特别是在高并发场景下。

非阻塞IO

在这里插入图片描述

如果内核还没有准备好数据,内核也会进行返回,并且返回一个EWOULDBLOCK错误码;

非阻塞IO需要在代码中循环,不断的查询读写这个文件描述符,这种操作称为轮询。在特定场景中,才会进行使用,会比较浪费CPU资源。

信号驱动IO

在这里插入图片描述

当内核将数据准备好时,内核会向应用进程发送一个SIGIO信号,通知程序进行IO操作

IO多路转接

在这里插入图片描述

系统调用允许单个线程同时监视多个文件描述符的IO就绪状态,从而提高了IO操作的效率。

异步IO

在这里插入图片描述

异步IO是指系统内核主动发起IO请求,用户空间的线程被动接受IO操作的结果。这种方式下,用户线程无需等待IO操作完成即可继续执行其他任务,当IO操作完成后,内核会通知用户线程处理结果。

小结

任何IO过程,都包含两个步骤:等待数据+拷贝数据

在实际应用中,等待消耗的时间往往大于拷贝消耗的时间。

所以,要想提高IO的效率,就要减少等待的消耗时间。

高级IO的重要概念

同步IO

同步IO是指需要等待前面程序完成后,才能继续执行后面的程序。这意味着当程序发起一个IO请求时,它会阻塞当前线程的执行,直到IO操作完成。

特点:

阻塞性:同步IO在读写数据时会阻塞程序的执行,导致程序无法充分利用CPU资源。顺序性:同步IO按照程序指定的顺序依次执行IO操作,前一个IO操作完成后,下一个IO操作才会开始。简单性:同步IO的实现相对简单,因为程序只需按照顺序执行IO操作,无需处理复杂的并发逻辑。

同步IO在IO操作期间会阻塞程序执行,导致CPU资源无法得到充分利用。

当IO操作时间较长时,程序会处于等待状态,造成CPU资源的浪费。

同步IO适用于一些简单的、不需要高并发处理的场景。例如,批处理作业、简单计算和查询程序等。

在这些场景中,同步IO的阻塞性和顺序性并不会对程序的性能产生太大影响。

异步IO

异步IO是指在进行数据读写操作时,程序无需等待IO操作完成。程序会发起IO操作,并立即返回一个标识符或回调函数,用于指示IO操作的状态或结果。

特点:

非阻塞性:异步IO允许程序在发起IO请求后继续执行其他任务,无需等待IO操作完成。并发性:异步IO可以同时处理多个IO请求,提高了程序的并发处理能力。复杂性:异步IO的实现相对复杂,因为程序需要处理IO完成的通知和管理异步操作的状态。这通常涉及到事件循环、回调函数等概念。

异步IO可以充分利用CPU资源,因为程序可以在IO操作期间执行其他任务。

异步IO通过并发处理多个IO请求,提高了系统的整体性能和资源利用率。

异步IO适用于需要处理大量并发IO请求的场景。例如,Web服务器、数据库访问、网络编程等。

在这些场景中,异步IO的非阻塞性和并发性能够显著提高程序的执行效率和并发处理能力。

非阻塞IO的实现

对于C语言/C++来说,默认都是阻塞IO,如果要改为非阻塞IO,就要通过特定函数来进行实现;

fcntl()

在Linux系统中用于操作文件描述符的库函数。

<code>#include <fcntl.h>

#include <unistd.h>

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

参数:

fd: 文件描述符,是一个非负整数,表示要操作的文件。cmd: 一个命令,用于指定要执行的操作。不同的命令需要不同数量的附加参数。… /* arg */: 根据 cmd 命令的不同,可能需要一个或多个附加参数。这些参数通常是一个指向数据的指针。

常用命令

F_DUPFD: 复制文件描述符。F_GETFD: 获取文件描述符标志。F_SETFD: 设置文件描述符标志。F_GETFL: 获取文件状态标志(如只读、非阻塞等)。F_SETFL: 设置文件状态标志。F_GETLK: 获取文件锁的状态。F_SETLK: 设置文件锁(非阻塞)。F_SETLKW: 设置文件锁(阻塞)。

#include<iostream>

#include<unistd.h>

#include<fcntl.h>

void SetNonBlock(int fd)

{

int fl = ::fcntl(fd, F_GETFL);//获取fd当前文件描述符状态,默认为阻塞IO

if(fl < 0)

{

return;

}

fcntl(fd, F_SETFL, fl | O_NONBLOCK);//将其设置为非阻塞IO

}

int main()

{

char buffer[1024];

SetNonBlock(0);//设置成非阻塞的

while (true)

{

//读取标准输入流的数据

ssize_t s = read(0, buffer, sizeof(buffer) - 1);

if(s > 0)

{

buffer[s] = 0;

std::cout << "Echo# " << buffer << std::endl;

}

else

{

//问题:我怎么知道是底层IO条件不就绪,还是读取错误了呢???

// 底层IO条件就绪和读取错误采用的是同样返回值操作的

if(errno == EWOULDBLOCK || errno == EAGAIN)

{

std::cout << "底层数据没有就绪, 下次在试试吧! 做做其他事情!" << std::endl;

sleep(1);

continue;

}

std::cout << "读取错误... s : " << s << " errno: " << errno << std::endl;

sleep(1);

}

}

return 0;

}

在这里插入图片描述

在阻塞IO中,read函数如果没有接收到输入的数据,那么就会一直阻塞,直到有数据的输入;

在这里插入图片描述

非阻塞IO中,如果没有输入数据,那么read函数也不会阻塞,会因为while循环进行轮询,不断的执行当前主函数,一旦read函数收到数据,那么就会打印对应数据。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。