嵌入式Linux系统编程 — 2.4 标准I/O库:I/O缓冲详解

目录

1 I/O 缓冲简介

1.1 什么是I/O 缓冲

1.2 I/O 缓冲的目的

2 文件 I/O 的内核缓冲

3 刷新文件 I/O 的内核缓冲区

3.1 什么是刷新文件 I/O 的内核缓冲区

3.2 控制文件 I/O 内核缓冲的系统调用函数

3.3 示例程序

4 控制文件 I/O 内核缓冲的标志

4.1 O_DSYNC 和 O_SYNC标志简介

4.2 示例程序

5 直接 I/O:绕过内核缓冲

5.1 绕过内核缓冲含义

5.2 为什么不都使用直接 I/O

5.3 直接 I/O 的对齐限制

6 stdio 缓冲

6.1 stdio 缓冲简介

6.2 标准 I/O 的 stdio 缓冲函数

6.3 标准输出 printf()的行缓冲模式测试

6.4 将标准输出配置为无缓冲模式测试

6.5 fflush()刷新 stdio 缓冲区

6.6 关闭与退出时刷新 stdio 缓冲区


1 I/O 缓冲简介

1.1 什么是I/O 缓冲

出于速度和效率的考虑,系统 I/O 调用(即文件 I/O)和标准 C 语言库 I/O 函数(即标准 I/O 函数)在操作磁盘文件时会对数据进行缓冲。I/O缓冲指的是在数据传输过程中,用于临时存储数据的内存区域。它允许程序先将数据写入或从内存中的一个缓冲区读取,然后再与外部设备(如硬盘、网络或控制台)进行数据交换。

1.2 I/O 缓冲的目的

提高效率:减少实际的I/O操作次数,因为可以积累一定量的数据后再执行一次较大的传输,而不是每次只传输少量数据。

减少阻塞:在缓冲的帮助下,程序可以继续执行其他任务,而不必等待每次I/O操作完成。

数据整合:在输出时,可以将多次小的数据写入合并为一次较大的写入操作。

2 文件 I/O 的内核缓冲

read()和 write()系统调用在进行文件读写操作的时候并不会直接访问磁盘设备,而是仅仅在用户空间缓冲区和内核缓冲区(kernel buffer cache)之间复制数据。譬如调用 write()函数将 5 个字节数据从用户空间内存拷贝到内核空间的缓冲区中:

write(fd, "Hello", 5); //写入 5 个字节数据

调用 write()后仅仅只是将这 5 个字节数据拷贝到了内核空间的缓冲区中,拷贝完成之后函数就返回了,在后面的某个时刻,内核会将其缓冲区中的数据写入(刷新)到磁盘设备中,所以由此可知,系调用 write()与磁盘操作并不是同步的, write()函数并不会等待数据真正写入到磁盘之后再返回。如果在此期间, 其它进程调用 read()函数读取该文件的这几个字节数据,那么内核将自动从缓冲区中读取这几个字节数据返回给应用程序。

与此同理,对于读文件而言亦是如此,内核会从磁盘设备中读取文件的数据并存储到内核的缓冲区中,当调用 read()函数读取数据时, read()调用将从内核缓冲区中读取数据,直至把缓冲区中的数据读完,这时,内核会将文件的下一段内容读入到内核缓冲区中进行缓存。

文件 I/O 的内核缓冲区自然是越大越好, Linux 内核本身对内核缓冲区的大小没有固定上限。内核会分配尽可能多的内核来作为文件 I/O 的内核缓冲区,但受限于物理内存的总量,如果系统可用的物理内存越多,那自然对应的内核缓冲区也就越大,操作越大的文件也要依赖于更大空间的内核缓冲。

3 刷新文件 I/O 的内核缓冲区

3.1 什么是刷新文件 I/O 的内核缓冲区

