【Linux】带你了解高级IO

news2024/12/26 11:33:32

​🌠 作者:@阿亮joy.
🎆专栏:《学会Linux》
🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根
在这里插入图片描述

目录

    • 👉IO的基本概念👈
    • 👉五种IO模型👈
    • 👉高级IO重要概念👈
      • 同步通信 VS 异步通信
      • 阻塞IO VS 非阻塞IO
    • 👉其他高级IO👈
    • 👉非阻塞IO👈
    • 👉总结👈

👉IO的基本概念👈

什么是 IO

IO,全称 Input / Output,是计算机系统中的一种重要的操作,指的是系统的输入输出操作。简单来说,就是计算机系统与外界设备(如硬盘、键盘、鼠标、网络等)之间的数据交换过程。

IO 的分类

IO 通常分为阻塞 IO、非阻塞 IO、信号驱动 IO、多路复用 IO 和异步 IO 四种方式。

  • 阻塞 IO(Blocked IO):在阻塞 IO 模型下,当应用程序发起 IO 请求后,应用程序会一直阻塞等待,直到操作系统完成IO操作并将结果返回给应用程序。

  • 非阻塞 IO (Non-Blocked IO):在非阻塞 IO 模型下,当应用程序发起 IO 请求后,应用程序可以继续执行其他操作,而不需要等待操作系统完成 IO 操作。应用程序需要不断地轮询 IO 操作是否完成,这样会带来额外的 CPU 消耗。

  • 多路复用 IO(IO Multiplexing):在多路复用 IO 模型下,应用程序通过调用 select / poll / epoll 等函数,将多个 IO 操作注册到一个函数中,然后等待这些 IO 操作中的任意一个操作完成后,再去处理已完成的 IO 操作。

  • 信号驱动IO(Signal Driven IO):在信号驱动 IO 模型下,当应用程序发起 IO 请求后,应用程序会给操作系统注册一个信号处理函数,然后应用程序继续执行其他操作,当操作系统完成 IO 操作后,操作系统会向应用程序发送一个信号,然后应用程序在信号处理函数中处理已完成的 IO 操作。

  • 异步 IO(Asynchronous IO):在异步 IO 模型下,应用程序发起 IO 请求后,应用程序继续执行其他操作,当操作系统完成 IO 操作后,操作系统会向应用程序发送一个通知,通知应用程序已完成 IO 操作,并将结果返回给应用程序。

网络通信的本质就是 IO

进行网络通信的双方从网络中获取数据或将数据发送到网络中,本质就是输入和输出数据,也就是 IO,根据冯诺依曼体系就是将数据从内存中的数据放到网卡或从网卡中读取数据。

IO 的效率问题

IO 操作对系统的性能和效率有着很大的影响,因为 IO 操作需要涉及到磁盘或网络等慢速设备的读写,而这些操作会比 CPU 计算要慢得多。因此,如果 IO 操作不能高效地完成,就会导致应用程序的性能下降,甚至出现瓶颈。

那为什么 IO 操作就抵消呢?以读取数据(Input)为例:

  • 当调用 read / recv 函数时,如果底层缓冲区中没有数据,那么进程就会阻塞在 recv / recv 函数中,也就是等缓冲区中有数据。
  • 当调用 read / recv 函数时,如果底层缓冲区中有数据。那么今后才能就会将数据拷贝到应用层缓冲区中。

所以,IO操作 就等于 “等” 加 “数据拷贝”。read、recv、write 和 send 等 IO 函数需要等 IO 时间就绪,IO 时间就绪后就可以进行数据拷贝了。在实际的应用场景中,“等” 消耗的时间往往比 “数据拷贝” 消耗的时间要多得多,因此想让 IO 变得高效,最重要的就是让 “等” 的时间尽量减少。

👉五种IO模型👈

