关于RDMA传输的基本流量控制

Basic flow control for RDMA transfers | The Geek in the Corner (wordpress.com)

文心一言

已经介绍了使用发送/接收操作和RDMA读写操作,那么现在是一个很好的机会来结合这两种方法的元素,并讨论一般的流量控制。还会稍微谈谈RDMA带有立即数据的写操作(IBV_WR_RDMA_WRITE_WITH_IMM),并且将通过一个示例来说明这些方法,该示例使用RDMA传输命令行中指定的文件。

示例包括一个服务器和一个客户端。服务器等待来自客户端的连接。客户端在连接到服务器后主要执行两个操作:它发送要传输的文件名,然后发送文件的内容。我们不会关注建立连接的细节;这些在之前的帖子中已经讨论过了。相反,我们将专注于同步和流量控制。不过,在本文中的代码结构上做了一些调整,与之前关于RDMA读写的帖子中的结构相反——在那里,将连接管理代码分别放在client.c和server.c中,而将完成处理代码放在common.c中。而在这里,将连接管理代码集中放在common.c中,并将完成处理代码分别放在client.c和server.c中。

回到示例。有许多方法可以从客户端向服务器传输整个文件。例如:

  1. 将整个文件加载到客户端内存中,连接到服务器,等待服务器发布一系列接收操作,然后在客户端端发起一个发送操作(send)以将内容复制到服务器。
  2. 将整个文件加载到客户端内存中,注册内存,将区域详细信息传递给服务器,让服务器发起RDMA读取操作将整个文件复制到其内存中,然后将内容写入磁盘。
  3. 与上述相同,但发起RDMA写入操作以将文件内容复制到服务器内存中,然后通知它写入磁盘。
  4. 在客户端打开文件,读取一个块,等待服务器发布接收操作,然后在客户端端发布一个发送操作,并循环直到整个文件被发送。
  5. 与上述相同,但使用RDMA读取操作。
  6. 与上述相同,但使用RDMA写入操作。

将整个文件加载到内存中对于大文件来说可能不切实际,因此将跳过前三个选项。在剩下的三个选项中,将专注于使用RDMA写入操作,以便可以说明RDMA带有立即数据的写入操作的使用,这是一直想讨论的一个话题。这种操作类似于常规的RDMA写入,但发起者可以将32位值“附加”到写入操作上。与常规RDMA写入不同,RDMA带有立即数据的写入要求在目标的接收队列上发布一个接收操作。当从目标的队列中拉取完成时,该32位值将可用。

12月26日:Roland D. 相当热心地指出,iWARP适配器不支持RDMA带有立即数据的写入。我们可以重写代码以使用RDMA写入(不带立即数据)后跟一个发送操作,但这留作读者的练习。

既然我们已经决定要将文件拆分成块,并将这些块一次一个地写入服务器的内存,我们需要找到一种方法来确保我们不会比服务器能够处理的速度更快地写入块。我们将通过服务器在准备好接收数据时向客户端发送显式消息来实现这一点。另一方面,客户端将使用带有立即数据的写入来向服务器发送信号。这个过程的大致顺序如下:

  1. 服务器开始监听连接。
  2. 客户端发布一个用于流量控制消息的接收操作,并启动到服务器的连接。
  3. 服务器发布一个用于RDMA带有立即数据的写入的接收操作,并接受来自客户端的连接。
  4. 服务器向客户端发送其目标内存区域的详细信息。
  5. 客户端重新发布一个接收操作,然后通过将文件名写入服务器的内存区域来响应。立即数据字段包含文件名的长度。
  6. 服务器打开一个文件描述符,重新发布一个接收操作,然后发送一个消息,指示它已准备好接收数据。
  7. 客户端重新发布一个接收操作,从输入文件中读取一个数据块,然后将该数据块写入服务器的内存区域。立即数据字段包含该数据块的大小(以字节为单位)。
  8. 服务器将数据块写入磁盘,重新发布一个接收操作,然后发送一个消息,指示它已准备好接收数据。
  9. 重复步骤7和8,直到没有数据要发送。
  10. 客户端重新发布一个接收操作,然后向服务器的内存发起一个零字节的写入操作。立即数据字段设置为零。
  11. 服务器发送一个消息,指示已完成操作。
  12. 客户端关闭连接。
  13. 服务器关闭文件描述符。

