当前位置: 首页 > news >正文

【探索Linux】—— 强大的命令行工具 P.14(进程间通信 | 匿名管道 | |进程池 | pipe() 函数 | mkfifo() 函数)

在这里插入图片描述

阅读导航

  • 引言
  • 一、进程间通信概念
  • 二、进程间通信目的
  • 三、进程间通信分类
  • 四、管道
    • 1. 什么是管道
    • 2. 匿名管道
      • (1)创建和关闭
        • ⭕pipe() 函数
        • ⭕创建匿名管道
        • ⭕关闭匿名管道
      • (2)通信方式
      • (3)用法示例
      • (4)匿名管道的特点
    • 3. 运用匿名管道建立进程池
    • 4. 命名管道
      • (1)创建和关闭
        • ⭕mkfifo() 函数
        • ⭕创建命名管道
        • ⭕关闭命名管道
      • (2)通信方式
      • (3)用法示例
      • (4)命名管道的特点
    • 5. 匿名管道与命名管道的区别
      • 1. 匿名管道:
      • 2. 命名管道:
  • 温馨提示

引言

当今计算机系统中,进程间通信扮演着至关重要的角色。随着计算机系统的发展和复杂性的增加,多个进程之间的协作变得更加必要和常见。进程间通信使得不同进程能够共享资源、协调工作、传输数据,并实现更加复杂和强大的功能。本文将深入探讨进程间的通信,以及管道的作用。它为多个进程提供了一种有效的交互方式,使得系统能够更好地协同工作、共享资源,并实现更高级别的功能。通过恰当地选择和使用进程间通信的方式,我们可以构建出高效、可靠且高度协同的系统。下面话不多说坐稳扶好咱们要开车了😍

一、进程间通信概念

进程间通信(IPC)是操作系统中的一个重要概念,它允许不同的进程在执行过程中交换数据、共享资源、协调行为等。在多道程序设计环境下,多个进程可能需要相互通信以完成复杂的任务,而进程间通信提供了各种机制来实现这种交互

二、进程间通信目的

🍔进程间通信的主要目的包括:

  1. 数据交换:允许进程之间传递数据,比如传输文件、文本、图像等信息。
  2. 资源共享:多个进程可以访问和共享同一块内存区域,以便协同完成某项任务。
  3. 进程控制:允许一个进程控制另一个进程的行为,比如启动、暂停、终止等。
  4. 同步与互斥:确保多个进程能够按照特定的顺序执行,避免竞态条件和数据冲突。
  5. 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。

三、进程间通信分类

🍁以下是几种常见的进程间通信方式:

  1. 管道(Pipe):管道是一种半双工的通信方式,用于具有亲缘关系的进程间通信。它可以是匿名管道(使用pipe系统调用)或命名管道(使用mkfifo命令),并且数据只能在一个方向上流动。

  2. 信号(Signal):信号是一种异步的通信机制,用于通知进程发生了某种事件。进程可以向另一个进程发送信号,比如终止信号(SIGTERM)、中断信号(SIGINT)等。

  3. 消息队列(Message Queue):消息队列是一种消息传递机制,可以在不同进程之间按队列方式传递数据。它允许一个进程向另一个进程发送消息,而不需要直接的数据连接。

  4. 共享内存(Shared Memory):共享内存允许多个进程访问同一块物理内存,因此它是最快的 IPC 方式之一。但需要开发者自行解决竞争条件和同步的问题。

  5. 信号量(Semaphores):信号量是一种计数器,用于控制对共享资源的访问。它通常与共享内存一起使用,以避免多个进程同时访问共享内存时产生的竞争条件。

  6. 套接字(Socket):套接字是一种进程间通信的常见方式,可以用于不同主机之间的通信,也可以用于同一主机上不同进程之间的通信。

四、管道

1. 什么是管道

管道(Pipe)是一种在UNIX和类UNIX系统中用于进程间通信的机制。管道允许一个进程将其输出直接发送到另一个进程的输入,从而实现两个进程之间的数据传输

🍪管道的特点包括:

  • 管道是一种半双工的通信方式,数据只能在一个方向上流动
  • 管道通常用于实现父子进程之间的通信,例如一个进程的输出连接到另一个进程的输入,实现数据传递和处理。
  • 管道的数据是以先进先出(FIFO)的方式传输的,保持了数据的顺序性