钓鱼的过程和 IO 的过程是非常地类似的,我们那通过一个钓鱼的例子来引出五种 IO 模型。

  • 钓鱼的过程同样分为 “等” 和 “数据拷贝” 两个步骤,这里的 “等” 就是等于上钩,“数据拷贝” 就是将上钩的鱼从河里放入到我们的桶里。
  • 进行 IO 操作时,“等” 的时间往往要比 “数据拷贝” 的时间长,钓鱼恰好也是符合这个特点。一个人在钓鱼的时候,大部分的时间都是在等鱼上钩,而将鱼从河里钓上来放到桶里只是十几秒钟的时间。
  • 那么在单位时间内,一个人等待鱼上钩所占的时间比例越低,那么这个人的钓鱼效率就越高。

钓鱼五人组

  • 张三:拿着一根鱼竿,将鱼钩抛入到河里就死死地盯住鱼漂,在这个过程中,他什么都不做,直到有鱼上钩才挥动鱼竿将鱼钓上来。
  • 李四:拿着一根鱼竿,将鱼钩抛入到河里就去做其他事情了,然后定期地观察鱼漂,如果有鱼上钩就将其钓上来,如果没有就继续去做其他时间。
  • 王五:在鱼竿的顶部放一个铃铛,将鱼钩抛入到河里然后去做其他的事情。如果铃铛响了,则说明有鱼上钩了就挥动鱼竿将它钓上来;否则就继续干其他事根本需要管鱼竿。
  • 赵六:拿着一百根鱼竿,将一百个鱼钩都抛入到河里,然后就观察鱼漂。只要鱼漂懂了,就说明有鱼上钩了则挥动鱼竿将鱼钓上来。
  • 田七:田七是个有钱的老总,他有个助手,他给了自己的助手一个手机、一个电话和一个桶,说等桶中装满了鱼就打电话告诉我来拿鱼。说完,田七开着自己的车子去干别的事情了。

以上五个人的钓鱼方式就对应着五中 IO 模型。

  • 张三:阻塞式 IO
  • 李四:非阻塞轮询式 IO
  • 王五:信号驱动式 IO
  • 赵六:多路复用 / 多路转接
  • 田七:异步 IO

谁的钓鱼效率最高呢?

赵六毫无疑问是五个人中钓鱼效率最高的,因为赵六有一百根鱼竿,可以同时等多根鱼竿有鱼上钩。也就是说,在单位时间内,鱼咬赵六鱼钩的概率更大,那么他的调用效率也就更高了。也就说明了,在五种 IO 模型中,多路复用的 IO 效率是最高的。

  • 阻塞 IO

在这里插入图片描述

  • 非阻塞IO:如果内核还未将数据准备好,系统调用仍然会直接返回,并且返回 EWOULDBLOCK 错误码。

在这里插入图片描述
非阻塞 IO 往往需要程序员循环的方式反复尝试读写文件描述符,这个过程称为轮询,这对 CPU 来说是较大的浪费,一般只有特定场景下才使用。

  • 信号驱动 IO:内核将数据准备好的时候,使用 SIGIO 信号通知应用程序进行 IO 操作。

在这里插入图片描述

  • IO 多路转接:虽然从流程图上看起来和阻塞 IO 类似。实际上最核心在于 IO 多路转接能够同时等待多个文件描述符的就绪状态。

在这里插入图片描述

select 函数可以同时等多个文件描述符,当这些文件描述符没有就绪,select 函数也会阻塞。当文件描述符就绪时,就会通知上层,一个个地将就绪文件描述符中的数据拷贝上来,那么调用 recvfrom 拷贝数据时就不会被阻塞了。

  • 异步 IO:当内核拷贝数据到用户层的缓冲区时,通知应用程序(而信号驱动是告诉应用程序何时可以开始拷贝数据)。

在这里插入图片描述

任何 IO 过程中,都包含两个步骤:第一是等待,第二是拷贝。而且在实际的应用场景中,等待消耗的时间往往都远远高于拷贝的时间。让 IO 更高效,最核心的办法就是让等待的时间尽量少。