一个图表可能会有所帮助:

查看这个序列,我们可以看到服务器只向客户端发送小消息,并且只从客户端接收RDMA写入操作。客户端只执行RDMA写入操作,并且只从服务器接收小消息。

让我们从服务器开始看起。建立连接的细节现在隐藏在rc_init()函数之后,该函数设置了各种回调函数,以及rc_server_loop()函数,它运行一个事件循环:

int main(int argc, char **argv)
{rc_init(on_pre_conn,on_connection,on_completion,on_disconnect);printf("waiting for connections. interrupt (^C) to exit.\n");rc_server_loop(DEFAULT_PORT);return 0;
}

回调函数的名称相当直观:on_pre_conn()在接收到连接请求但尚未接受连接时被调用,on_connection()在建立连接时被调用,on_completion()在从完成队列中拉取条目时被调用,而on_disconnect()在断开连接时被调用。

on_pre_conn()中,我们分配一个结构体来包含各种连接上下文字段(一个缓冲区来包含来自客户端的数据,一个缓冲区用于向客户端发送消息等),并发布一个接收工作请求以接收客户端的RDMA写入操作:

static void post_receive(struct rdma_cm_id *id)
{struct ibv_recv_wr wr, *bad_wr = NULL;memset(&wr, 0, sizeof(wr));wr.wr_id = (uintptr_t)id;wr.sg_list = NULL;wr.num_sge = 0;TEST_NZ(ibv_post_recv(id->qp, &wr, &bad_wr));
}

这里有趣的是我们设置了sg_list = NULLnum_sge = 0。传入的RDMA写请求将指定一个目标内存地址,由于这个工作请求只与传入的RDMA写请求匹配,所以我们不需要使用sg_listnum_sge来指定接收的内存位置。在连接建立后,on_connection()将内存区域的详细信息发送给客户端:

static void on_connection(struct rdma_cm_id *id)
{struct conn_context *ctx = (struct conn_context *)id->context;ctx->msg->id = MSG_MR;ctx->msg->data.mr.addr = (uintptr_t)ctx->buffer_mr->addr;ctx->msg->data.mr.rkey = ctx->buffer_mr->rkey;send_message(id);
}

这促使客户端开始发出RDMA写入操作,这会触发on_completion()回调函数:

static void on_completion(struct ibv_wc *wc)
{struct rdma_cm_id *id = (struct rdma_cm_id *)(uintptr_t)wc->wr_id;struct conn_context *ctx = (struct conn_context *)id->context;if (wc->opcode == IBV_WC_RECV_RDMA_WITH_IMM) {uint32_t size = ntohl(wc->imm_data);if (size == 0) {ctx->msg->id = MSG_DONE;send_message(id);// don't need post_receive() since we're done with this connection} else if (ctx->file_name[0]) {ssize_t ret;printf("received %i bytes.\n", size);ret = write(ctx->fd, ctx->buffer, size);if (ret != size)rc_die("write() failed");post_receive(id);ctx->msg->id = MSG_READY;send_message(id);} else {memcpy(ctx->file_name, ctx->buffer, (size > MAX_FILE_NAME) ? MAX_FILE_NAME : size);ctx->file_name[size - 1] = '\0';printf("opening file %s\n", ctx->file_name);ctx->fd = open(ctx->file_name, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);if (ctx->fd == -1)rc_die("open() failed");post_receive(id);ctx->msg->id = MSG_READY;send_message(id);}}
}

我们在第7行检索即时数据字段,并将其从网络字节顺序转换为主机字节顺序。然后我们测试三种可能的情况:

如果size == 0,则表示客户端已完成数据写入(第9-14行)。我们用MSG_DONE来确认这一点。

如果ctx->file_name的第一个字节被设置,则表示我们已经有了文件名并且有一个打开的文件描述符(第15-30行)。我们调用write()将客户端的数据追加到我们打开的文件中,然后用MSG_READY回复,表示我们已准备好接收更多数据。

否则,我们尚未收到文件名(第30-45行)。我们从输入缓冲区中复制它,打开一个文件描述符,然后用MSG_READY回复,表示我们已准备好接收数据。

在断开连接时,在on_disconnect()中,我们关闭打开的文件描述符并整理内存注册等。服务器的操作就是这样!

在客户端,main()函数稍微复杂一些,因为我们需要将服务器主机名和端口传递给rc_client_loop()

int main(int argc, char **argv)
{struct client_context ctx;if (argc != 3) {fprintf(stderr, "usage: %s <server-address> <file-name>\n", argv[0]);return 1;}ctx.file_name = basename(argv[2]);ctx.fd = open(argv[2], O_RDONLY);if (ctx.fd == -1) {fprintf(stderr, "unable to open input file \"%s\"\n", ctx.file_name);return 1;}rc_init(on_pre_conn,NULL, // on connecton_completion,NULL); // on disconnectrc_client_loop(argv[1], DEFAULT_PORT, &ctx);close(ctx.fd);return 0;
}

我们不为连接或断开连接提供回调函数,因为这些事件对客户端来说不是特别相关。on_pre_conn()回调函数与服务器端的相当类似,除了连接上下文结构是预先分配的之外,我们发布的接收工作请求(在post_receive()中)需要一个内存区域:

static void post_receive(struct rdma_cm_id *id)
{struct client_context *ctx = (struct client_context *)id->context;struct ibv_recv_wr wr, *bad_wr = NULL;struct ibv_sge sge;memset(&wr, 0, sizeof(wr));wr.wr_id = (uintptr_t)id;wr.sg_list = &sge;wr.num_sge = 1;sge.addr = (uintptr_t)ctx->msg;sge.length = sizeof(*ctx->msg);sge.lkey = ctx->msg_mr->lkey;TEST_NZ(ibv_post_recv(id->qp, &wr, &bad_wr));
}

我们将sg_list指向一个足够大的缓冲区,以容纳一个message结构体。服务器将使用这个缓冲区来传递流控制消息。每个消息都会触发对on_completion()的调用,这是客户端执行大部分工作的地方:

static void on_completion(struct ibv_wc *wc)
{struct rdma_cm_id *id = (struct rdma_cm_id *)(uintptr_t)(wc->wr_id);struct client_context *ctx = (struct client_context *)id->context;if (wc->opcode & IBV_WC_RECV) {if (ctx->msg->id == MSG_MR) {ctx->peer_addr = ctx->msg->data.mr.addr;ctx->peer_rkey = ctx->msg->data.mr.rkey;printf("received MR, sending file name\n");send_file_name(id);} else if (ctx->msg->id == MSG_READY) {printf("received READY, sending chunk\n");send_next_chunk(id);} else if (ctx->msg->id == MSG_DONE) {printf("received DONE, disconnecting\n");rc_disconnect(id);return;}post_receive(id);}
}

这与上面描述的序列相匹配。send_file_name()send_next_chunk()最终都调用了write_remote()

static void write_remote(struct rdma_cm_id *id, uint32_t len)
{struct client_context *ctx = (struct client_context *)id->context;struct ibv_send_wr wr, *bad_wr = NULL;struct ibv_sge sge;memset(&wr, 0, sizeof(wr));wr.wr_id = (uintptr_t)id;wr.opcode = IBV_WR_RDMA_WRITE_WITH_IMM;wr.send_flags = IBV_SEND_SIGNALED;wr.imm_data = htonl(len);wr.wr.rdma.remote_addr = ctx->peer_addr;wr.wr.rdma.rkey = ctx->peer_rkey;if (len) {wr.sg_list = &sge;wr.num_sge = 1;sge.addr = (uintptr_t)ctx->buffer;sge.length = len;sge.lkey = ctx->buffer_mr->lkey;}TEST_NZ(ibv_post_send(id->qp, &wr, &bad_wr));
}

这个RDMA请求与之前帖子中描述的请求在两个方面有所不同:我们将操作码(opcode)设置为IBV_WR_RDMA_WRITE_WITH_IMM,并将imm_data设置为我们缓冲区的长度。

这并不难理解,对吧?如果一切正常,你应该会看到以下情况:

ib-host-1$ ./server 
waiting for connections. interrupt (^C) to exit.
opening file test-file
received 10485760 bytes.
received 10485760 bytes.
received 5242880 bytes.
finished transferring test-file
^Cib-host-1$ md5sum test-file
5815ed31a65c5da9745764c887f5f777  test-file
ib-host-2$ dd if=/dev/urandom of=test-file bs=1048576 count=25
25+0 records in
25+0 records out
26214400 bytes (26 MB) copied, 3.11979 seconds, 8.4 MB/sib-host-2$ md5sum test-file
5815ed31a65c5da9745764c887f5f777  test-fileib-host-2$ ./client ib-host-1 test-file
received MR, sending file name
received READY, sending chunk
received READY, sending chunk
received READY, sending chunk
received READY, sending chunk
received DONE, disconnecting

如果你在内存注册期间遇到错误,比如以下错误,你可能需要增加你的锁定内存资源限制:

error: ctx->buffer_mr = ibv_reg_mr(rc_get_pd(), ctx->buffer, BUFFER_SIZE, IBV_ACCESS_LOCAL_WRITE) failed (returned zero/null).

OpenMPI的FAQ对如何设置锁定内存限制有很好的解释。

示例代码可以在这里找到。

12月21日:更新了帖子以描述锁定内存限制错误,并更新了示例代码以:检查ibv_reg_mr()的错误;使用文件路径的basename()而不是完整路径;在open()调用中添加缺失的模式参数;在Makefile中添加缺失的库引用。感谢Matt。

10月4日:示例代码现在位于https://github.com/tarickb/the-geek-in-the-corner/tree/master/03_file-transfer。

Kimi

这篇文章讨论了使用RDMA(Remote Direct Memory Access,远程直接内存访问)传输大量数据的基本流程控制。作者Matt最近询问了发送大于或等于2GB数据的方法。作者指出,尽管存在2^31的消息大小限制,但有多种有效的方法可以处理这个问题,考虑到内存资源有限,将跳过将整个文件加载到内存中的前三个选项。作者选择了使用RDMA写入操作来说明RDMA写入即时数据(IBV_WR_RDMA_WRITE_WITH_IMM)操作的使用,这是一种与常规RDMA写入类似的操作,但发起方可以附加一个32位的值。

文章提供了一个示例,包括服务器和客户端的代码,服务器等待客户端连接,客户端在连接后发送要传输的文件名,然后发送文件内容。文章重点讨论了同步和流程控制,而不是连接建立的细节。

以下是文章的中文翻译概要:

  1. 服务器和客户端结构:服务器等待连接,客户端在连接后发送文件名和内容。
  2. 流程控制:使用RDMA写入操作和即时数据来控制数据传输速度,确保不会比服务器处理速度快。
  3. 服务器代码:隐藏了连接建立的细节,通过回调函数处理连接请求、建立、完成和断开连接。
  4. 客户端代码:比服务器复杂,需要传递服务器主机名和端口,处理接收到的消息,并执行RDMA写入操作。
  5. 写入远程数据:客户端使用RDMA写入即时数据操作发送文件名和数据块。
  6. 错误处理:如果遇到内存注册错误,可能需要增加锁定内存资源限制。

文章最后提供了示例代码的链接,并在后续更新中修正了一些错误和遗漏。

请注意,这是文章内容的概要翻译,并非逐字翻译。如果需要更详细的翻译或对特定部分有疑问,请告知。

这是一篇关于使用RDMA(Remote Direct Memory Access,远程直接内存访问)进行大文件传输的讨论。评论者Matt最近询问了如何发送较大的数据量,例如大于或等于2GB的文件。他怀疑存在一种有效的方法来处理这个问题,因为消息大小有一个2^31的限制。他指出,他没有大量的内存可用,因为它被用于其他事情。

文章中提到了多种方法来协调从客户端到服务器的整个文件传输。包括将整个文件加载到客户端内存中,然后发送到服务器;或者使用RDMA读取或写入操作。但是,加载整个文件到内存对于大文件来说可能是不切实际的,所以我们将跳过前三个选项。在剩下的三个选项中,作者选择使用RDMA写入,这样可以展示RDMA-write-with-immediate-data操作的使用,这是他一直想讨论的内容。这种操作与传统的RDMA写入类似,但发起方可以“附加”一个32位的值到写入操作中。与常规的RDMA写入不同,带有立即数据的RDMA写入需要目标在接收队列上发布接收操作。当从目标队列中提取完成时,32位的值将可用。

文章还讨论了如何处理流控制,并提供了一个示例,说明如何使用RDMA传输命令行中指定的文件。示例包括服务器和客户端。服务器等待客户端的连接。客户端在连接到服务器后主要做两件事:发送它正在传输的文件的名称,然后发送文件的内容。文章不会涉及建立连接的细节,而是将重点放在同步和流控制上。

服务器端的代码示例包括初始化、接受连接、处理完成和断开连接的回调函数。客户端代码示例包括发送文件名和文件块,以及接收服务器的流控制消息。

文章最后提供了一个示例,展示了如何使用RDMA写入和立即数据来传输文件,并说明了服务器和客户端如何通过发送和接收消息来协调文件传输的过程。

请注意,由于网络原因,我无法访问提供的GitHub链接以验证示例代码。如果你需要查看示例代码,你可以访问提供的GitHub链接。此外,RDMA write with immediate data在iWARP适配器中可能不受支持,文章建议使用RDMA write(不带立即数据)后跟一个send操作,但这留给读者作为练习。

文章最后更新了一些关于锁定内存限制错误的描述,并更新了示例代码,以检查ibv_reg_mr()的错误,使用文件路径的basename()而不是完整路径,添加了open()调用中缺少的mode参数,以及在Makefile中添加了缺失的库引用。

这篇文章讨论了使用RDMA(Remote Direct Memory Access)传输大量数据的基本流程控制。评论者Matt最近询问了关于发送较大数据量(例如大于或等于2GB的文件)的问题。作者指出,尽管存在2^31的消息大小限制,但有多种有效的方法来处理这种数据传输,尤其是在内存资源有限的情况下。

文章中提到了几种可能的数据传输方式,包括将整个文件加载到客户端内存、使用RDMA读写操作等。但考虑到内存限制,作者选择了使用RDMA写操作,并特别提到了带有立即数据的RDMA写操作(IBV_WR_RDMA_WRITE_WITH_IMM),这种操作允许发送方附加一个32位的值到写操作中,并且需要目标方在接收队列中发布接收操作。

文章详细介绍了客户端和服务器的实现方式,包括它们如何同步和控制流程。服务器等待客户端的连接请求,而客户端在连接后发送要传输的文件名,然后发送文件内容。文章重点介绍了如何使用立即数据信号服务器,以及服务器如何显式地向客户端发送消息以表示它已准备好接收数据。

以下是文章的中文翻译摘要:

Matt最近询问了关于发送较大数据量的问题,例如发送大于或等于2GB的文件。作者提供了一些指针或示例,说明了如何发送大量数据。由于存在2^31的消息大小限制,作者提出了一种结合发送/接收操作和RDMA读写操作的方法来处理流程控制,并特别介绍了带有立即数据的RDMA写操作。

文章中的示例包括一个服务器和一个客户端。服务器等待客户端的连接,客户端在连接后主要做两件事:发送它要传输的文件名,然后发送文件内容。文章没有涉及建立连接的细节,而是专注于同步和流程控制。

作者选择了使用RDMA写操作来分解文件并逐个写入服务器内存,并使用立即数据信号来确保不会比服务器处理速度更快地写入数据。服务器通过发送显式消息来告知客户端何时准备好接收数据。

文章还提供了一个流程图来说明文件传输的顺序,包括服务器和客户端如何交互,以及它们如何使用RDMA写操作和立即数据来控制数据传输。

最后,文章提供了服务器和客户端的示例代码,展示了如何实现上述流程。如果在内存注册过程中遇到错误,可能需要增加锁定内存资源的限制。

文章的示例代码可以在GitHub上找到,链接已在文章中更新。

请注意,这是一个非常技术性的文章,涉及到特定的编程概念和网络协议。如果你需要更详细的翻译或对特定部分有疑问,请告知。

这篇文章讨论了使用RDMA(Remote Direct Memory Access,远程直接内存访问)传输大量数据的基本流程控制。评论者Matt最近询问了发送较大数据量(例如大于或等于2GB的文件)的方法。文章作者指出,尽管存在2^31的消息大小限制,但有多种有效的方法可以处理这种数据传输,尤其是考虑到Matt提到可用内存有限。

文章介绍了几种可能的文件传输方式,包括将整个文件加载到客户端内存中,然后发送到服务器;或者使用RDMA读取或写入操作。作者选择了使用RDMA写入操作,因为它可以展示RDMA-write-with-immediate-data(带有立即数据的RDMA写入)操作的使用,这是一种特殊的RDMA写入操作,允许发送方附加一个32位的值。

文章接着详细描述了客户端和服务器的交互流程,包括服务器监听连接、客户端发送文件名和文件内容、同步和流程控制等。服务器和客户端都使用特定的回调函数来处理连接请求、建立连接、完成队列中的条目以及断开连接。

文章还提供了一个示例代码,展示了如何使用RDMA写入操作和立即数据来传输文件。服务器和客户端的代码都进行了简化,以便专注于流程控制和同步机制。服务器代码隐藏了连接建立的细节,并运行了一个事件循环。客户端代码则需要传递服务器主机名和端口到循环中,并在接收到服务器的内存区域细节后开始发送文件名和文件内容。

最后,文章提到如果遇到内存注册错误,可能需要增加锁定内存资源限制,并提供了OpenMPI FAQ的链接来解释如何操作。文章还提到了示例代码的位置,并感谢了Matt和其他贡献者的帮助。

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

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

相关文章

DeepDriving | 多目标跟踪算法之SORT

本文来源公众号“DeepDriving”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;多目标跟踪算法之SORT 1 简介 SORT是2016年发表的一篇文章《Simple Online and Realtime Tracking》中提出的一个经典的多目标跟踪算法&#xff0c;…

九大微服务监控工具详解

Prometheus Prometheus 是一个开源的系统监控、和报警工具包&#xff0c;Prometheus 被设计用来监控“微服务架构”。 主要解决&#xff1a; 监控和告警&#xff1a;Prometheus 可以对系统、和应用程序进行实时监控&#xff0c;并在出现问题时发送告警&#xff1b;数据收集和…

从0到1实现一个自己的大模型,实践中了解模型流程细节

前言 最近看了很多大模型&#xff0c;也使用了很多大模型。对于大模型理论似乎很了解&#xff0c;但是好像又缺点什么&#xff0c;思来想去决定自己动手实现一个 toy 级别的模型&#xff0c;在实践中加深对大语言模型的理解。 在这个系列的文章中&#xff0c;我将通过亲手实践…

问题:当频点数大于载波数时,() #学习方法#知识分享

问题&#xff1a;当频点数大于载波数时&#xff0c;&#xff08;&#xff09; A.基带跳频可以执行&#xff0c;混合跳频可以执行 B.基带跳频不可以执行&#xff0c;混合跳频可以执行 C.基带跳频可以执行&#xff0c;混合跳频不可以执行 D.基带跳频不可以执行&#xff0c;混…

用 Notepad++ 写 Java 程序

安装包 百度网盘 提取码&#xff1a;6666 安装步骤 双击安装包开始安装。 安装完成&#xff1a; 配置编码 用 NotePad 写 Java 程序时&#xff0c;需要设置编码。 在 设置&#xff0c;首选项&#xff0c;新建 中进行设置&#xff0c;可以对每一个新建的文件起作用。 Note…

IO进程线程(十)进程间通信 消息队列 共享内存 信号灯集

文章目录 一、IPC(Inter-Process Communication)进程间通信相关命令 &#xff1a;&#xff08;一&#xff09;ipcs --- 查看IPC对象&#xff08;二&#xff09;获取IPC键值&#xff08;三&#xff09;删除IPC对象的命令&#xff08;四&#xff09;获取IPC键值的函数1. 函数定义…

Maxkb玩转大语言模型

Maxkb玩转大语言模型 随着国外大语言模型llama3的发布&#xff0c;搭建本地个人免费“人工智能”变得越来越简单&#xff0c;今天博主分享使用Max搭建本地的个人聊天式对话及个人本地知识域的搭建。 1.安装Maxkb开源应用 github docker快速安装 docker run -d --namemaxkb -p 8…

FL Studio21.2.9中文破解版水果软件安装包附带激活码注册码

音乐制作软件&#xff0c;对很多人而言&#xff0c;是一个“高门槛”的存在。它既需要专业的音乐知识&#xff0c;也需要复杂的操作技巧。 「FL Studio 21中文版马丁版下载」&#xff0c;复制整段内容&#xff0c;打开最新版「夸克APP」即可获取链接&#xff1a; https://pan…

Dokcer 基础使用 (4) 网络管理

文章目录 Docker 网络管理需求Docker 网络架构认识Docker 常见网络类型1. bridge 网络2. host 网络3. container 网络4. none 网络5. overlay 网络 Docker 网路基础指令Docker 网络管理实操 其他相关链接 Docker 基础使用(0&#xff09;基础认识 Docker 基础使用(1&#xff09;…

git(其六)--总结

配置基础信息 //1.配置用户名和邮箱 git config --global user.name "带着引号写一个昵称" git config --global user.email "带着引号写一个邮箱"//2.建立一个git本地库 git init//3.查看本地内容 git status //可以看到那些处于待加入本地库的文件&a…

使用AutoGen框架进行多智能体协作:AI Agentic Design Patterns with AutoGen

AI Agentic Design Patterns with AutoGen 本文是学习https://www.deeplearning.ai/short-courses/ai-agentic-design-patterns-with-autogen/ 这门课的学习笔记。 What you’ll learn in this course In AI Agentic Design Patterns with AutoGen you’ll learn how to buil…

当C++的static遇上了继承

比如我们想要统计下当前类被实例化了多少次&#xff0c;我们通常会这么写 class A { public:A() { Count_; }~A() { Count_--; }int GetCount() { return Count_; }private:static int Count_; };class B { public:B() { Count_; }~B() { Count_--; }int GetCount() { return …

抢人!抢人!抢人! IT行业某岗位已经开始抢人了!

所谓抢滩鸿蒙&#xff0c;人才先行。鸿蒙系统火力全开后&#xff0c;抢人已成鸿蒙市场的主题词&#xff01; 智联招聘数据显示&#xff0c;春节后首周&#xff0c;鸿蒙相关职位数同比增长163%&#xff0c;是去年同期的2.6倍&#xff0c;2023年9-12月鸿蒙相关职位数同比增速为3…

前端开发常用的工具和软件,提高编程效率

目录 1. 文本编辑器与IDE (集成开发环境)2. 版本控制工具3. 构建工具与包管理器4. 前端框架与库5. 设计与原型工具6. 测试与调试工具7. 代码协作与项目管理8. 自动化部署与持续集成/持续部署(CI/CD)相关链接&#xff1a; 前端开发过程中使用的工具和软件种类繁多&#xff0c;可…

Python实现调用并执行Linux系统命令

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主。 &#x1f913; 同时欢迎大家关注其他专栏&#xff0c;我将分享Web前后端开发、人工智能、机器学习、深…

【Node】node的Events模块(事件模块)的介绍和使用

文章目录 简言EventsPassing arguments and this to listeners 向监听器传递参数Asynchronous vs. synchronous 异步和同步Handling events only once 只一次处理事件Error events 错误事件Capture rejections of promises 捕捉拒绝承诺的情况Class: EventEmitter 事件类Event:…

聊聊二叉堆、红黑树、时间轮在定时任务中的应用

定时任务作为常用的一种调度方式&#xff0c;在各大系统得到了广泛的应用。 笔者也曾写过两篇关于定时任务框架介绍的文章&#xff1a; 《介绍一下,spring cloud下的另一种定时任务解决方案》《四叉堆在GO中的应用-定时任务timer》 之前都是以如何使用为主&#xff0c;这次从…

Vue项目安装axios报错npm error code ERESOLVE npm error ERESOLVE could not resolve解决方法

在Vue项目中安装axios时报错 解决方法&#xff1a;在npm命令后面加--legacy-peer-deps 例如&#xff1a;npm install axios --save --legacy-peer-deps 因为别的需求我把node版本重装到了最新版&#xff08;不知道是不是这个原因&#xff09;&#xff0c;后来在项目中安装axi…

在推荐四款软件卸载工具,让流氓软件无处遁形

Revo Uninstaller Revo Uninstaller是一款电脑软件、浏览器插件卸载软件&#xff0c;目前已经有了17年的历史了。可以扫描所有window用户卸载软件后的残留物&#xff0c;并及时清理&#xff0c;避免占用电脑空间。 Revo Uninstaller可以通过命令行卸载软件&#xff0c;可以快速…

前端生成海报图技术选型与问题解决

作者&#xff1a;vivo 互联网大前端团队 - Tian Yuhan 本篇文章主要聚焦海报图分享这个形式&#xff0c;探讨纯前端在H5&小程序内&#xff0c;合成海报到下载到本地、分享至社交平台整个流程中可能遇到的问题&#xff0c;以及如何解决。 一、引言 绝大多数的电商平台都会…