在这里插入图片描述

2. 匿名管道

(1)创建和关闭

⭕pipe() 函数

在Linux系统中,pipe()函数用于创建匿名管道,它是一个系统调用函数,位于<unistd.h>头文件中。该函数创建一个管道,返回两个文件描述符,一个用于读取数据,另一个用于写入数据。

语法

#include <unistd.h>int pipe(int pipefd[2]);

参数

  • pipefd: 一个整型数组,用于存储管道的文件描述符。pipefd[0]用于从管道中读取数据,pipefd[1]用于向管道中写入数据。

返回值

  • 若成功,返回值为0;若失败,返回值为-1,并设置errno来指示错误类型。
⭕创建匿名管道
  1. 使用pipe()系统调用来创建匿名管道。pipe()系统调用会创建一个管道,返回两个文件描述符,一个用于读取数据,另一个用于写入数据。
  2. 在Linux系统中,可以通过命令行工具或者编程语言来使用pipe()系统调用创建匿名管道。
  3. 以下是使用C语言创建匿名管道的示例代码:
#include <unistd.h>int main() {int pipefd[2];if (pipe(pipefd) == -1) {// 处理创建失败的情况}// 现在pipefd[0]是用于读取的文件描述符,pipefd[1]是用于写入的文件描述符
}
⭕关闭匿名管道
  1. 匿名管道的关闭通常由操作系统自动处理,当所有指向管道的文件描述符都关闭时,操作系统会自动关闭管道。
  2. 在编程中,可以通过close()系统调用显式地关闭管道的读取端或写入端。
  3. 以下是使用C语言关闭匿名管道的示例代码:
#include <unistd.h>int main() {int pipefd[2];if (pipe(pipefd) == -1) {// 处理创建失败的情况}// 在适当的时机关闭管道close(pipefd[0]); // 关闭读取端close(pipefd[1]); // 关闭写入端
}

在这里插入图片描述

(2)通信方式

父子进程之间的通信:父子进程可以通过匿名管道进行通信。通常的做法是在调用fork()之后,子进程继承了父进程的文件描述符,包括管道。子进程可以关闭不需要的文件描述符,然后使用write()函数向管道中写入数据,父进程则使用read()函数从管道中读取数据。

兄弟进程之间的通信:兄弟进程之间也可以通过匿名管道进行通信。通常的做法是在调用pipe()和fork()之后,子进程再次调用fork()创建兄弟进程。然后兄弟进程可以通过管道进行通信,一个进程负责写入,另一个进程负责读取。

(3)用法示例

  • 在Shell脚本中,可以使用管道将一个命令的输出传递给另一个命令进行处理,比如command1 | command2
  • 在C语言或其他编程语言中,可以通过创建管道来实现父子进程之间的通信,或者在多个兄弟进程之间进行数据交换(后面进程池会细讲示例)。

(4)匿名管道的特点

  1. 阻塞式读写:

    • 当管道读取端为空时,尝试从管道中读取数据的进程将会被阻塞,直到有数据可供读取为止。读取端的进程会等待直到管道中有数据可用,或者直到收到信号中断。
    • 当管道写入端已满时,尝试向管道中写入数据的进程将会被阻塞,直到有足够的空间可以写入为止。这个时候,写入端的进程会等待直到管道中有足够的空间,或者直到收到信号中断。
  2. 数据顺序性:

    • 匿名管道保证数据的顺序性,数据是以先进先出(FIFO)的方式传输的,从而保持了数据的顺序性。
  3. 局限性:

    • 匿名管道通常适用于具有亲缘关系的进程间通信,无法用于无亲缘关系的进程间通信
    • 匿名管道只能在本地进程间通信,无法用于远程通信。
  4. 单向通信:匿名管道是一种单向通信机制,数据只能在一个方向上传输。其中一个进程负责写入数据,而另一个进程负责读取数据。这使得匿名管道适用于一些特定的通信场景,如父子进程或者兄弟进程之间的通信。

  5. 半双工通信:匿名管道是半双工的,意味着它可以在两个进程之间进行双向通信,但是不能同时进行读和写操作。虽然它可以实现双向通信,但是在任意给定的时间点,数据只能在一个方向上传输。

  6. 自动关闭:当所有指向管道的文件描述符全部关闭时,操作系统会自动关闭管道。这样做可以确保在程序结束时释放资源,并且不会造成资源泄漏。