刷新文件 I/O 的内核缓冲区:就是强制将文件 I/O 内核缓冲区中缓存的数据写入(刷新)到磁盘设备中。对于某些应用场景来说,可能是很有必要的,例如应用程序在进行某操作之前, 必须要确保前面步骤调用 write()写入到文件的数据已经真正写入到了磁盘中, 诸如一些数据库的日志进程。

以Ubuntu系统中的文件传输为例,当用户将文件复制到U盘后,拔除U盘前通常需要执行sync命令。sync命令的作用是确保所有文件I/O操作已经完成,内核缓冲区中的数据已被强制写入到U盘。如果省略此步骤,直接拔出U盘,可能会导致数据丢失或文件损坏,因为缓冲区中的数据可能尚未完全写入到U盘。

3.2 控制文件 I/O 内核缓冲的系统调用函数

Linux 中提供了一些系统调用可用于控制文件 I/O 内核缓冲,包括系统调用 sync()、 syncfs()、 fsync()以及 fdatasync()。

#include <unistd.h>void sync(void);
int syncfs(int fd);
int fsync(int fd);
int fdatasync(int fd);

sync()将所有未写的或延迟写入的缓冲数据发送到磁盘。

  • 用法:通常在系统范围内刷新所有的文件系统缓冲区。

syncfs()同步指定文件描述符的文件系统缓冲区到磁盘。

  • 用法:syncfs() 调用只影响特定的文件系统,而不是整个系统。

fsync()强制将指定文件描述符的所有未写入数据同步到磁盘。

  • 用法:fsync() 通常用于确保对某个特定文件的所有更改都已持久化。

fdatasync()类似于 fsync(),但它只同步文件的数据部分,不包括元数据(如修改时间)。

  • 用法:当只需要同步文件内容而不需要更新文件属性时,使用 fdatasync()

3.3 示例程序

下面是程序示例演示了如何使用 fsync()fdatasync() 函数来确保文件数据被同步到磁盘:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>int main() 
{// 打开文件用于写入int fd = open("example.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);if (fd == -1) {perror("Failed to open file");return 1;}// 写入数据到文件const char *data = "Important data to sync to disk";if (write(fd, data, strlen(data)) == -1) {perror("Failed to write to file");close(fd);return 1;}// 使用 fsync() 同步文件的所有数据和元数据到磁盘if (fsync(fd) == -1) {perror("fsync failed");close(fd);return 1;}printf("File and metadata synced to disk using fsync.\n");// 再次写入数据到文件if (write(fd, data, strlen(data)) == -1) {perror("Failed to write to file");close(fd);return 1;}// 使用 fdatasync() 只同步文件的数据部分到磁盘if (fdatasync(fd) == -1) {perror("fdatasync failed");close(fd);return 1;}printf("Data synced to disk using fdatasync.\n");// 关闭文件描述符close(fd);return 0;
}

程序首先打开(或创建)一个名为 example.txt 的文件用于写入,然后向其中写入一段数据。接着,使用 fsync() 函数确保这些数据和文件的元数据被同步到磁盘,以保证数据的持久性。之后,程序再次写入相同的数据,并使用 fdatasync() 函数仅同步数据部分到磁盘,而不包括元数据。最后,关闭文件描述符。程序运行结果如下:

4 控制文件 I/O 内核缓冲的标志

4.1 O_DSYNCO_SYNC标志简介

O_DSYNCO_SYNC 是两个用于控制文件I/O操作同步性的标志,定义了文件操作的同步写入行为。可以与 open() 函数一起使用,以改变文件的默认写入行为。

O_SYNC

  • 当使用 O_SYNC 标志打开文件时,所有对该文件的写操作都将被执行为同步写入。意味着每次写操作(write())完成后,系统都会确保数据被实际写入到磁盘,而不仅仅是内核缓冲区,但可能会降低性能,因为每次写操作都需要等待磁盘I/O完成。

