熊熊出没 发表于 2024-9-16 07:11:18

【在Linux世界中追寻巨大的One Piece】五种IO模子和壅闭IO

目次
1 -> 五种IO模子
1.1 -> 壅闭IO(Blocking IO)
1.2 -> 非壅闭IO(Non-blocking IO)
1.3 -> 信号驱动IO(Signal-Driven IO)
1.4 -> IO多路转接(IO Multiplexing)
1.5 -> 异步IO(Asynchronous IO)
2 -> 高级IO概念
2.1 -> 同步通讯VS异步通讯(synchronous communication/asynchronous communication)
2.2 -> 壅闭VS非壅闭
2.3 -> 非壅闭IO
2.3.1 -> fcntl函数
3 -> 实现函数SetNoBlock
4 -> 轮询方式读取标准输入
https://i-blog.csdnimg.cn/direct/11f3137c77e6436280b0ded96e16345e.png
1 -> 五种IO模子

1.1 -> 壅闭IO(Blocking IO)

壅闭IO是一种同步IO模子,它是最基本的IO模子。在这种模子中,当一个线程调用read()或write()举行IO操纵时,该线程被壅闭,即线程会暂停执行直到有数据被读取或数据完全写入。在此期间,线程不能执行其他操纵。壅闭IO的特点是实现简单,但在高并发场景下,每个线程都可能长时间处于壅闭状态,导致系统资源使用率低下。 
在壅闭IO模子中,如果应用步伐实验读取数据,而内核的缓冲区中没有数据可读,那么应用步伐会壅闭等待,直到数据预备好。同样,如果应用步伐实验写入数据,但内核的缓冲区已满,写入操纵也会壅闭,直到有空间可用。 
壅闭IO通常用于毗连数较少且每个毗连都有大量数据互换的应用场景。在这种环境下,每个毗连都可以由一个单独的线程处理,从而简化编程模子。
总的来说,壅闭IO是在内核将数据预备好之前,系统调用会不绝等待。所有的套接字,默认都是壅闭方式。
壅闭IO是最常见的IO模子。 
https://i-blog.csdnimg.cn/direct/1eca6c4e53ce41ea8bc2380c7a9d7029.png
1.2 -> 非壅闭IO(Non-blocking IO)

非壅闭IO答应线程发起IO请求后立即返回,即使数据还没有预备好。在这种模式下,线程可以举行其他任务,而不是等待IO操纵完成。如果数据预备好,线程可以继续执行IO操纵;如果没有,线程可以轮询或执行其他任务。非壅闭IO实用于需要处理大量并发毗连但每个毗连的数据量不大的场景。 
总的来说,如果内核还未将数据预备好,系统调用仍然会直接返回,并且返回EWOULDBLOCK错误码。非壅闭IO往往需要步伐员循环的方式反复实验读写文件描述符,这种过程称为轮询。这对CPU来说是较大的浪费,一样平常只有特定场景下才使用。
https://i-blog.csdnimg.cn/direct/3d7065ef04ee4da28184abacd755c712.png
1.3 -> 信号驱动IO(Signal-Driven IO)

信号驱动IO(Signal-Driven IO)是一种IO模子,它答应应用步伐在数据预备好时通过信号来处理IO事件。在这种模子中,当用户线程发起一个IO请求操纵时,会给对应的socket注册一个信号函数,然后用户线程会继续执行。当内核数据就绪时,会发送一个信号给用户线程,用户线程吸收到信号之后,便在信号函数中调用IO读写操纵来举行现实的IO请求操纵。 
信号驱动IO特别适合于UDP套接字,因为在UDP中,SIGIO信号会在数据报到达套接字或套接字上发生错误时产生。这样,应用步伐可以在信号处理函数中读取数据,而不需要不断轮询。 
在TCP套接字中,SIGIO信号的使用并不常见,因为TCP是双工的,信号产生得过于频繁,并且信号的出现并没有告诉我们发生了什么事变。因此,对于TCP套接字,SIGIO信号的使用是有限的。
总的来说,信号驱动IO是内核将数据预备好的时候,使用SIGIO信号关照应用步伐举行IO操纵。
https://i-blog.csdnimg.cn/direct/3edd8295c2654161be59c9fc352b7ccd.png
1.4 -> IO多路转接(IO Multiplexing)

IO多路转接是一种网络编程技术,它答应一个或多个进程使用单个或少量的线程高效地管理多个网络毗连。这种技术通过内核提供的系统调用来实现,内核会监视多个文件描述符,当这些文件描述符上的IO事件(如可读、可写)预备就绪时,内核会关照应用步伐。这样,应用步伐就可以在同一个线程中同时处理多个网络毗连,而不是为每个毗连创建一个新的线程,从而进步了步伐的并发处理本领和系统资源的使用率。
在Linux系统中,常见的IO多路转接技术包括select、poll和epoll。这些技术在处理大量并发毗连时非常有用,尤其是在构建高性能的服务器应用步伐时。
虽然从流程图上看起来和壅闭IO类似。现实上最焦点在于IO多路转接可以大概同时等待多个文件描述符的就绪状态。
https://i-blog.csdnimg.cn/direct/477d1e2f98154cacbfb4925d435612af.png
1.5 -> 异步IO(Asynchronous IO)