3. 运用匿名管道建立进程池

#include <iostream>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctime>
#include <cstdlib>
#include <cassert>
#define PROCESS_NUM 5  // 定义常量 PROCESS_NUM 为 5using namespace std;// 从文件描述符 waitFd 中读取命令
int waitCommand(int waitFd, bool &quit) {uint32_t command = 0;ssize_t s = read(waitFd, &command, sizeof(command));  // 从文件描述符中读取命令if (s == 0) {  // 如果成功读取到命令quit = true;  // 标记为需要退出return -1;}assert(s == sizeof(uint32_t));  // 断言读取的字节数与命令长度相等return command;  // 返回读取到的命令
}// 向指定的进程发送命令,并在标准输出中打印相关信息
void sendAndWakeup(pid_t who, int fd, uint32_t command) {write(fd, &command, sizeof(command));  // 向文件描述符中写入命令cout << "main process: call process " << who << " execute " << desc[command] << " through " << fd << endl;  // 打印相关信息
}int main()
{load(); // 加载一些内容vector<pair<pid_t, int>> slots; // 用于保存子进程的PID和管道写端文件描述符// 先创建多个进程for (int i = 0; i < PROCESS_NUM; i++){int pipefd[2] = {0};assert(pipe(pipefd) == 0); // 创建管道并检查是否成功pid_t id = fork();assert(id != -1); // 检查fork()是否成功if (id == 0) // 子进程逻辑{close(pipefd[1]); // 关闭写端while (true){bool quit = false;int command = waitCommand(pipefd[0], quit); // 等待命令if (quit)break; // 如果收到退出命令,则退出循环if (command >= 0 && command < handlerSize()){dummyHandler(); // 执行对应的命令处理函数}else{cout << "非法command: " << command << endl;}}exit(1);}else // 父进程逻辑{close(pipefd[0]); // 关闭子进程的读端slots.push_back(pair<pid_t, int>(id, pipefd[1])); // 保存子进程的PID和写端管道文件描述符}}// 父进程派发任务srand((unsigned long)time(nullptr) ^ getpid() ^ 23323123123L); // 设置随机数种子while (true){int command = rand() % handlerSize(); // 随机选择一个任务int choice = rand() % slots.size(); // 随机选择一个子进程sendAndWakeup(slots[choice].first, slots[choice].second, command); // 向选定的子进程发送任务sleep(1); // 休眠一秒}// 关闭fd, 所有的子进程都会退出for (const auto &slot : slots){close(slot.second); // 关闭所有子进程的写端}// 回收所有的子进程信息for (const auto &slot : slots){waitpid(slot.first, nullptr, 0); // 回收子进程}
}

这段代码是一个简单的进程调度和通信示例,它创建了多个子进程,并使用管道进行进程间通信,父进程通过随机选择一个子进程来派发任务。

  1. 创建多个子进程:

    • 使用 fork() 函数创建子进程,并使用 pipe() 函数创建管道用于进程间通信。
    • 父进程将每个子进程的 PID 和写端管道文件描述符保存在 slots 向量中。
  2. 子进程逻辑:

    • 子进程关闭写端,然后进入一个无限循环,不断等待命令并执行。
    • 当收到命令时,执行对应的命令处理函数,如果收到退出命令则退出循环并终止子进程。
  3. 父进程逻辑:

    • 通过 srand() 来初始化随机数种子,使得每次运行产生的随机数不同。
    • 进入一个无限循环,随机选择一个任务和一个子进程,然后将任务发送给选定的子进程。
    • 每次发送完任务后,休眠一秒钟。
  4. 最后,父进程关闭所有子进程的写端,然后回收所有子进程的信息。

4. 命名管道

(1)创建和关闭

⭕mkfifo() 函数

mkfifo() 函数用于创建一个FIFO(First In First Out)或者称为命名管道,它允许进程之间进行通信。下面是关于 mkfifo() 函数的详细介绍:

函数原型

#include <sys/types.h>
#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);

参数

  • pathname:要创建的命名管道的路径名。
  • mode:创建命名管道时设置的权限模式,通常以 8 进制表示,比如 0666