O_DSYNC

  • O_DSYNC 标志用于打开文件,使得所有写操作都执行为同步的,就像 O_SYNC 一样。但是,与 O_SYNC 不同的是,O_DSYNC 只保证数据的同步性,而不保证文件元数据(如修改时间)的同步更新。意味着使用 O_DSYNC 标志时,文件内容会及时写入磁盘,但文件属性可能不会立即更新。

4.2 示例程序

下面的示例演示如使用 O_SYNCO_DSYNC 标志打开文件,并进行写操作。

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main() {const char *filename = "sync_example.txt";const char *data = "Data that needs to be synced immediately";// 使用 O_SYNC 标志打开文件,每次写操作后都会同步数据和元数据int sync_fd = open(filename, O_WRONLY | O_CREAT | O_SYNC, 0644);if (sync_fd == -1) {perror("Failed to open file with O_SYNC");return 1;}write(sync_fd, data, strlen(data)); // 写操作会立即同步到磁盘close(sync_fd);// 使用 O_DSYNC 标志打开文件,每次写操作后都会同步数据,但元数据可能不会int dsync_fd = open(filename, O_WRONLY | O_CREAT | O_DSYNC, 0644);if (dsync_fd == -1) {perror("Failed to open file with O_DSYNC");return 1;}write(dsync_fd, data, strlen(data)); // 数据会立即同步到磁盘,元数据可能不会close(dsync_fd);return 0;
}
  • 使用 O_SYNC 标志打开文件 sync_example.txt 进行写操作,这确保了每次写操作后,数据和文件的元数据都会立即同步到磁盘。
  • 使用 O_DSYNC 标志再次打开同一个文件进行写操作,这确保了数据会立即同步,但文件的元数据(如修改时间)可能不会立即更新到磁盘。

5 直接 I/O:绕过内核缓冲

5.1 绕过内核缓冲含义

前面的内容提到,数据传输过程中会临时存储数据到内存缓冲区,为了保持数据同步,会利用相关函数和标志将数据立即更新到磁盘。那么,有没有方法省略中间的缓冲步骤,直接将数据传递到文件或磁盘设备?

从Linux内核2.4版本起,Linux系统就支持应用程序执行直接I/O操作,即绕过内核缓冲区,直接在用户空间和磁盘之间传输数据。

5.2 为什么不都使用直接 I/O

在有些情况下,这种操作通常是很有必要的,例如,某应用程序的作用是测试磁盘设备的读写率, 那么在这种应用需要下,我们就需要保证 read/write 操作是直接访问磁盘设备,而不经过内核缓冲,如果不能得到这样的保证,必然会导致测试结果出现比较大的误差。

在特定情况下,如磁盘读写性能测试,直接I/O是必要的,以确保读写操作不经过内核缓冲,从而获得准确的测试结果。对于大多数应用程序而言,使用直接 I/O 可能会大大降低性能,这是因为为了提高 I/O 性能,内核针对文件 I/O 内核缓冲区做了不少的优化,譬如包括按顺序预读取、在成簇磁盘块上执行 I/O、允许访问同一文件的多个进程共享高速缓存的缓冲区。如果应用程序使用直接 I/O 方式, 将无法享受到这些优化措施所带来的性能上的提升,直接 I/O 只在一些特定的需求场合,譬如磁盘速率测试工具、数据库系统等。

要实现直接I/O,可以在打开文件时通过open()函数并带上O_DIRECT标志来指定。例如:

int fd = open("file.dat", O_WRONLY | O_CREAT | O_DIRECT, 0644);
if (fd < 0) {perror("open");// 处理错误
}
// 使用write()等函数进行I/O操作
// ...
close(fd);

示例中,O_DIRECT标志被添加到open()函数的参数中,操作系统进行直接I/O操作。

5.3 直接 I/O 的对齐限制

因为直接 I/O 涉及到对磁盘设备的直接访问,所以在执行直接 I/O 时,必须要遵守以下三个对齐限制要求:

  • 应用程序中用于存放数据的缓冲区,其内存起始地址必须以块大小的整数倍进行对齐;
  • 写文件时,文件的位置偏移量必须是块大小的整数倍;
  • 写入到文件的数据大小必须是块大小的整数倍。

