【网络】高级IO——阻塞IO和非阻塞IO的实现
目录
一.文件描述符的默认行为——阻塞IO
二.非阻塞IO
2.1.在打开文件或创建套接字时设置非阻塞模式:
2.2.在使用网络I/O接口时请求非阻塞行为:
2.3.fcntl函数
一.文件描述符的默认行为——阻塞IO
在Linux系统中,无论是通过open系统调用打开的文件(包括系统文件、设备文件等),还是通过socket创建的网络套接字(sock),它们对应的文件描述符(fd)默认都是阻塞的。这种阻塞行为是操作系统为了简化同步I/O操作而设计的。
当进程尝试对一个文件描述符执行读或写操作时,如果所需的数据当前不可用(例如,读操作而缓冲区为空)或无法立即写入数据(例如,写操作而缓冲区已满或磁盘I/O繁忙),则进程将被挂起(阻塞),直到以下条件之一发生:
- 对于读操作:有数据可供读取,或者到达文件末尾(EOF)。
- 对于写操作:数据已经被成功写入到内核的缓冲区中,即使这些数据还没有被实际写入到磁盘上。
在阻塞模式下,进程需要等待这些条件成立才能继续执行,这可能会导致进程在I/O操作上花费大量时间,从而降低程序的响应性和吞吐量。
下面是一个简单的例子。
#include <iostream>
#include <unistd.h>
#include <string.h> int main() { std::cout << "This program echoes input in blocking mode. Try typing something and pressing enter.\n"; char buffer[100]; while (true) { ssize_t bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer) - 1); if (bytes_read > 0) { buffer[bytes_read] = '\0'; // 确保字符串以null字符结束 std::cout << "Echo: " << buffer << std::endl; } else if (bytes_read == 0) { // 对于文件描述符如stdin,在阻塞模式下,read返回0通常意味着EOF(文件结束) // 但对于stdin,这通常不会发生,除非输入被重定向自一个文件或管道,并且该文件或管道已经到达末尾 std::cout << "Unexpected end of input (this should not normally happen with stdin)." << std::endl; break; } else { // read返回-1时表示发生错误 std::cerr << "Error reading input: " << strerror(errno) << std::endl; break; } } return 0;
}
上面这就是典型的阻塞式IO,当程序运行起来时,执行流会在read处阻塞,因为read今天读取的是0号文件描述符,也就是键盘文件上的数据,只要我不从键盘上输入数据的话,read就会一直阻塞,此时进程会被操作系统挂起,直到硬件设备键盘上有数据时,进程才会重新投入CPU的运行队列,当我们输入数据后,可以立马看到进程显示出了echo回应的结果,同时进程又立马陷入阻塞,等待我进行下一次的输入数据,这样的IO方式就是典型的阻塞式,同时也是最常用,最简单的IO方式。
为了解决这个问题,Linux提供了非阻塞I/O(Non-blocking I/O)和异步I/O(Asynchronous I/O)等机制。通过设置文件描述符为非阻塞模式,进程可以在I/O操作无法立即完成时立即返回一个错误(通常是EAGAIN
或EWOULDBLOCK
),而不是被挂起。这样,进程就可以继续执行其他任务,而不会因等待I/O操作而阻塞。
对于网络套接字,除了设置非阻塞模式外,还可以使用I/O多路复用技术(如select、poll、epoll)来同时监视多个文件描述符的状态,从而在不增加线程或进程数量的情况下处理多个并发连接。这些技术允许进程在单个线程中高效地管理多个I/O操作,提高了程序的性能和可扩展性。
二.非阻塞IO
在Linux操作系统中,关于设置文件描述符(fd)为非阻塞模式的方式主要有两种。下面是对这两种主要方式的准确描述:
2.1.在打开文件或创建套接字时设置非阻塞模式:
- 对于文件(包括设备文件),通常不建议也不常见将其设置为非阻塞模式,因为文件I/O操作(如读、写)通常是同步完成的,且没有像网络I/O那样的等待状态。但是,如果您确实需要对文件描述符设置非阻塞模式(例如,对于管道、FIFO或某些特殊类型的文件),可以在使用open系统调用时,在flags参数中包含O_NONBLOCK标志。
- 对于套接字,由于套接字是通过socket系统调用创建的,而不是open,因此您不能在socket调用时直接设置O_NONBLOCK。但是,您可以在socket调用之后,使用fcntl函数和F_SETFL命令来修改套接字文件描述符的标志,以包含O_NONBLOCK。
// 创建套接字 int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 设置套接字为非阻塞模式 fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
注意:像fcntl这样的方式,无论对于系统文件还是网络套接字都是完全适用的。
2.2.在使用网络I/O接口时请求非阻塞行为:
对于网络套接字,虽然您已经在套接字级别设置了非阻塞模式,但在某些情况下,您可能还想在特定的send、recv等I/O调用中请求非阻塞行为,以确保该调用不会因等待数据而阻塞。这可以通过在调用这些函数时包含MSG_DONTWAIT标志来实现。但是,请注意,如果套接字已经设置为非阻塞模式,那么即使没有指定MSG_DONTWAIT,这些调用也不会阻塞。
MSG_DONTWAIT主要用于在套接字处于阻塞模式时,临时请求非阻塞行为。
// 套接字已经设置为非阻塞模式,但显式使用MSG_DONTWAIT也可以 ssize_t n = recv(sockfd, buf, len, MSG_DONTWAIT); if (n == -1 && errno == EAGAIN) { // 处理非阻塞情况下没有数据可读的情况 }
然而,在实际应用中,当您希望套接字以非阻塞方式工作时,通常会在套接字创建后立即使用fcntl设置其非阻塞模式,并在后续的网络I/O调用中不再需要显式地指定MSG_DONTWAIT(除非您有特定的理由需要在某些调用中临时恢复阻塞行为)。
因此,总结来说,设置文件描述符为非阻塞模式的主要方式是通过fcntl和O_NONBLOCK标志(对于套接字和某些特殊类型的文件),而不是通过open的O_NONBLOCK选项(因为套接字不是通过open创建的),并且MSG_DONTWAIT是在网络I/O调用中请求非阻塞行为的额外选项,但它不是设置文件描述符非阻塞模式的独立方法。
2.3.fcntl函数
当涉及到在Linux中对文件进行控制和管理时,fcntl(file control)函数是一个强大的工具。它提供了一种灵活的方式来执行各种文件操作,从修改文件属性到锁定文件,甚至是改变文件的行为。本文将深入探讨fcntl函数的用法、参数和示例,帮助读者更好地了解如何利用这个功能强大的API来操作文件。
fcntl函数是Linux系统中用于执行各种文件控制操作的系统调用之一。它可以用于修改文件描述符的属性,如文件状态标志(file status flags)、文件描述符标志(file descriptor flags)、文件锁(file locks)以及其他的一些操作。fcntl函数提供了对文件或文件描述符进行底层控制的接口,使得开发者可以更精细地管理文件的行为。
fcntl函数的原型和参数
在C语言中,fcntl函数的原型如下:
#include <fcntl.h>int fcntl(int fd, int cmd, ... /* arg */);
- fd 是要操作的文件描述符。
- cmd 是控制操作的命令。
- arg 是与命令相关联的可选参数。
fcntl函数的cmd参数决定了具体执行的操作类型,常见的一些操作包括:
- F_GETFL:获取文件描述符的状态标志。
- F_SETFL:设置文件描述符的状态标志。
- F_GETLK:获取文件锁。
- F_SETLK:设置或释放文件锁。
- F_SETLKW:阻塞地设置或释放文件锁。
文件状态标志(File status flags)
- O_RDONLY:只读打开。
- O_WRONLY:只写打开。
- O_RDWR:读写打开。
- O_APPEND:追加写入。
- O_CREAT:如果文件不存在则创建文件。
- O_EXCL:与O_CREAT一起使用,如果文件存在则报错。
- O_TRUNC:如果文件存在且为只写或读写,则将其长度截断为0。
文件描述符标志(File descriptor flags):
- FD_CLOEXEC:在exec执行期间关闭文件描述符。
其他标志:
- O_NONBLOCK:非阻塞模式,用于文件描述符,使得对文件的读写操作不会阻塞进程。
- O_SYNC:使得每次write都等到物理 I/O 操作完成后才返回。
- O_DIRECTORY:如果文件名是目录,则打开失败。
- O_DSYNC:等待物理 I/O 数据完成,不等待文件属性更新。
- O_NOATIME:不更新访问时间戳。
- O_NOCTTY:如果设备是终端,不将其分配为控制终端。
下面是一个简单的将文件描述符设置为非阻塞的例子
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h> int main() { std::cout << "This program echoes input in non-blocking mode. Try typing something and pressing enter.\n"; char buffer[100]; while (true) { ssize_t bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer) - 1); if (bytes_read > 0) { buffer[bytes_read] = '\0'; // 确保字符串以null字符结束 std::cout << "Echo: " << buffer << std::endl; } else if (bytes_read == 0) { // 对于文件描述符如stdin,read返回0通常不表示结束,但在某些特殊情况下(如管道关闭)可能会发生 std::cout << "Unexpected end of input (this should not happen with stdin)." << std::endl; break; } else { // read返回-1且errno被设置时表示发生错误 if (errno == EAGAIN || errno == EWOULDBLOCK) { // 这是非阻塞模式下期望的行为,表示没有数据可读 std::cout << "No data available. Waiting...\n"; sleep(1); // 等待一秒后再次尝试 } else { // 处理其他类型的错误 std::cerr << "Error reading input: " << strerror(errno) << std::endl; break; } } } return 0;
}
我们看到它阻塞了。
接下来我将使用fcntl函数来将fd的属性改为非阻塞
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h> /** * 将指定的文件描述符设置为非阻塞模式。 * * @param fd 需要设置为非阻塞模式的文件描述符。 */
void set_no_block(int fd) { // 尝试获取文件描述符的当前标志(flags) int flags = fcntl(fd, F_GETFL, 0); // 检查fcntl调用是否失败 if (flags == -1) { // 如果失败,则打印错误消息并返回 perror("fcntl"); // 使用perror打印错误信息,它会自动添加"fcntl: "前缀 return; } // 使用OR操作将O_NONBLOCK标志添加到现有的flags中 // 这样做的目的是保留文件描述符的其他标志不变,只添加非阻塞特性 fcntl(fd, F_SETFL, flags | O_NONBLOCK); // 注意:这里没有检查fcntl的返回值,因为即使设置失败,也可能不会立即影响程序的行为 // 在实际使用中,可能需要根据需要添加错误处理逻辑
} int main() { std::cout << "This program echoes input in non-blocking mode. Try typing something and pressing enter.\n"; // 将标准输入设置为非阻塞模式 set_no_block(STDIN_FILENO); // 使用STDIN_FILENO代替0,更具可读性 char buffer[100]; while (true) { ssize_t bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer) - 1); if (bytes_read > 0) { buffer[bytes_read] = '\0'; // 确保字符串以null字符结束 std::cout << "Echo: " << buffer << std::endl; } else if (bytes_read == 0) { // 对于文件描述符如stdin,read返回0通常不表示结束,但在某些特殊情况下(如管道关闭)可能会发生 std::cout << "Unexpected end of input (this should not happen with stdin)." << std::endl; break; } else { // read返回-1且errno被设置时表示发生错误 if (errno == EAGAIN || errno == EWOULDBLOCK) { // 这是非阻塞模式下期望的行为,表示没有数据可读 std::cout << "No data available. Waiting...\n"; sleep(1); // 等待一秒后再次尝试 } else { // 处理其他类型的错误 std::cerr << "Error reading input: " << strerror(errno) << std::endl; break; } } } return 0;
}
注意:
F_GETFL
操作用于获取文件描述符的当前标志,这些标志包括文件的访问模式(如只读、只写、读写)和文件状态标志(如同步、异步、非阻塞等)。F_SETFL
操作用于设置文件描述符的标志。O_NONBLOCK
是一个标志,用于指示文件描述符应处于非阻塞模式。在非阻塞模式下,如果操作(如读取或写入)不能立即完成,则调用将返回一个错误,而不是阻塞等待操作完成。这对于需要处理多个输入源或避免阻塞的应用程序非常有用。- 先通过F_GETFL选项获取原有文件描述符的标志位,然后再通过F_SETFL选项将原有的标志位与O_NONBLOCK按位或之后,再重新设置回文件中。这样就可以将文件描述符设置为非阻塞了。
- 非阻塞IO时,read的返回结果是-1,这样合理吗?
当在非阻塞模式下调用
read
函数,并且底层没有数据时,read
会返回-1
,并设置errno
为EAGAIN
或EWOULDBLOCK
(这两个错误码在大多数系统上可互换,表示资源暂时不可用)。这是非阻塞IO的一种标准行为,用于告诉调用者当前没有数据可读,而不是表示read
函数本身出现了错误。底层没有数据,这算错误吗?其实这并不算错误,只不过当底层没有数据时,read以错误的方式返回了,但我们该如何区分read接口是真的调用失败了(比如read读取了一个不存在的fd),还是仅仅底层没有数据罢了,当然通过read的返回值我们是无法区分的,因为read在这两种情况下都返回-1,但可以通过错误码来区分,当非阻塞IO返回时,如果是底层没有数据,错误码会是EWOULDBLOCK或EAGAIN,如果read是真的出错调用了,会有相对应的错误码。
因此,在处理非阻塞IO时,一种常见的做法是在
read
返回-1
时检查errno
,并根据errno
的值来决定下一步的操作。如果errno
表示资源暂时不可用(EAGAIN
或EWOULDBLOCK
),则程序可能会选择等待一段时间后再次尝试读取,或者执行其他任务。如果errno
表示真正的错误(如EBADF
),则程序应该采取适当的错误处理措施。
在Linux系统中,
EAGAIN
和EWOULDBLOCK
是两个常见的错误码,它们通常用于指示资源暂时不可用的情况。具体来说:
EAGAIN:这个错误码代表“Try again”(再试一次),意味着请求的操作暂时无法完成,但之后可能会成功。在网络编程中,
EAGAIN
经常出现在非阻塞套接字的读写操作中,表示暂时没有数据可读或数据无法立即写入。在文件操作中,如果文件描述符被设置为非阻塞模式,并且请求的操作不能立即完成(例如,读取时文件指针已经到达文件末尾,或者写入时磁盘空间不足但系统决定不等待),那么也会返回EAGAIN
。EWOULDBLOCK:这个错误码在某些情况下与
EAGAIN
可互换,同样表示资源暂时不可用。然而,在某些系统或特定的上下文中,EWOULDBLOCK
可能更具体地用于指示某个操作被阻塞了,因为它会等待某个条件(如数据到达)变为真,但在非阻塞模式下,这个等待被阻止了。在大多数情况下,EAGAIN
和EWOULDBLOCK
可以视为同义词,尤其是在处理非阻塞I/O时。
怎么样?是不是很神奇?
相关文章:

【网络】高级IO——阻塞IO和非阻塞IO的实现
目录 一.文件描述符的默认行为——阻塞IO 二.非阻塞IO 2.1.在打开文件或创建套接字时设置非阻塞模式: 2.2.在使用网络I/O接口时请求非阻塞行为: 2.3.fcntl函数 一.文件描述符的默认行为——阻塞IO 在Linux系统中,无论是通过open系统调用…...

ASPICE培训:打造卓越的汽车软件开发能力
随着汽车行业的快速发展和智能化技术的不断演进,汽车软件开发已成为汽车制造过程中不可或缺的一部分。为了确保汽车软件的质量、可靠性和安全性,ASPICE(Automotive SPICE)作为一种国际公认的汽车软件开发过程评估标准,…...

基于Python的人工智能应用案例系列(2):分类
在本篇文章中,我们将探讨分类问题,具体的应用场景是贷款审批预测。通过该案例,我们将学习如何使用Python处理分类问题,训练模型并预测贷款是否会被批准。 案例背景 该数据集包含贷款申请的相关信息,目标是预测贷款是否…...

演示:基于WPF自绘的中国省份、城市、区县矢量地图
一、目的:演示一个基于WPF自绘的中国省份、城市、区县矢量地图 二、效果 国 省 市 三、功能 支持实际经纬度显示 支持平移,缩放等功能 显示中国地图 显示各个省份地图 显示各个省份地图(包含在表格中,包含缩率图) 显…...