返回值

  • 若成功,返回值为 0;若失败,返回值为 -1,并设置errno来指示错误类型。

功能
mkfifo() 函数的作用是在文件系统中创建一个特殊类型的文件,该文件在外观上类似于普通文件,但实际上是一个FIFO,用于进程之间的通信。这种通信方式是单向的,即数据写入FIFO的一端,可以从另一端读取出来,按照先进先出的顺序。

⭕创建命名管道
  1. 包含头文件:首先需要包含相关的头文件,以便使用相关函数和数据结构。

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
  2. 调用 mkfifo() 函数:使用 mkfifo() 函数创建命名管道。该函数原型如下:

    int mkfifo(const char *pathname, mode_t mode);
    
    • pathname:要创建的命名管道的路径名。
    • mode:创建命名管道时设置的权限模式,通常以 8 进制表示,比如 0666

    示例代码:

    std::string fifoPath = "/tmp/my_named_pipe";  // 命名管道的路径名
    mkfifo(fifoPath.c_str(), 0666); // 创建权限为0666的命名管道
    
  3. 处理返回值:检查 mkfifo() 的返回值,若返回 0 表示成功创建,若返回 -1 表示创建失败,并通过 errno 来获取具体的错误信息。

🚨注意事项

  • 路径名:确保要创建的命名管道路径名合法且没有重复。
  • 权限模式:根据实际需求设置合适的权限模式,确保可被需要访问该管道的进程所访问。
  • 错误处理:对 mkfifo() 函数的返回值进行适当的错误处理,根据具体的错误原因进行相应的处理和日志记录。
  1. 示例
    下面是一个简单的创建命名管道并处理错误的示例:
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cerrno>int main() {std::string fifoPath = "/tmp/my_named_pipe";  // 命名管道的路径名if (mkfifo(fifoPath.c_str(), 0666) == -1) {if (errno == EEXIST) {std::cerr << "Named pipe already exists" << std::endl;} else {perror("Error creating named pipe");}} else {std::cout << "Named pipe created successfully" << std::endl;}return 0;
}

🔴 使用命名管道进行读写操作:在打开命名管道后,可以通过 read()write() 函数对其进行读写操作。

⭕关闭命名管道
  1. 关闭命名管道:当进程使用完毕命名管道后,需要调用 close() 函数来关闭文件描述符,释放相关资源。

    close(fd);  // 关闭命名管道
    
  2. 注意事项

    • 关闭顺序:如果有多个文件描述符指向同一个命名管道,需要依次关闭这些文件描述符,直到所有相关资源都得到释放。
  3. 示例
    下面是一个简单的示例,演示了关闭命名管道的过程:

#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>int main() {int fd = open("/tmp/my_named_pipe", O_RDONLY);  // 以只读方式打开命名管道// 进行读取操作...if (close(fd) == -1) {perror("Error closing named pipe");} else {std::cout << "Named pipe closed successfully" << std::endl;}return 0;
}

总之,关闭命名管道是确保在进程使用完毕后释放相关资源的重要步骤。通过调用 close() 函数可以关闭文件描述符,释放命名管道相关的资源。

(2)通信方式

  1. 单向通信
    命名管道提供了一种单向通信的方式,一个进程可以向管道中写入数据,而另一个进程则可以从管道中读取数据。这种通信方式适用于需要单向数据传输的场景。

  2. 持久性
    命名管道与匿名管道不同之处在于,它以文件的形式存在于文件系统中,具有持久性。即使管道的创建进程终止,命名管道仍然存在,其他进程可以继续使用该管道进行通信。

  3. 阻塞和非阻塞
    在进行命名管道通信时,可以选择阻塞或非阻塞模式。在阻塞模式下,如果读取进程尝试从空管道中读取数据,它将被阻塞直到有数据可读;而在非阻塞模式下,读取进程将立即返回一个错误,从而避免阻塞。

(3)用法示例

下面是一个简单的示例,演示了两个进程通过命名管道进行通信的方式:

进程 A 写入数据到命名管道

int fd = open("/tmp/my_named_pipe", O_WRONLY);  // 以只写方式打开命名管道
write(fd, "Hello, named pipe!", 18);  // 向管道中写入数据
close(fd);  // 关闭命名管道

进程 B 从命名管道读取数据