异步IO(Asynchronous IO)是一种IO处理模式,它答应应用步伐在发起一个IO操纵后立即继续执行其他任务,而不需要等待IO操纵的完成。在异步IO模子中,当应用步伐发起一个IO请求时,它会立即返回,应用步伐可以继续处理其他任务。一旦IO操纵完成,系统会通过某种机制关照应用步伐,应用步伐可以在适当的时候处理IO操纵的结果。这种模子可以明显进步步伐的并发处理本领和系统资源的使用率,尤其是在处理大量并发IO请求时。
总的来说,异步IO是由内核在数据拷贝完成时,关照应用步伐(而信号驱动是告诉应用步伐何时可以开始拷贝数据)。
https://i-blog.csdnimg.cn/direct/a906295434a744008dd497cee5400246.png
任何IO过程中,都包含两个步骤。第一是等待,第二是拷贝。而且在现实的应用场景中,等待消耗的时间往往都远远高于拷贝的时间。让IO更高效,最焦点的办法就是让等待的时间尽量少。
2 -> 高级IO概念

高级IO涉及到文件的IO操纵,它包括多种技术,如非壅闭IO、记录锁、IO多路转接、异步IO和存储映射等。这些技术通常依赖于文件描述符(fd)和fcntl函数的支持,用于进步文件IO操纵的服从和机动性。
2.1 -> 同步通讯VS异步通讯(synchronous communication/asynchronous communication)

同步和异步关注的是消息通讯机制。


[*]所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回. 但是一旦调用返回,就得到返回值了;换句话说,就是由调用者主动等待这个调用的结果。
[*]异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果;换句话说,当一个异步过程调用发出后,调用者不会立即得到结果;而是在调用发出后,被调用者通过状态、关照来关照调用者,或通过回调函数处理这个调用。
这里的同步通讯和进程之间的同步是完全不相干的概念。


[*]进程/线程同步也是进程/线程之间直接的制约关系。
[*]是为完成某种任务而建立的两个或多个线程,这个线程需要在某些位置上协调他们的工作序次而等待、传递信息所产生的制约关系。尤其是在访问临界资源的时候。
2.2 -> 壅闭VS非壅闭

壅闭和非壅闭关注的是步伐在等待调用结果(消息、返回值)时的状态。


[*]壅闭调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
[*]非壅闭调用指在不能立即得到结果之前,该调用不会壅闭当前线程。
2.3 -> 非壅闭IO

2.3.1 -> fcntl函数

fcntl(file control)是一个在UNIX和类UNIX操纵系统中用于文件描述符控制的系统调用。它提供了多种功能,包括复制文件描述符、设置文件状态标志、管理文件锁定以及设置文件描述符的异步I/O关照等。fcntl函数的原型通常如下:
#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ ); 此中,fd是要操纵的文件描述符,cmd是一个指示要执行的操纵的下令,arg是一个可选参数,具体取决于cmd的值。fcntl函数可以接受不同的下令来执行不同的操纵,例如:


[*]F_DUPFD:复制文件描述符。
[*]F_GETFD 和 F_SETFD:获取和解析文件描述符的标志。
[*]F_GETFL 和 F_SETFL:获取和解析文件状态标志,如O_NONBLOCK(非壅闭模式)。
[*]F_GETLK、F_SETLK 和 F_SETLKW:获取、设置和实验设置记录锁。
[*]F_GETOWN 和 F_SETOWN:获取和解析吸收SIGIO信号的进程ID或进程组ID。
fcntl函数在文件IO操纵中非常有用,尤其是在需要对文件描述符举行精致控制时,如设置文件为非壅闭模式或管理多进程对同一文件的并发访问。在网络编程中,fcntl也常用于设置文件描述符的异步关照,答应进程在IO事件发生时吸收信号,而不是轮询查抄。
3 -> 实现函数SetNoBlock

基于fcntl,我们实现一个SetNoBlock函数,将文件描述符设置为非壅闭。
void SetNoBlock(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参数。
4 -> 轮询方式读取标准输入

#include <stdio.h>#include <unistd.h>#include <fcntl.h>void SetNoBlock(int fd)
{
        int fl = fcntl(fd, F_GETFL);
        if (fl < 0)
        {
                perror("fcntl");

                return;
        }

        fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}int main() {        SetNoBlock(0);        while (1)         {                char buf = { 0 };                ssize_t read_size = read(0, buf, sizeof(buf) - 1);                if (read_size < 0)                 {                        perror("read");                        sleep(1);                        continue;                }                printf("input:%s\n", buf);        }        return 0;}

感谢各位大佬支持!!!
互三啦!!!



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【在Linux世界中追寻巨大的One Piece】五种IO模子和壅闭IO