同时拥有独显和核显,怎么让应用程序选择使用哪个GPU?
看你现在使用的是核显还是独显 勾选上GPU引擎选项,后面便会标识你所使用的是哪种显卡,如果是独立显卡,就可以免去后续的操作;如果不是,那么请继续接下来的操作。 将你需要使用独显的程序换成gpu1(独显&am…...

C++八股文之面向对象篇
🤖个人主页:晚风相伴-CSDN博客 思维导图链接:面向对象的性质 持续更新中…… 💖如果觉得内容对你有帮助的话,还请给博主一键三连(点赞💜、收藏🧡、关注💚)吧 …...

点云深度学习系列:Sam2Point——基于提示的点云分割
文章:SAM2POINT:Segment Any 3D as Videos in Zero-shot and Promptable Manners 代码:https://github.com/ZiyuGuo99/SAM2Point Demo:https://huggingface.co/spaces/ZiyuG/SAM2Point 1)摘要 文章介绍了SAM2POINT,这是…...

mysql学习教程,从入门到精通,TOP 和MySQL LIMIT 子句(15)
1、TOP 和MySQL LIMIT 子句内容 在SQL中,不同的数据库系统对于限制查询结果的数量有不同的实现方式。TOP 关键字主要用于 SQL Server 和 Access 数据库中,而 LIMIT 子句则主要用于 MySQL、PostgreSQL(通过 LIMIT/OFFSET 语法)、S…...

备战软考Day02-数据结构与算法
1.基本概念与三要素 1.什么是数据 数据是信息的载体,是描述客观事物属性的数、字符及所有能输入到计算机中并被计算机程序识别和处理的符号的集合。数据是计算机程序加工的原料。 2.数据元素、数据项 数据元素是数据的基本单位,通常作为一个整体进行…...

COMP 6714-Info Retrieval and Web Search笔记week1
哭了哭了,这周唯一能听懂的就这门 目录 IR(Information Retrieval)是什么?IR的基本假设Unstructured (text) vs. structuredDocuments vs. Database Records比较文本(Comparing Text)IR的范围(Dimensions of IR)IR的任…...

C++在Linux实现多线程和多进程的TCP服务器和客户端通信
多进程版本 服务器 #include <arpa/inet.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/wait.h> #include <signal.h> #include <string&…...

音视频开发常见的开源项目汇总
FFmpeg 地址:https://ffmpeg.org/介绍:FFmpeg 是一个非常强大的开源多媒体框架,它可以用来处理视频和音频文件。它支持多种格式的转换、编码、解码、转码、流处理等。FFmpeg 包括了 libavformat、libavcodec、libavutil、libswscale、libpos…...

Java操控Redis (面经之 使用Redis)
操控Redis的工具 ReactiveRedisTemplate 和 RedisTemplate : RedisTemplate: 它是一个通用的模板类,可以使用任何序列化策略来序列化和反序列化键和值。默认情况下,它使用 JdkSerializationRedisSerializer 序列化值,并使用 Strin…...

【计网】从零开始使用UDP进行socket编程 --- 服务端业务实现
在我们每个人都曾经历过“沮丧”时刻里, 如果我们不能对别人说有益的好话, 那我们最好还是什么也别说。 --- 卡耐基 《人性的弱点》--- 从零开始使用UDP进行socket编程 1 前情提要2 单词翻译2.1 业务需求2.2 设计字典类2.3 服务端与客户端逻辑2.4 运…...

正式发售!《黑神话:悟空》背后的技术力量——UE5与实时云渲染
千呼万唤始出来,《黑神话:悟空》终于在今年8月发售了,相信大家都已经玩起来了! 作为国产游戏的画质巅峰之作,《黑神话:悟空》凭借其令人叹为观止的画面质量和游戏体验,赢得了广泛的好评。这一切…...

qt-creator-10.0.2之后版本的jom.exe编译速度慢下来了
1、Qt的IDE一直在升级,qt-creator的新版本下载地址 https://download.qt.io/official_releases/qtcreator/ 2、本人一直用的是qt-creator-10.0.2版本,官网历史仓库可以下载安装包qt-creator-opensource-windows-x86_64-10.0.2.exe https://download.qt…...

2024CSP-J初赛全真模拟卷选择题篇(原创,难度偏简单)
注意,本卷由再临TSC原创,禁止转载! 本卷难度偏简单,若想要通过初赛本卷应拿80分左右 查看答案的方法: if(设备"PC") { 把光标移到答案上面,选中答案,就会显示(); } …...

【Android 13源码分析】WindowContainer窗口层级-4-Layer树
在安卓源码的设计中,将将屏幕分为了37层,不同的窗口将在不同的层级中显示。 对这一块的概念以及相关源码做了详细分析,整理出以下几篇。 【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树 【Android 13源码分析】WindowCon…...

C# 开发教程-中级教程
1.C# 多线程/异步 C# 异步编程Task整理(一) C# 异步编程Task整理(二)异常捕捉 C# 异步编程Task(三) async、await C#中创建线程,创建带参数的线程 C# 线程同步之排它锁/Monitor监视器类 C# lock关键词/lock语句块…...

【C++】c++的继承
目录 思维导图大纲: 1.基类和派生类 1.1 定义格式 1.2 继承方式 1.3 基类和派生类的转换 2. 继承中的作用域(隐藏关系) 2.1 考察继承作⽤域相关选择题 3. 派生类的默认成员函数 4. 继承类模板 5. 一个不能被继承的类 编辑 6.继承与友元 编辑 7. 继…...

【ShuQiHere】 进制转换的世界:从十进制到二进制、十六进制的转换技巧
【ShuQiHere】 在计算机科学中,进制转换(Radix Conversion) 是一个基础且非常重要的技能。无论是理解计算机的存储、数据表示,还是在编程中处理不同的进制数据,进制转换都是不可或缺的。本文将详细讲解 十进制&#x…...

《化工管理》
《化工管理》征稿简则 《化工管理》杂志是由中国石油和化学工业联合会主管、中国化工企业管理协会主办,1986年创刊,在国内外公开发行,国内统一连续出版物号:CN 11—3991/F,中国标准连续出版物号:ISSN 1008—…...

LeetCode70:爬楼梯
class Solution { public:int climbStairs(int n) {if(n 1) return 1;if(n 2) return 2;vector<int> dp(n 1, 0);dp[1] 1;dp[2] 2;for(int i 3; i < n 1; i){dp[i] dp[i - 1] dp[i - 2];}return dp[n];} }; 这个题目也就是最简单的动态规划,题目…...

[程序员] 前人留下的苦难源,我们是否有勇气改正?
最近遇到一个客户现场发现的,表象是网络有问题,分析一圈下来发现是程序进入了某种死循环状态,耗尽CPU。 产品里的很多线程/进程的优先级设置的很高,甚至高过了内核运行程序的优先级,高过了产品内警告处理程序的运行&a…...

聚类_K均值
import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import make_blobs1.数据预处理 #创建基于高斯分布的样本点, x是点的坐标,y是所属聚类值 x, y make_blobs(n_samples100, centers6, random_state100, cluster_std0.6) # 设置图形尺寸…...

Mac电脑剪切板在哪里找 苹果电脑剪切板打开教程【详解】
Windows 和 Mac 电脑在使用方式上存在一些差异,许多习惯了 Windows 系统的用户初次接触 Mac 时可能会对某些操作感到困惑。比如,很多人会问:Mac 上的剪贴板在哪里?如果你也有这样的疑问,不妨看看下面这篇关于如何在 Ma…...

Python编程 - 三器一包
目录 前言 一、迭代器 (一)基本概念 (二)迭代器和可迭代对象 (三)创建迭代器 (四)内置迭代器函数 (五)优点和局限性 二、生成器 (一&…...

InternVL 多模态模型部署微调实践
友情链接 该文档参考InternVL垂直领域场景微调实践而写成,感谢社区同学法律人的文档。 写在前面(什么是InternVL) InternVL 是一种用于多模态任务的深度学习模型,旨在处理和理解多种类型的数据输入,如图像和文本。它…...

Ruby Dir 类和方法
Ruby Dir 类和方法 Ruby 中的 Dir 类提供了用于处理目录的各种方法。这些方法允许您列出目录内容、更改当前工作目录、创建和删除目录等。本文将详细介绍 Dir 类的常用方法,并通过示例展示如何使用它们。 目录 Dir 类的简介常用方法 Dir.chdirDir.childrenDir.de…...

C++STL~~deque
文章目录 deque的概念deque的使用deque的练习总结 deque的概念 deque(双端队列):是一种序列容器、是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1)ÿ…...