int fd = open("/tmp/my_named_pipe", O_RDONLY);  // 以只读方式打开命名管道
char buffer[50];
read(fd, buffer, 50);  // 从管道中读取数据
close(fd);  // 关闭命名管道

(4)命名管道的特点

  1. 持久性:命名管道以文件的形式存在于文件系统中,并且具有持久性。即使创建了命名管道的进程终止,该管道仍然存在于文件系统中,其他进程可以继续使用它进行通信。

  2. 单向通信:命名管道提供单向通信的能力,允许一个进程向管道中写入数据,而另一个进程则可以从管道中读取数据。这种单向通信模式适用于需要单向数据传输的场景。

  3. 实时数据传输:命名管道允许实时的数据传输,写入管道的数据会立即被读取进程获取,从而实现了实时通信的能力。

  4. 阻塞和非阻塞模式:对于读取和写入操作,命名管道可以选择阻塞或非阻塞模式。在阻塞模式下,读取进程将被阻塞直到有数据可读,而在非阻塞模式下,读取进程将立即返回错误,避免阻塞。

  5. 简单易用:使用命名管道进行进程间通信相对简单,只需通过类似文件操作的方式打开、读取和关闭管道即可完成通信过程。

  6. 适用范围广泛:命名管道适用于各种场景,例如实现多个进程之间的数据共享、进程之间的控制和协调等。

5. 匿名管道与命名管道的区别

匿名管道和命名管道分别适用于不同的通信需求。
匿名管道适用于有亲缘关系的父子进程间的通信。
命名管道更适合不相关进程间的通信,且具有持久性和更灵活的应用方式。

1. 匿名管道:

  • 单向通信:匿名管道只能支持单向通信,即数据只能从一个进程流向另一个进程,无法实现双向通信。
  • 存在于内存中:匿名管道存在于内存中,并且只能用于相关进程之间的通信。一旦相关进程终止,管道也会自动被销毁。
  • 只能用于父子进程间通信:匿名管道通常用于父子进程之间的通信,因为它要求通信的进程具有一定的亲缘关系。
  • 通常用于shell命令间的通信:在Unix/Linux系统中,匿名管道经常用于将一个命令的输出传递给另一个命令作为输入。

2. 命名管道:

  • 持久性:命名管道以文件的形式存在于文件系统中,具有持久性,即使创建管道的进程终止,管道依然存在,其他进程也可以访问和使用它。
  • 可用于不相关的进程通信:命名管道可以用于不相关的进程之间的通信,这些进程可以位于不同的终端或主机上。
  • 支持阻塞和非阻塞模式:命名管道可以选择阻塞或非阻塞模式进行读写操作。
  • 适用于多种场景:命名管道适用于需要不相关进程之间进行通信的各种场景,例如进程之间的数据共享、控制和协调等。

温馨提示

感谢您对博主文章的关注与支持!如果您喜欢这篇文章,可以点赞、评论和分享给您的同学,这将对我提供巨大的鼓励和支持。另外,我计划在未来的更新中持续探讨与本文相关的内容。我会为您带来更多关于Linux以及C++编程技术问题的深入解析、应用案例和趣味玩法等。如果感兴趣的话可以关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。我们期待与您建立更紧密的互动,共同探索Linux、C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!
在这里插入图片描述

相关文章:

【探索Linux】—— 强大的命令行工具 P.14(进程间通信 | 匿名管道 | |进程池 | pipe() 函数 | mkfifo() 函数)

阅读导航 引言一、进程间通信概念二、进程间通信目的三、进程间通信分类四、管道1. 什么是管道2. 匿名管道&#xff08;1&#xff09;创建和关闭⭕pipe() 函数⭕创建匿名管道⭕关闭匿名管道 &#xff08;2&#xff09;通信方式&#xff08;3&#xff09;用法示例&#xff08;4&…...

图论12-无向带权图及实现

文章目录 带权图1.1带权图的实现1.2 完整代码 带权图 1.1带权图的实现 在无向无权图的基础上&#xff0c;增加边的权。 使用TreeMap存储边的权重。 遍历输入文件&#xff0c;创建TreeMap adj存储每个节点。每个输入的adj节点链接新的TreeMap&#xff0c;存储相邻的边和权重 …...