👉高级IO重要概念👈

同步通信 VS 异步通信

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

  • 所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由调用者主动等待这个调用的结果。
  • 异步则是相反, 调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后, 被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。

IO 的本质是 “等” 加 “数据拷贝”,那么同步 IO 就是要么参与 “等” 的过程,要么参与 “数据拷贝” 的过程或者要么两个过程都参与。

显而易见的是,张三、李四和王五的钓鱼方式都是同步 IO,那王五的钓鱼方式是同步 IO 还是异步 IO 呢?其实王五的调鱼方式是同步 IO,尽管王五可以通过铃铛是否响了来判断是否有鱼上钩,不需要观察鱼漂的状态,但是王五在有鱼上钩的时候也需要将鱼从河里钓上来并放到同理。也就是说,尽管信号的产生是异步的,但是进程或线程也需要将内核缓冲区中的数据拷贝到应用层缓冲区中,也需要参与 IO 的过程,因此王五的钓鱼方式也是同步 IO。

在这里插入图片描述

同步通信 VS 同步与互斥

在学习多进程多线程的时候,也提到同步和互斥,但是这里的同步通信和进程之间的同步是完全不想干的概念。

  • 进程 / 线程同步是指,在保证临界资源安全的情况下,让线程 / 进程以某种顺序来访问临界资源,其谈论的是进程 / 线程之间直接的制约关系。
  • 而同步 IO 更多谈论的是主动参与 IO 的过程。

阻塞IO VS 非阻塞IO

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。

  • 阻塞 IO 和非阻塞 IO 都需要自己进行数据拷贝,它们的区别是等待的方式。
  • 阻塞调用是指调用结果返回之前,当前线程会被挂起,调用线程只有在得到结果之后才会返回。
  • 非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程,并通过轮询的方式来判断结果是否就绪。

👉其他高级IO👈

非阻塞 IO,纪录锁,系统 V 流机制, IO 多路转接(也叫 IO多路复用),readv 和 writev 函数以及存储映射 IO(mmap),这些统称为高级 IO。

  • 纪录锁(Record Lock)是一种用于在共享文件上同步进程之间访问的机制。当一个进程使用纪录锁时,它可以锁定文件中的一部分,以确保其他进程无法在此时修改该部分。当另一个进程尝试修改已被锁定的部分时,它将被阻塞,直到该部分被解锁。纪录锁还可以用于防止多个进程同时对文件进行写操作,从而保护数据的完整性和一致性。
  • 系统V流机制(System V Streams)是一种在 Unix 系统上实现的高级 IO 机制。它提供了一种可插拔的架构,可以方便地扩展各种不同类型的IO设备和驱动程序。Streams 还提供了一些高级功能,例如面向字符的 IO、流过滤器和流处理器等,以便更方便地处理数据。Streams 还提供了一种非常灵活的机制,可以用于在进程之间共享文件描述符。
  • readv 和 writev 函数是一组用于读取和写入数据的系统调用,它们可以在单个系统调用中读取或写入多个非连续缓冲区。这些函数可以减少数据复制和系统调用的数量,从而提高了 IO 性能。
  • 存储映射 IO(mmap)是一种将文件映射到进程的虚拟地址空间中的技术。这种技术可以使应用程序通过直接访问内存来读取和写入文件数据,从而避免了复制数据到缓冲区的过程。存储映射 IO 通常用于处理大型文件和数据库,因为它可以极大地提高 IO 性能。

存储映射 IO

fstat 函数是一个 POSIX 标准函数,用于获取指定文件的元数据(metadata),如文件大小、创建时间、最后修改时间等等。其函数原型如下:

int fstat(int fd, struct stat *buf);

其中,fd 是要查询的文件描述符,buf 是指向一个 struct stat 类型的结构体的指针,用于存储查询结果。

struct stat 结构体包含了文件的元数据信息,具体定义如下:

struct stat 
{
    dev_t     st_dev;         // 文件所在设备的ID
    ino_t     st_ino;         // 文件的Inode号
    mode_t    st_mode;        // 文件的类型和权限信息
    nlink_t   st_nlink;       // 文件的硬链接数
    uid_t     st_uid;         // 文件所有者的用户ID
    gid_t     st_gid;         // 文件所有者的组ID
    dev_t     st_rdev;        // 如果文件是一个特殊文件,则这是设备的ID
    off_t     st_size;        // 文件的大小(以字节为单位)
    blksize_t st_blksize;     // 文件系统用于I/O的最佳块大小
    blkcnt_t  st_blocks;      // 文件所使用的磁盘块数量
    time_t    st_atime;       // 文件的最后访问时间
    time_t    st_mtime;       // 文件的最后修改时间
    time_t    st_ctime;       // 文件的最后状态改变时间
};

调用 fstat 函数后,struct stat 结构体中的各个字段将被填充上文件的相关信息。如果函数调用成功,返回值为 0,否则返回 -1,并设置 errno 变量表示错误原因。

mmap 是 Unix 和类 Unix 系统提供的函数,其功能是将文件或其他对象的一段区域映射到调用进程的虚拟地址空间中。使用 mmap 函数可以在程序中直接访问文件内容,而无需进行 read 和 write 等系统调用。其函数原型如下:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

参数说明:

  • addr:映射区的开始地址,如果设为0,操作系统会自动分配地址。
  • length:映射区的长度。
  • prot:映射区的保护方式。可选值有:可读 PROT_READ,可写 PROT_WRITE 和可执行 PROT_EXEC。
  • flags:标志参数,用于控制映射区的各种属性。可选值有:共享映射区 MAP_SHARED,私有映射区 MAP_PRIVATE、指定映射区地址 MAP_FIXED 和匿名映射区 MAP_ANONYMOUS 等。
  • fd:要映射到内存的文件描述符。
  • offset:文件偏移量,表示从文件的哪个位置开始映射。

mmap 函数的返回值是映射区的起始地址。mmap 函数的主要用途是对大型文件进行高效的随机访问,它通常比传统的文件操作更快,因为它避免了在文件和内存之间频繁地进行数据传输。此外,它还可以用于创建共享内存区域、在内存中操作设备等。

使用 munmap 函数来释放这个映射区,其原型如下:

int munmap(void *addr, size_t length);

参数说明:

  • addr: 映射区的起始地址。
  • length: 映射区的长度。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