如果不满足以上任何一个要求,调用 write()均为以错误返回 Invalid argument。以上所说的块大小指的是磁盘设备的物理块大小(block size) ,常见的块大小包括 512 字节、 1024 字节、 2048 以及 4096 字节。

可以如下命令进行查看磁盘分区的块大小:

tune2fs -l /dev/sda | grep "Block size"

-l 后面指定了需要查看的磁盘分区,可以使用 df -h 命令查看 Ubuntu 系统的根文件系统所挂载的磁盘分区:

运行得到块大小:
 

6 stdio 缓冲

6.1 stdio 缓冲简介

标准 I/O(fopen、 fread、 fwrite、 fclose、 fseek 等)是 C 语言标准库函数, 而文件 I/O(open、 read、 write、close、 lseek 等)是系统调用,虽然标准 I/O 是在文件 I/O 基础上进行封装而实现, 但在效率、性能上标准 I/O 要优于文件 I/O,其原因在于标准 I/O 实现维护了自己的缓冲区, 我们把这个缓冲区称为 stdio 缓冲区。

前面提到了文件 I/O 内核缓冲,这是由内核维护的缓冲区,而标准 I/O 所维护的 stdio 缓冲是用户空间的缓冲区,当应用程序中通过标准 I/O 操作磁盘文件时,为了减少调用系统调用的次数,标准 I/O 函数会将用户写入或读取文件的数据缓存在 stdio 缓冲区,然后再一次性将 stdio 缓冲区中缓存的数据通过调用系统调用 I/O(文件 I/O)写入到文件 I/O 内核缓冲区或者拷贝到应用程序的 buf 中。通过这样的优化操作,当操作磁盘文件时,在用户空间缓存大块数据以减少调用系统调用的次数,使得效率、性能得到优化。 

6.2 标准 I/O 的 stdio 缓冲函数

C 语言提供了一些库函数可用于对标准 I/O 的 stdio 缓冲区进行相关的一些设置, 包括 setbuf()、setbuffer()以及 setvbuf()。

setvbuf() 函数设置缓冲区。

int setvbuf(FILE *stream, char *buf, int mode, size_t size);
  • stream:指向 FILE 结构的指针,表示要设置缓冲区的流。
  • buf:用户提供的缓冲区,或 NULL 以使用 stdio 的默认缓冲区。
  • mode:缓冲区模式,可以是 _IOFBF(全缓冲)、_IONBF(无缓冲)或 _IOLBF(行缓冲)。
  • size:缓冲区的大小。
  • 返回值: 成功返回 0,失败将返回一个非 0 值,并且会设置 errno 来指示错误原因。
无缓冲(_IONBF)在无缓冲模式下,每次写操作都会直接发送到输出设备,不经过任何内部缓冲区。
行缓冲(_IOLBF)行缓冲模式下,输出通常在遇到换行字符(\n)时被刷新到输出设备。
全缓冲(_IOFBF)全缓冲模式下,输出会被存储在缓冲区中,直到缓冲区满或者通过显式调用刷新缓冲区的函数(如fflush())时,才会将数据发送到输出设备。

setbuf() 函数:函数允许指定一个自定义的缓冲区,如果提供了有效的 buf 指针,将使用这个缓冲区进行数据缓冲。

void setbuf(FILE *stream, char *buf);
  • stream:指向 FILE 结构的指针,表示要设置缓冲区的输出流(如 stdout 或文件流)。
  • buf:指向字符数组的指针,用作流的缓冲区。如果 buf 为 NULL,则流变为无缓冲。

setbuffer() 函数setbuffer()函数类似于 setbuf(),但允许调用者指定 buf 缓冲区的大小

void setbuffer(FILE *stream, char *buf, size_t size);
  • stream:指向 FILE 结构的指针,表示要设置缓冲区的流。
  • buf:用户提供的缓冲区。
  • size:缓冲区的大小。