每日一题(LeetCode)----数组--有序数组的平方

每日一题(LeetCode)----数组–有序数组的平方 1.题目&#xff08;[977. 有序数组的平方](https://leetcode.cn/problems/sqrtx/)&#xff09; 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。…...

SpringCloud微服务:Eureka

目录 提供者与消费者 服务调用关系 eureka的作用 在Eureka架构中&#xff0c;微服务角色有两类 Eureka服务 提供者与消费者 服务提供者:一次业务中&#xff0c;被其它微服务调用的服务。(提供接口给其它微服务)服务消费者:一次业务中&#xff0c;调用其它微服务的服务。(调…...

19.删除链表的倒数第N个结点(LeetCode)

想法一 先用tail指针找尾&#xff0c;计算出节点个数&#xff0c;再根据倒数第N个指定删除 想法二 根据进阶的要求&#xff0c;只能遍历一遍链表&#xff0c;那刚刚想法一就做不到 首先&#xff0c;我们要在一遍内找到倒数第N个节点&#xff0c;所以我们设置slow和fast两个指…...

PyTorch技术和深度学习——三、深度学习快速入门

文章目录 1.线性回归1&#xff09;介绍2&#xff09;加载自由泳冠军数据集3&#xff09;从0开始实现线性回归模型4&#xff09;使用自动求导训练线性回归模型5&#xff09;使用优化器训练线性回归模型 2.使用torch.nn模块构建线性回归模型1&#xff09;使用torch.nn.Linear训练…...

360导航恶意修改浏览器启动页!我的chrome和IE均中招,如何解决?

0&#xff0c;关闭360等“安全”软件 1&#xff0c;按下组合键winR 2&#xff0c;输入regedit&#xff0c;回车 3&#xff0c;按下组合键ctrlF 4&#xff0c;输入http://hao.360.cn&#xff0c;查找下一个 5&#xff0c;查到一个注册表键值就删一个&#xff0c;一个不放过…...

RabbitMQ的高级特性

目录 数据导入 MQ的常见问题 消息可靠性问题 生产者确认机制 SpringAMQP实现生产者确认 消息持久化 消费者消息确认 失败重试机制 消费者失败消息处理策略 死信交换机 TTL 延时队列 安装插件 SpringAMQP使用插件 消息堆积问题 惰性队列 MQ的高可用 普通集群 …...

Java自学第10课:JavaBean和servlet基础

目录 目录 1 JavaBean &#xff08;1&#xff09;概念 &#xff08;2&#xff09;分类 &#xff08;3&#xff09;使用 2 servlet &#xff08;1&#xff09;代码结构 &#xff08;2&#xff09;常用接口 &#xff08;3&#xff09;如何开发 1 新建servlet 2 配置 1…...

AR打卡小程序:构建智能办公的新可能

【内容摘要】 随着技术的飞速发展&#xff0c;智能办公已不再是遥不可及的梦想。在这其中&#xff0c;AR打卡小程序以其独特的技术优势&#xff0c;正逐步成为新型办公生态的重要组成部分。本文将探讨AR打卡小程序的设计理念、技术实现以及未来的应用前景&#xff0c;并尝试深…...

Python环境安装、Pycharm开发工具安装(IDE)

Python下载 Python官网 Python安装 Python安装成功 Pycharm集成开发工具下载&#xff08;IDE&#xff09; PC集成开发工具 Pycharm集成开发工具安装&#xff08;IDE&#xff09; 安装完成 添加环境变量&#xff08;前面勾选了Path不用配置&#xff09; &#xff08;1&…...

报时机器人的rasa shell执行流程分析

本文以报时机器人为载体&#xff0c;介绍了报时机器人的对话能力范围、配置文件功能和训练和运行命令&#xff0c;重点介绍了rasa shell命令启动后的程序执行过程。 一.报时机器人项目结构 1.对话能力范围 (1)能够识别欢迎语意图(greet)和拜拜意图(goodbye) (2)能够识别时间意…...

C#开发的OpenRA游戏之世界存在的属性UpdatesPlayerStatistics(2)

C#开发的OpenRA游戏之世界存在的属性UpdatesPlayerStatistics(2) 在文件OpenRA\mods\cnc\rules\ defaults.yaml里,可以看到这个配置,它的作用就是让这个单元可以被观察者查看到相关的信息。 UpdatesPlayerStatistics属性同样也是有两个类组成,一个叫做信息类UpdatesPlay…...