int main()
{
    struct stat sb;
    int fd = open("Test.txt", O_RDONLY);

    if (fd == -1)
    {
        perror("open");
        exit(EXIT_FAILURE); // 退出进程
    }

    // fstat函数可以获取文件的相关信息
    if (fstat(fd, &sb) == -1)
    {
        perror("fstat");
        exit(EXIT_FAILURE);
    }

    char* mapped = (char*)mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (mapped == MAP_FAILED)
    {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    for (size_t i = 0; i < sb.st_size; i++)
    {
        putchar(mapped[i]);
        usleep(750);
    }

    if (munmap(mapped, sb.st_size) == -1)
    {
        perror("munmap");
        exit(EXIT_FAILURE);
    }

    if (close(fd) == -1)
    {
        perror("close");
        exit(EXIT_FAILURE);
    }

    return 0;
}
# Test.txt
自由的旗帜在风中飘扬
梦想的火炬在心中燃烧
不管身处何方
心中的信仰

永远都是自由和理想
长路漫漫,艰难险阻
追逐梦想,不畏困苦
不屈不挠,向前奔跑
为了那片自由的天空

没有束缚的翅膀
自由飞翔在天上
不畏风雨,不惧迷途、
只要有梦想,就有希望

心中的理想,如同星光
永远照耀着前方
走过荆棘,跨越障碍
迎接着自由的曙光

让我们肩并肩,手牵手
一起去追逐自由和理想
不论前路多么险阻
只要我们相信,必能飞翔

在这里插入图片描述

👉非阻塞IO👈

打开文件时,默认是以阻塞的方式来打开的。如果要以非阻塞的方式来打开一个文件,那么调用 open 函数需要传入 O_NONBLOCK 或 O_NDELAY,此时就是以非阻塞的方式打开一个文件了。

在这里插入图片描述

这时以非阻塞的方式打开一个文件,如果一个文件已经以阻塞的方式打开了,那么想要将该文件设置成非阻塞的方式,就需要用到 fcntl 函数了。

fcntl 是一个 Unix / Linux系统下的函数,用于控制已打开的文件描述符的各种属性。它可以用于打开 / 关闭文件描述符、设置 / 获取文件描述符标记、设置 / 获取文件锁、以及调整文件描述符的各种属性等。

#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */);

其中,参数 fd 是文件描述符,cmd 是要执行的操作,后面的可选参数 arg 用于传递操作所需的参数。

fcntl 函数的常见操作包括:

  • 复制文件描述符:cmd 参数设置为 F_DUPFD 或F_DUPFD_CLOEXEC,将 fd 复制到一个新的文件描述符,返回新的文件描述符。
  • 获取 / 设置文件描述符标记:cmd 参数设置为 F_GETFD 或 F_SETFD,用于获取或设置文件描述符的 close-on-exec 标记,如果设置为 1,则在进程调用 exec 函数时自动关闭该文件描述符。

在这里插入图片描述

  • 获取 / 设置文件状态标记:cmd 参数设置为 F_GETFL 或 F_SETFL,用于获取或设置文件状态标记,包括非阻塞标记 O_NONBLOCK 和追加标记 O_APPEND 等。
  • 获取 / 设置文件锁:cmd 参数设置为 F_GETLK、F_SETLK 或 F_SETLKW,用于获取、设置或阻塞式设置文件锁,以控制多个进程对同一文件的访问。
  • 设置异步 IO:cmd 参数设置为 F_SETOWN 或 F_SETSIG,用于设置异步 IO 的通知方式,例如当文件描述符上有数据可读时,向进程发送信号或者向进程发送 SIGIO 信号。

fcntl 函数的返回值取决于具体的操作和参数。下面是一些可能的返回值及其含义:

  • 对于 F_DUPFD,返回的是新的文件描述符,表示复制已有的文件描述符。
  • 对于 F_GETFD ,返回的是当前文件描述符的文件标志 FD_CLOEXEC 或 0。
  • 对于 F_SETFD ,返回的是 0 表示成功,返回 -1 表示出错。
  • 对于 F_GETFL,返回的是当前文件描述符的文件状态标志。
  • 对于 F_SETFL 命令,返回的是 0 表示成功,返回 -1 表示出错。
  • 对于 F_GETOWN ,返回的是当前文件描述符的拥有者进程 ID 或进程组 ID。
  • 对于 F_SETOWN,返回的是 0 表示成功,返回 -1 表示出错。

fcntl 函数的返回值和具体操作和参数相关,需要仔细阅读相应的文档和错误码来确定其含义和处理方法。

复制文件描述符

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

int main() 
{
    int fd1 = open("test.txt", O_RDONLY | O_CREAT, 0664);  // 打开一个文件
    int fd2 = fcntl(fd1, F_DUPFD, 0);  // 复制文件描述符
    std::cout << "fd1: " << fd1 << std::endl;
    std::cout << "fd2: " << fd2 << std::endl;
    return 0;
}

F_DUPFD 参数的作用是复制一个文件描述符,并返回一个新的文件描述符。在上述代码中,我们打开了一个名为 “test.txt” 的文件,并将其文件描述符赋值给 fd1。我们通过调用 fcntl 函数并指定 F_DUPFD 参数以复制文件描述符,这将创建一个新的文件描述符 fd2,并使其与 fd1 指向相同的打开的文件。我们可以看到,fd1 和 fd2 的值是不同的,但它们都指向同一个打开的文件。

在 F_DUPFD 命令中,第三个参数是新的文件描述符的最小值。系统会选择一个大于等于第三个参数的最小未使用的文件描述符,并返回它的值。第三个参数为 0,这意味着系统将返回当前可用的最小文件描述符。

在这里插入图片描述

将文件描述符设置为非阻塞

基于 fcntl 函数,我们实现一个 SetNonBlock 函数,将文件描述符设置为非阻塞。

bool SetNonBlock(int fd)
{
    int fl =  fcntl(fd, F_GETFL); // 获取当前fd的读写标记位
    if(fl < 0)
    {
        perror("fcntl\n");
        return false;
    }
    fcntl(fd, F_SETFL, fl | O_NONBLOCK);
    return true;
}
  • 使用 F_GETFL 将当前的文件描述符的属性取出来(这是一个位图)。
  • 然后再使用 F_SETFL 将文件描述符设置回去,设置回去的同时加上一个 O_NONBLOCK 参数。

以非阻塞轮询的方式读取标准输入

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <cerrno>
#include <cstring>

using namespace std;
const int SIZE = 1024;

// 将文件描述符设置为非阻塞
void SetNonBlock(int fd)
{
    int fl =  fcntl(fd, F_GETFL); // 获取当前fd的读写标记位
    if(fl < 0)
    {
        perror("fcntl\n");
        return;
    }
    fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}

int main()
{
    char buffer[SIZE];
    SetNonBlock(STDIN_FILENO); // 将标准输入设置为非阻塞

    while(true)
    {
        sleep(1);
        errno = 0;
        ssize_t s = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
        if(s > 0)
        {
            buffer[s - 1] = '\0'; // 去除换行符
            cout << "echo# " << buffer << endl;
        }
        else
        {
            if(errno == EWOULDBLOCK || errno == EAGAIN)
            {
                cout << "标准输入仍未就绪!" << errno << " " << strerror(errno) << endl;
                continue;
            }
            else if(errno == EINTR)
            {
                cout << "标准输入被信号中断!" << errno << " " << strerror(errno) << endl;
                continue;
            }
            else
            {
                cout << "标准输入出现错误!" << errno << " " << strerror(errno) << endl;
                break;
            }
        }
    }

    return 0;
}

代码说明:

  • 当 read 函数以非阻塞的方式读取标准输入时,如果底层的数据没有就绪,read 函数就会以出错的方式立即返回,并将错误码设置为 EWOULDBLOCK 或 EAGAIN,以表示底层数据没有准备好。

在这里插入图片描述

  • 当程序调用一些慢系统调用(例如 read、write 和 accept 等)时,如果这些系统调用的执行时间很长,在系统调用执行的过程中,如果接收到了一个信号,那么系统会中断当前的系统调用,并且将 errno 设置为 EINTR,然后系统会将控制权返回给程序,让程序自己处理这个信号。

在这里插入图片描述

  • 当 read 函数的返回值为 -1 时,并不代表 read 函数在读取底层数据时出现了什么问题。当返回值为 -1 时,我们需要检查错误码,如果错误码为 EWOULDBLOCK 或 EAGAIN 时,说明底层数据没有就绪,可以再进行轮询检测底层数据是否就绪;如果错误码为 EINTR 时,说明 read 函数被信号中断了,此时需要进行信号处理,然后再进行轮询。

代码运行起来后,我们并没有输入数据,就会出现轮询检测底层数据是否就绪的情况了。

在这里插入图片描述
只要我们进行了数据输入,此时 read 函数就能够检测到数据的输入,并将数据从内核缓冲区拷贝到用户层缓冲区中去,然后再进行数据打印。

在这里插入图片描述

👉总结👈

本篇博客主要讲解了 IO 的基本概念、五种 IO 模型、高级 IO 的重要概念、其他高级 IO 以及非阻塞 IO 等等。

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

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

相关文章

【Rust】Rust学习 第七章使用包、Crate和模块管理不断增长的项目

目前为止&#xff0c;我们编写的程序都在一个文件的一个模块中。伴随着项目的增长&#xff0c;你可以通过将代码分解为多个模块和多个文件来组织代码。一个包可以包含多个二进制 crate 项和一个可选的 crate 库。伴随着包的增长&#xff0c;你可以将包中的部分代码提取出来&…

ArcGIS Pro实践技术应用暨基础入门、制图、空间分析、影像分析、三维建模、空间统计分析与建模、python融合、案例应用

GIS是利用电子计算机及其外部设备&#xff0c;采集、存储、分析和描述整个或部分地球表面与空间信息系统。简单地讲&#xff0c;它是在一定的地域内&#xff0c;将地理空间信息和 一些与该地域地理信息相关的属性信息结合起来&#xff0c;达到对地理和属性信息的综合管理。GIS的…

动力节点新版Docker实用教程,从入门到高阶一套搞定

Docker是一种轻量级的容器化平台&#xff0c;它可以大大简化应用程序的在不同环境中的部署、管理与扩展。 学习Docker可以让你更好地了解和应用容器化技术&#xff0c;实现软件运环境的快速部署和管理。 如何系统全面掌握Docker&#xff1f; 推荐一套天花板级Docker教程&#x…

前端页面--视觉差效果

代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><link rel"stylesheet" href"https://un…

Explorable Tone Mapping Operators

Abstract 色调映射在高动态范围(HDR)成像中起着至关重要的作用。 它的目的是在有限动态范围的介质中保存HDR图像的视觉信息。 虽然许多工作已经提出从HDR图像中提供色调映射结果&#xff0c;但大多数只能以一种预先设计的方式进行色调映射。 然而&#xff0c;声调映射质量的主…

lc1.两数之和

暴力解法&#xff1a;两个for循环&#xff0c;寻找和为target的两个数的索引 时间复杂度&#xff1a;O(n2) 空间复杂度&#xff1a;O(1) 哈希表&#xff1a;遍历数组&#xff0c;将nums数组的数和索引分别存储在map的key和value中&#xff0c;一边遍历&#xff0c;一边寻找是…

多语言多用户购物网站--海外仓进出口贸易平台开发

搭建一个多语言多用户购物网站需要具备一定的技术和资源&#xff0c;下面是一个大致的步骤&#xff1a; 1.确定需求&#xff1a;首先确定购物网站的功能需求&#xff0c;包括商品展示、购物车、订单管理、支付方式、物流管理等。同时还需要考虑到海外仓进出口贸易的特点&#…

如何使用Pycharm 快速搭建 Django 项目 (分享详细图文教程)

1. 准备工作 在开始创建Django项目之前&#xff0c;需要先确保已经安装了Python和Pycharm。并且python中已经安装好了Django依赖。 1安装python&#xff08;这里我安装使用的是python3.11.4稳定版本&#xff09; 官网下载太慢了这里直接贴网盘下载连接了&#xff0c;一起贴出py…

java版工程项目管理系统源码+系统管理+系统设置+项目管理+合同管理+二次开发em

​ 鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部…

解决vue3中不能使用vue-simple-uploader

vue-simple-uploader本身是基于vue2的&#xff0c;直接npm i vue-simple-uploader -S下载下来版本的是0.7.6。在vue3中无法使用会报错。 解决&#xff1a;使用next安装接下来要发布的版本就会下载1.0.1版本&#xff0c;即可使用vue3 npm i vue-simple-uploadernext -S 注意&…

基于Echarts的大数据可视化模板:智慧物流管理

目录 引言物流管理的重要性大数据可视化在解决物流管理挑战中的作用智慧物流概述定义智慧物流的概念和特点智慧物流的关键技术和平台风险管理和预测:交通拥堵情况和风险预警Echarts与大数据可视化Echarts库以及其在大数据可视化领域的应用优势开发过程和所选设计方案模板如何满…

企业计算机服务器中了locked勒索病毒怎么办,如何预防勒索病毒攻击

计算机服务器是企业的关键信息基础设备&#xff0c;随着计算机技术的不断发展&#xff0c;企业的计算机服务器也成为了众多勒索者的攻击目标&#xff0c;勒索病毒成为当下计算机服务器的主要攻击目标。近期&#xff0c;我们收到很多企业的求助&#xff0c;企业的服务器被locked…

linux程序保护机制gcc编译选项

预备知识&#xff1a; 计算机内存的结构通常包括以下几个主要部分&#xff1a; 1.代码段(Code Segment)&#xff1a;也称为文本段&#xff0c;存储程序的可执行指令。代码段是被标记为可执行的&#xff0c;程序从代码段中获取指令并执行。 2.数据段(Data Segment)&#xff1a…

聚焦智慧医疗-RK3566智能主板助力POCT领域

POCT又称即时检验&#xff0c;是IVD(体外诊断)行业的一个细分领域&#xff0c;是指在采样现场即刻进行的快速诊断&#xff0c;省去了标本在实验室检验的复杂处&#xff0c;利用便携式分析仪器及配套试剂快速得到检测结果的一种检测方式。常见的POCT设备有生化分析仪、免疫分析仪…

Netty的ByteToMessageDecoder分析

说明 io.netty.handler.codec.ByteToMessageDecoder是一个解码器&#xff0c;从字节数据转换为其它类型的数据。ByteToMessageDecoder内部有一个累加器&#xff0c;将收到的字节读出来累积到一个ByteBuf中。ByteToMessageDecoder是个抽象类型&#xff0c;其中抽象函数decode(C…

Adobe全家桶-Prelude 2022安装教程

软件介绍 Adobe Prelude简称Pl&#xff0c;是Adobe出品的一款专业级别的视频编辑软件。Adobe Prelude软件将卓越的性能、优美的改进用户界面和许多奇妙的创意功能结合在一起&#xff0c;包括用于动态时间轴裁切、扩展的多机编辑、调整图层等。可用于结构化或简化视频制作工作流…

侯捷 C++面向对象编程笔记——8 静态 模板 namespace

8.1 static 对于非静态的函数和数据&#xff1a; 非静态的成员函数通过this指针来处理不同的数据&#xff08;一份函数—>多个对象&#xff09; 对于静态的函数和数据&#xff1a; 静态函数没有this&#xff0c;不能处理一般的数据&#xff0c;只能处理静态的数据 例1&a…

webshell免杀项目-Webshell_Generate(四)

用于生成各类免杀webshell 该工具没什么技术含量&#xff0c;学了一点javafx&#xff0c;使用jdk8开发出了几个简单功能用来管理webshell。页面比较low。 工具整合并改写了各类webshell&#xff0c;支持各个语言的cmd、蚁剑、冰蝎、哥斯拉&#xff0c;又添加了实际中应用到的一…

【Opencv入门到项目实战】(七):图像轮廓检测

所有订阅专栏的同学可以私信博主获取源码文件 文章目录 引言1.​轮廓检测2.轮廓特征3.轮廓近似4.外接矩形总结 引言 这一篇文章我们来讨论图像轮廓相关的知识点&#xff0c;什么叫做轮廓&#xff0c;从定义上来说它是指图像中连续的曲线或边界&#xff0c;表示了图像中目标的…

h5高德地图定位与Audio标签冲突

看到此文说明你也遇见此问题了&#xff0c;在ios设备使用(高德地图、百度地图)定位&#xff0c;如果在页面中有audio标签则会出现定位失败要么就是音频无法播放&#xff0c;此问题已和官方证实 是冲突问题&#xff0c;暂无解决方案。 因为高德地图也是基于原生定位开发的&#…