6.3 标准输出 printf()的行缓冲模式测试

我们先看看下面这个简单地示例代码,调用了 printf()函数,区别在于第二个 printf()没有输出换行符。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(void)
{printf("Hello World 1\n");printf("Hello World 2");for ( ; ; )sleep(1);
}

运行之后可以发现只有第一个 printf()打印的信息显示出来了,第二个并没有显示出来,这是为什么呢?

这就是 stdio 缓冲的问题,前面提到了标准输出默认采用的是行缓冲模式, printf()输出的字符串写入到了标准输出的 stdio 缓冲区中,只有输出换行符时(不考虑缓冲区填满的情况) 才会将这一行数据刷入到内核缓冲区。因为第一个 printf()包含了换行符,所以已经刷入了内核缓冲区,而第二个 printf 并没有包含换行符,所以第二个 printf 输出的"Hello World!"还缓存在 stdio 缓冲区中。

scanf()函数的行缓冲模式:格式化输入 scanf()函数通过键盘输入数据,只有在按下回车键(换行符键)时程序才会接着往下执行,因为标准输入默认也是采用了行缓冲模式。

6.4 将标准输出配置为无缓冲模式测试

修改上面的代码,使标准输出变成无缓冲模式,修改后代码如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{/* 将标准输出设置为无缓冲模式 */if (setvbuf(stdout, NULL, _IONBF, 0)) {perror("setvbuf error");exit(0);}printf("Hello World 1\n");printf("Hello World 2");for ( ; ; )sleep(1);
}

在使用 printf()之前,调用 setvbuf()函数将标准输出的 stdio 缓冲设置为无缓冲模式,可以发现该程序能够成功输出两个“Hello World!”。运行结果如下:

6.5 fflush()刷新 stdio 缓冲区

无论我们采取何种缓冲模式,在任何时候都可以使用库函数 fflush()来强制刷新stdio 缓冲区, 该函数会刷新指定文件的 stdio 输出缓冲区,此函数原型如下所示:

int fflush(FILE *stream);

参数: stream 指定需要进行强制刷新的文件,如果该参数设置为 NULL,则表示刷新所有的 stdio 缓冲区。
返回值:函数调用成功返回 0,否则将返回-1,并设置 errno 以指示错误原因。

进一步修改上面的代码,在第二个 printf 后面调用 fflush()函数,修改后代码如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(void)
{printf("Hello World 1\n");printf("Hello World 2");fflush(stdout); //刷新标准输出 stdio 缓冲区for ( ; ; )sleep(1);
}

可以看到,打印了两次“Hello World”, 这就是 fflush()的作用了强制刷新 stdio 缓冲区。运行结果如下:

6.6 关闭与退出时刷新 stdio 缓冲区

当文件关闭时、程序退出时,也会自动刷新 stdio 缓冲区。修改上面的代码,在调用第二个 printf 函数后关闭标准输出,如下所示:

// 关闭文件时刷新 stdio 缓冲区
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(void)
{printf("Hello World!\n");printf("Hello World!");fclose(stdout); //关闭标准输出for ( ; ; )sleep(1);
}

程序退出时也会自动刷新 stdio 缓冲区,修改上面的代码,去掉 for 死循环,让程序结束,修改完之后如下所示:

// 程序结束时刷新 stdio 缓冲区
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(void)
{printf("Hello World!\n");printf("Hello World!");
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/849547.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(深度学习记录)第TR4周:Pytorch复现Transformer

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 &#x1f3e1;我的环境&#xff1a; 语言环境&#xff1a;Python3.11.4编译器&#xff1a;Jupyter Notebooktorcch版本&#xff1a;2.0.…

fastadmin/thinkPHPQueue消息队列详细教程

thinkphp-queue 是thinkphp 官方提供的一个消息队列服务,它支持消息队列的一些基本特性: 消息的发布,获取,执行,删除,重发,失败处理,延迟执行,超时控制等队列的多队列, 内存限制 ,启动,停止,守护等消息队列可降级为同步执行1、通过composer安装thinkPHP消息队列 …

Java概述 , Java环境安装 , 第一个Hello World

环境变量,HelloWorld 1.会常用的dos命令 2.会安装java所需要的环境(jdk) 3.会配置java的环境变量 4.知道java开发三步骤 5.会java的入门程序(HelloWorld) 6.会三种注释方式 7.知道Java入门程序所需要注意的地方 8.知道println和print的区别第一章 Java概述 1.1 JavaSE体系介绍…

社区服务支持

社区服务支持 原创 小王搬运工 时序课堂 2024-06-07 19:29 四川 &#x1f31f; 邀请函 | 加入我们的时序数据挖掘社区 &#x1f680; 尊敬的数据爱好者们&#xff0c; 我们诚挚地邀请您加入我们的专业社区——时序数据挖掘社区&#xff0c;一个专注于时序数据分析、挖掘与应…

阿里通义千问,彻底爆了!(本地部署+实测)

点击“终码一生”&#xff0c;关注&#xff0c;置顶公众号 每日技术干货&#xff0c;第一时间送达&#xff01; 问大家一个问题&#xff1a;你是否想过在自己的电脑上部署一套大模型&#xff1f;并用自己的知识库训练他&#xff1f; 阿里通义千问今天发布了最新的开源大模型系…

Docker基础篇之Docker常规软件安装

文章目录 1. 总体步骤2. 安装tomcat3. 安装Mysql4. 安装Redis 1. 总体步骤 安装软件的总体步骤如下所示&#xff1a; 搜索镜像拉取镜像查看镜像启动镜像停止容器移除容器 2. 安装tomcat docker hub上查找tomcat镜像 或者使用一下命令查找&#xff1a; docker search tomca…

Open AI又出王炸GPT-4,目测一大波人的饭碗要碎了...

前言 在科技的惊涛骇浪中&#xff0c;每一次技术的飞跃都预示着新时代的曙光。近日&#xff0c;Open AI公司再次震撼业界&#xff0c;推出了其最新力作——GPT-4&#xff0c;这款被誉为“王炸”的语言模型&#xff0c;以其前所未有的智能水平和创造力&#xff0c;不仅在技术圈…

echarts学习:如何让y轴显示为带箭头的直线

前言 在我的固有印象中折线图、柱状图等图形中的y轴&#xff0c;应该都是像下图一样是一条末端带箭头的直线。 然而在echarts中y轴的轴线是默认不显示的&#xff08;这让我很不习惯&#xff09;&#xff0c;因此也就不会显示轴线末端的箭头。 1.让y轴的轴线显示出来 通过将yA…

wx小程序自定义tabbar

1.在app.json文件中&#xff0c;添加自定义tabbar配置&#xff1a;"custom": true "tabBar": {"custom": true,"backgroundColor": "#fafafa","borderStyle": "white","selectedColor": &quo…

5 分钟内构建一个简单的基于 Python 的 GAN

文章目录 一、说明二、代码三、训练四、后记 一、说明 生成对抗网络&#xff08;GAN&#xff09;因其能力而在学术界引起轩然大波。机器能够创作出新颖、富有灵感的作品&#xff0c;这让每个人都感到敬畏和恐惧。因此&#xff0c;人们开始好奇&#xff0c;如何构建一个这样的网…

计算机网络面试基础(一)

文章目录 一、HTTP基本概念1.HTTP是什么&#xff1f;2.HTTP 常见的状态码有哪些&#xff1f;3.http常见字段 二、GET和POST1.get和post有什么区别 三、HTTP缓存技术1.HTTP 缓存有哪些实现方式&#xff1f;2.什么是强制缓存&#xff1f;3.什么是协商缓存&#xff1f;(不太懂) 四…

长文预警:九头蛇的进化——Tesla AutoPilot 纯视觉方案解析

九头蛇的进化&#xff1a;Tesla AutoPilot 纯视觉方案解析 前言 本文整理自原文链接&#xff0c;写的非常好&#xff0c;给了博主很多启发&#xff0c;投原创是因为平台机制&#xff0c;希望能被更多人看到。 嘿嘿&#xff0c;漫威粉不要打我←_←不是Hail Hydra&#xff0c…

分享:各种原理测厚仪的发展历程!

板材厚度的检测离不开测厚仪的应用&#xff0c;目前激光测厚仪、射线测厚仪、超声波测厚仪等都已被广泛的应用于板材生产线中&#xff0c;那你了解他们各自的发展历程吗&#xff1f; 激光测厚仪的发展&#xff1a; 激光测厚仪是随着激光技术和CCD&#xff08;电荷耦合器件&…

swaggerHole:针对swaggerHub的公共API安全扫描工具

关于swaggerHole swaggerHole是一款针对swaggerHub的API安全扫描工具&#xff0c;该工具基于纯Python 3开发&#xff0c;可以帮助广大研究人员检索swaggerHub上公共API的相关敏感信息&#xff0c;整个任务过程均以自动化形式实现&#xff0c;且具备多线程特性和管道模式。 工具…

网络安全实验BUAA-全套实验报告打包

下面是部分BUAA网络安全实验✅的实验内容 &#xff1a; 认识路由器、交换机。掌握路由器配置的基本指令。掌握正确配置路由器的方法&#xff0c;使网络正常工作。 本博客包括网络安全课程所有的实验报告&#xff1a;内容详细&#xff0c;一次下载打包 实验1-路由器配置实验2-AP…

快速搭建高效运营体系,Xinstall App下载自动绑定助您一臂之力

在互联网的浪潮中&#xff0c;App的推广与运营面临着诸多挑战。如何在多变的互联网环境下迅速搭建起能时刻满足用户需求的运营体系&#xff0c;成为了众多企业关注的焦点。今天&#xff0c;我们就来聊聊如何通过Xinstall的App下载自动绑定功能&#xff0c;轻松解决App推广与运营…

PXE、无人值守实验

PXE部署 [roottest2 ~]# systemctl stop firewalld [roottest2 ~]# setenforce 0一、部署tftp服务 [roottest2 ~]# yum -y install tftp-server.x86_64 xinetd.x86_64 [roottest2 ~]# systemctl start tftp [roottest2 ~]# systemctl enable tftp [roottest2 ~]# systemctl …

因为宇宙一片漆黑,所以地球才有昼夜之分,宇宙为什么是黑的?

因为宇宙一片漆黑&#xff0c;所以地球才有昼夜之分&#xff0c;宇宙为什么是黑的&#xff1f; 地球为何会有昼夜之分&#xff1f; 乍一看&#xff0c;这个问题很是简单&#xff0c;当然是因为地球一直在自转了&#xff0c;当地球的一部分被太阳照射时就是白昼&#xff0c;而…

UI框架与MVC模式详解(1)——逻辑与数据分离

【效率最高的耦合方式】 以实际的例子来说明&#xff0c;更容易理解些。 这里从上到下&#xff0c;从左到右共有8个显示项&#xff0c;如果只需要显示这8个&#xff0c;不会做任何改变&#xff0c;数据固定&#xff0c;那么我们只需要最常规的思路去写就好&#xff0c;这是最…

【JSP】如何在IDEA上部署JSP WEB开发项目

以我的课设为例&#xff0c;教大家拿到他人的项目后&#xff0c;如何在IDEA上部署。 需要准备&#xff1a; JDK17&#xff08;或者JDK13&#xff09;IntelliJ IDEA 2023.2.6MySQL 8.0Tomcat 9.0 一&#xff0c;新建项目添加文件 1.1复制“位置”的路径 1.2找到该文件夹 1.3…