Ocelot:.NET开源API网关提供路由管理、服务发现、鉴权限流等功能

随着微服务的兴起&#xff0c;API网关越来越常见。API网关是连接应用程序和用户之间的桥梁&#xff0c;就像一个交通指挥员&#xff0c;负责处理所有进出应用的数据和请求&#xff0c;确保安全、高效、有序地流通。 今天给大家推荐一个.NET开源API网关。 01 项目简介 Ocelot…...

wsl [Ubuntu20.04.6] 安装 Hadoop

文章目录 1.安装WSL2.安装Java安装Hadoop3.3配置文件1.修改hadoop-env.sh2.修改core-site.xml3.修改hdfs-site.xml ssh启动 1.安装WSL 重启电脑 管理员打开powershell PS C:\windows\system32> wsl --list --online PS C:\windows\system32> wsl --install -d Ubuntu-2…...

2023华为ict网络赛道初赛(部分)试题

2023华为ict网络赛道初赛&#xff08;部分&#xff09;试题 10.在网络运维中&#xff0c;Telnet是用于连接远程设备的协议之一&#xff0c;那么以下哪一个设备不支持通过Telnet协议远程连接&#xff1f; PCACAPAR 12.openFlow交换机基于流表转发报文&#xff0c;每个流表项由…...

rabbitMq虚拟主机概念

虚拟主机是RabbitMQ中的一种逻辑隔离机制&#xff0c;用于将消息队列、交换机以及其他相关资源进行隔离。 在RabbitMQ中&#xff0c;交换机&#xff08;Exchange&#xff09;用于接收生产者发送的消息&#xff0c;并根据特定的路由规则将消息分发到相应的队列中。而虚拟主机则…...

2-CentOS7.9下安装docker

默认情况下,CentOS7.9下有两种方法可以安装docker,分别是在线安装docker和离线安装docker(伪离线,最后还是需要网络支持) 1.环境信息 HostNameIPAddressOS VersionDocker VersionNotecentos79172.20.10.12CentOS Linux release 7.9.2009 (Core)Docker version 23.0.6, buil…...

【已验证-直接用】微信小程序wx.request请求服务器json数据并渲染到页面

微信小程序的数据总不能写死吧&#xff0c;肯定是要结合数据库来做数据更新&#xff0c;而小程序数据主要是json数据格式&#xff0c;所以我们可以利用php操作数据库&#xff0c;把数据以json格式数据输出即可。 现在给大家讲一下微信小程序的wx.request请求服务器获取数据的用…...

如何提高小红书笔记的互动率

相信有很多新手在运营小红书的时候&#xff0c;可能都会遇到过以下这样的情况&#xff1a; 笔记点赞、收藏数据明明还可以&#xff0c;但评论区却没有人留言&#xff1f;为何大家只给点赞、收藏&#xff0c;却不关注账号&#xff1f; 其实&#xff0c;这背后有很多运营技巧&a…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用

文章目录 一、背景知识&#xff1a;什么是 B-Tree 和 BTree&#xff1f; B-Tree&#xff08;平衡多路查找树&#xff09; BTree&#xff08;B-Tree 的变种&#xff09; 二、结构对比&#xff1a;一张图看懂 三、为什么 MySQL InnoDB 选择 BTree&#xff1f; 1. 范围查询更快 2…...

Chrome 浏览器前端与客户端双向通信实战

Chrome 前端&#xff08;即页面 JS / Web UI&#xff09;与客户端&#xff08;C 后端&#xff09;的交互机制&#xff0c;是 Chromium 架构中非常核心的一环。下面我将按常见场景&#xff0c;从通道、流程、技术栈几个角度做一套完整的分析&#xff0c;特别适合你这种在分析和改…...

智能职业发展系统:AI驱动的职业规划平台技术解析

智能职业发展系统&#xff1a;AI驱动的职业规划平台技术解析 引言&#xff1a;数字时代的职业革命 在当今瞬息万变的就业市场中&#xff0c;传统的职业规划方法已无法满足个人和企业的需求。据统计&#xff0c;全球每年有超过2亿人面临职业转型困境&#xff0c;而企业也因此遭…...