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

[学习]POSIX消息队列的原理与案例分析(完整示例代码)

POSIX消息队列的原理与案例分析

文章目录

  • POSIX消息队列的原理与案例分析
    • 摘要
    • 关键词
    • 一、引言
      • 1.1 研究背景与意义
      • 1.2 国内外研究现状
      • 1.3 研究内容与方法
    • 二、POSIX消息队列的基本原理
      • 2.1 消息队列概述
      • 2.2 POSIX消息队列的特性
      • 2.2 POSIX消息队列的特性
      • 2.3 POSIX消息队列的内部机制
    • 三、POSIX消息队列的API函数详解
      • 3.1 消息队列的创建与打开
      • 3.2 消息的发送与接收
      • 3.3 消息队列的关闭与删除
      • 3.4 消息队列属性的获取与设置
    • 四、POSIX消息队列的异步通知机制
      • 4.1 异步通知概述
      • 4.2 sigevent结构体
      • 4.3 mq_notify函数
      • 4.4 示例代码
    • 五、POSIX消息队列的多线程安全特性
      • 5.1 多线程安全概述
      • 5.2 线程安全实现机制
      • 5.3 示例代码
    • 六、POSIX消息队列的案例分析
      • 6.1 案例一:生产者-消费者模型
      • 6.2 案例二:日志处理系统
      • 6.3 案例三:实时监控系统
    • 七、POSIX消息队列的性能优化与测试
      • 7.1 性能优化策略
      • 7.2 性能测试方法
    • 八、结论与展望
      • 8.1 研究成果总结
      • 8.2 存在问题与不足
      • 8.3 未来研究方向
    • 参考文献

摘要

本文深入探讨POSIX消息队列的原理、特性及其在Linux系统中的应用。通过分析消息队列的核心机制、API函数、异步通知功能及多线程安全特性,结合具体案例展示其在生产者-消费者模型、日志处理系统及实时监控系统中的应用。POSIX消息队列作为进程间通信的重要手段,以其高效、灵活和可移植性在分布式系统中发挥关键作用。

关键词

POSIX消息队列;进程间通信;异步通知;多线程安全;生产者-消费者模型

一、引言

1.1 研究背景与意义

随着计算机技术的飞速发展,分布式系统、并行计算及多线程编程日益普及。进程间通信(IPC)作为这些技术的基础,其效率和可靠性直接影响到整个系统的性能。POSIX消息队列作为一种标准化的IPC机制,因其高效、灵活和可移植性,在Linux系统及其他支持POSIX标准的操作系统中得到了广泛应用。研究POSIX消息队列的原理与应用,对于深入理解IPC机制、优化系统性能具有重要意义。

1.2 国内外研究现状

国内外学者对POSIX消息队列的研究主要集中在以下几个方面:一是消息队列的内部机制,包括消息存储、优先级排序及阻塞/非阻塞模式;二是消息队列的性能优化,如减少内存拷贝、提高并发处理能力;三是消息队列在不同应用场景下的实践,如分布式系统、实时监控等。然而,现有研究多侧重于理论分析或单一应用场景,缺乏对POSIX消息队列原理与案例的全面整合。

1.3 研究内容与方法

本文将从POSIX消息队列的基本原理出发,深入剖析其API函数、异步通知功能及多线程安全特性。通过构建生产者-消费者模型、日志处理系统及实时监控系统等案例,展示POSIX消息队列在不同应用场景下的实际应用效果。研究方法包括文献综述、理论分析、代码实现及性能测试等。

二、POSIX消息队列的基本原理

2.1 消息队列概述

消息队列是一种用于进程间通信的机制,它允许不同进程通过发送和接收消息来进行数据交换。与管道、共享内存等IPC机制相比,消息队列具有消息边界清晰、支持优先级排序及异步通知等优点。POSIX消息队列作为标准化的IPC接口,提供了统一的API函数,便于跨平台开发。

2.2 POSIX消息队列的特性

  • ** 标准化接口**
    POSIX消息队列遵循IEEE 1003.1标准(也称为POSIX.1),提供了一套统一的API函数,包括mq_openmq_sendmq_receivemq_closemq_unlink等。这些标准化的接口使得开发者能够在不同的类UNIX操作系统(如Linux、macOS、FreeBSD等)之间轻松移植代码。例如,开发者可以使用mq_open函数创建或打开一个消息队列,mq_send向队列发送消息,mq_receive从队列接收消息,而无需为不同平台编写特定代码。

  • 消息优先级
    POSIX消息队列支持为每条消息设置优先级,优先级范围为0到sysconf(_SC_MQ_PRIO_MAX)的最大值(通常为32768)。高优先级的消息会被优先接收,这对于实时性要求较高的应用场景非常有用。例如,在嵌入式系统中,紧急任务的消息可以设置为高优先级,确保它们能及时被处理,而普通任务的消息则设置为低优先级。

  • 异步通知
    POSIX消息队列支持通过信号或线程回调实现消息到达的异步通知。开发者可以使用mq_notify函数注册通知机制,当消息到达队列时,系统会通过信号或线程回调通知进程,从而减少进程阻塞时间,提高系统响应速度。例如,在一个事件驱动的系统中,可以使用异步通知机制避免轮询消息队列,从而节省CPU资源。

  • 引用计数
    POSIX消息队列采用引用计数机制来管理队列的生存周期。只有当所有使用队列的进程都调用mq_close关闭队列后,队列才会被标记为可删除。这种机制确保资源的安全释放,避免队列在仍被使用的情况下被意外删除。例如,在多进程应用中,即使一个进程调用了mq_unlink删除队列,只要其他进程仍在使用队列,队列的实际资源就不会被释放。

  • 多线程安全
    POSIX消息队列的操作是线程安全的,这意味着多个线程可以同时调用mq_sendmq_receive等函数而不会导致数据竞争或资源冲突。这种特性使得消息队列非常适合在多线程环境中使用,开发者无需额外实现同步机制。例如,在一个多线程服务器中,多个工作线程可以安全地从同一个消息队列中接收任务,而无需担心线程安全问题。

  • 其他特性

    • 消息大小和队列容量:POSIX消息队列允许开发者配置每条消息的最大大小和队列的总容量,以满足不同应用场景的需求。例如,在高吞吐量系统中,可以增加队列容量以减少消息丢失的风险。
    • 超时机制mq_sendmq_receive函数支持设置超时时间,避免进程因队列满或空而无限阻塞。例如,在实时系统中,可以为关键任务设置较短的超时时间,确保任务能够及时处理。
    • 持久性:POSIX消息队列在系统中具有持久性,即使创建队列的进程退出,队列仍然存在,直到被显式删除。这种特性使得消息队列适合用于进程间长期通信的场景。

通过以上特性,POSIX消息队列为开发者提供了一种高效、可靠且灵活的进程间通信机制,适用于多种复杂的应用场景。

2.2 POSIX消息队列的特性

  • 标准化接口:POSIX消息队列遵循POSIX标准,提供了统一的API函数,如mq_open、mq_send、mq_receive等,便于开发者在不同操作系统间移植代码。
  • 消息优先级:每条消息可设置优先级,高优先级消息优先被接收,满足实时性要求较高的应用场景。
  • 异步通知:支持通过信号或线程回调实现消息到达通知,减少进程阻塞时间,提高系统响应速度。
  • 引用计数:只有当所有使用队列的进程都关闭队列后,队列才会被标记为可删除,确保资源安全释放。
  • 多线程安全:POSIX消息队列的操作是线程安全的,可在多线程环境中使用,无需额外同步机制。

2.3 POSIX消息队列的内部机制

  • 消息存储:消息队列在内核中以链表形式存储消息,每条消息包含消息体、优先级及消息类型等信息。
  • 优先级排序:消息按优先级倒序排列,相同优先级的消息按先进先出(FIFO)顺序处理。
  • 阻塞/非阻塞模式:支持阻塞和非阻塞两种模式,阻塞模式下,发送或接收操作在队列满或空时等待;非阻塞模式下,操作立即返回错误码。

三、POSIX消息队列的API函数详解

3.1 消息队列的创建与打开

  • mq_open函数:用于创建或打开一个消息队列,返回消息队列描述符。参数包括队列名称、打开标志、权限掩码及队列属性等。
  • 示例代码:展示如何使用mq_open函数创建或打开一个消息队列,并设置队列属性。
#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>int main() {mqd_t mq;struct mq_attr attr;const char *queue_name = "/test_queue";// 设置消息队列属性attr.mq_flags = 0;                   // 阻塞模式attr.mq_maxmsg = 10;                 // 最大消息数attr.mq_msgsize = 256;               // 单条消息最大长度attr.mq_curmsgs = 0;                 // 当前消息数(由内核管理)// 创建或打开消息队列mq = mq_open(queue_name, O_CREAT | O_RDWR, 0644, &attr);if (mq == (mqd_t)-1) {perror("mq_open");exit(EXIT_FAILURE);}printf("Message queue created/opened successfully.\n");// 关闭消息队列if (mq_close(mq) == -1) {perror("mq_close");exit(EXIT_FAILURE);}// 删除消息队列(仅在不再需要时)if (mq_unlink(queue_name) == -1) {perror("mq_unlink");exit(EXIT_FAILURE);}return 0;
}

3.2 消息的发送与接收

  • mq_send函数:用于向消息队列发送消息,参数包括消息队列描述符、消息指针、消息长度及消息优先级等。
  • mq_receive函数:用于从消息队列接收消息,参数包括消息队列描述符、消息缓冲区指针、缓冲区长度及消息优先级指针等。
  • 示例代码:展示如何使用mq_send和mq_receive函数进行消息的发送与接收,并处理错误情况。
#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>int main() {mqd_t mq;const char *queue_name = "/test_queue";char buffer[256];unsigned int prio;// 打开消息队列mq = mq_open(queue_name, O_RDWR);if (mq == (mqd_t)-1) {perror("mq_open");exit(EXIT_FAILURE);}// 发送消息const char *message = "Hello, POSIX MQ!";if (mq_send(mq, message, strlen(message) + 1, 1) == -1) { // 优先级为1perror("mq_send");exit(EXIT_FAILURE);}printf("Message sent: %s\n", message);// 接收消息if (mq_receive(mq, buffer, sizeof(buffer), &prio) == -1) {perror("mq_receive");exit(EXIT_FAILURE);}printf("Message received: %s, Priority: %u\n", buffer, prio);// 关闭消息队列if (mq_close(mq) == -1) {perror("mq_close");exit(EXIT_FAILURE);}return 0;
}

3.3 消息队列的关闭与删除

  • mq_close函数:用于关闭消息队列描述符,释放进程持有的资源。
  • mq_unlink函数:用于删除消息队列名称,当所有进程关闭队列后,队列将被销毁。
  • 示例代码:展示如何使用mq_close和mq_unlink函数关闭和删除消息队列。
#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>int main() {mqd_t mq;const char *queue_name = "/test_queue";struct mq_attr attr, new_attr;// 打开消息队列mq = mq_open(queue_name, O_RDWR);if (mq == (mqd_t)-1) {perror("mq_open");exit(EXIT_FAILURE);}// 获取当前属性if (mq_getattr(mq, &attr) == -1) {perror("mq_getattr");exit(EXIT_FAILURE);}printf("Original attributes:\n");printf("Flags: %ld\n", attr.mq_flags);printf("Max messages: %ld\n", attr.mq_maxmsg);printf("Message size: %ld\n", attr.mq_msgsize);printf("Current messages: %ld\n", attr.mq_curmsgs);// 修改属性(注意:mq_maxmsg和mq_msgsize在创建后不能修改)new_attr = attr;new_attr.mq_flags = O_NONBLOCK; // 设置为非阻塞模式if (mq_setattr(mq, &new_attr, NULL) == -1) {perror("mq_setattr");exit(EXIT_FAILURE);}// 验证属性修改if (mq_getattr(mq, &attr) == -1) {perror("mq_getattr");exit(EXIT_FAILURE);}printf("\nModified attributes:\n");printf("Flags: %ld\n", attr.mq_flags);// 关闭消息队列if (mq_close(mq) == -1) {perror("mq_close");exit(EXIT_FAILURE);}return 0;
}

3.4 消息队列属性的获取与设置

  • mq_getattr函数:用于获取消息队列的属性,如最大消息数、单消息最大长度及当前消息数等。
  • mq_setattr函数:用于设置消息队列的属性,如阻塞标志等。
  • 示例代码:展示如何使用mq_getattr和mq_setattr函数获取和设置消息队列的属性,详见3.3.

四、POSIX消息队列的异步通知机制

4.1 异步通知概述

异步通知机制是POSIX消息队列中一种高效的事件驱动机制,它允许进程在消息队列中有新消息到达时,通过信号或线程回调的方式接收通知,从而减少进程阻塞时间,提高系统响应速度。这种机制特别适用于需要实时处理消息的场景,例如网络通信、实时监控系统等。通过异步通知,进程可以在不主动轮询消息队列的情况下,及时获取新消息,从而节省CPU资源,提高系统整体性能。

在实际应用中,异步通知机制可以显著提升系统的并发处理能力。例如,在一个多任务操作系统中,多个进程可能同时等待来自不同消息队列的消息。如果采用传统的阻塞式读取方式,每个进程都需要不断地检查消息队列,这将导致大量的CPU时间浪费。而通过异步通知机制,进程可以在消息到达时立即被唤醒,从而高效地处理消息。

4.2 sigevent结构体

sigevent结构体是POSIX标准中用于指定异步通知方式的关键数据结构。它定义了当消息队列中有新消息到达时,系统应如何通知进程。sigevent结构体包含以下几个重要字段:

  • sigev_notify:指定通知方式,常见的取值包括SIGEV_SIGNAL(通过信号通知)和SIGEV_THREAD(通过线程回调通知)。
  • sigev_signo:当sigev_notifySIGEV_SIGNAL时,指定用于通知的信号编号。
  • sigev_notify_function:当sigev_notifySIGEV_THREAD时,指定用于通知的线程回调函数。
  • sigev_notify_attributes:当sigev_notifySIGEV_THREAD时,指定线程回调函数的属性。
  • sigev_value:传递给通知处理函数的数据,通常用于传递上下文信息。

通过合理配置sigevent结构体,开发者可以灵活地选择适合应用场景的通知方式。例如,在实时系统中,可能更倾向于使用信号通知,以确保消息处理的及时性;而在多线程应用中,线程回调可能更为合适,因为它可以避免信号处理函数的复杂性。

4.3 mq_notify函数

mq_notify函数是POSIX消息队列中用于注册异步通知事件的核心函数。它的主要作用是将一个sigevent结构体与指定的消息队列关联起来,当该队列中有新消息到达时,系统会根据sigevent结构体的设置,通过信号或线程回调的方式通知进程。

mq_notify函数的原型如下:

int mq_notify(mqd_t mqdes, const struct sigevent *sevp);

其中,mqdes是消息队列的描述符,sevp是指向sigevent结构体的指针。如果sevpNULL,则表示取消当前进程对该消息队列的异步通知注册。

使用mq_notify函数时,需要注意以下几点:

  1. 单进程注册:每个消息队列在同一时间只能有一个进程注册异步通知。如果另一个进程尝试注册,mq_notify将返回EBUSY错误。
  2. 通知触发条件:异步通知仅在消息队列从空变为非空时触发。如果消息队列中已有消息,新消息的到达不会触发通知。
  3. 重复注册:在接收到通知后,进程需要重新调用mq_notify函数以重新注册异步通知,否则后续消息到达时将不会触发通知。

4.4 示例代码

以下是一个使用mq_notify函数的示例代码:

#include <mqueue.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>void handle_notification(union sigval sv) {printf("New message arrived in the queue.\n");// 重新注册异步通知mq_notify(sv.sival_int, &(struct sigevent){.sigev_notify = SIGEV_THREAD, .sigev_notify_function = handle_notification, .sigev_value.sival_int = sv.sival_int});
}int main() {mqd_t mqdes = mq_open("/my_queue", O_RDONLY);if (mqdes == (mqd_t)-1) {perror("mq_open");exit(EXIT_FAILURE);}struct sigevent sev = {.sigev_notify = SIGEV_THREAD,.sigev_notify_function = handle_notification,.sigev_value.sival_int = mqdes};if (mq_notify(mqdes, &sev) == -1) {perror("mq_notify");exit(EXIT_FAILURE);}while (1) {// 主循环,等待通知sleep(1);}mq_close(mqdes);return 0;
}

在这个示例中,当消息队列/my_queue中有新消息到达时,系统会调用handle_notification函数进行处理,并在处理完成后重新注册异步通知,以确保后续消息能够继续触发通知。

下面是另一个示例代码,展示如何使用mq_notify函数注册异步通知事件,并处理信号或线程回调。

#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>mqd_t mq;
const char *queue_name = "/test_queue";void notify_handler(union sigval sv) {char buffer[256];unsigned int prio;mqd_t mq = *(mqd_t *)sv.sival_ptr;if (mq_receive(mq, buffer, sizeof(buffer), &prio) == -1) {perror("mq_receive");return;}printf("Async message received: %s, Priority: %u\n", buffer, prio);
}int main() {struct sigevent sev;// 打开消息队列mq = mq_open(queue_name, O_RDWR);if (mq == (mqd_t)-1) {perror("mq_open");exit(EXIT_FAILURE);}// 设置异步通知sev.sigev_notify = SIGEV_THREAD;sev.sigev_notify_function = notify_handler;sev.sigev_notify_attributes = NULL;sev.sigev_value.sival_ptr = &mq;if (mq_notify(mq, &sev) == -1) {perror("mq_notify");exit(EXIT_FAILURE);}// 模拟发送消息以触发通知(在实际应用中,这可能由另一个进程完成)const char *message = "Async Message";if (mq_send(mq, message, strlen(message) + 1, 2) == -1) { // 优先级为2perror("mq_send");exit(EXIT_FAILURE);}// 等待一段时间以便处理异步通知(在实际应用中,这通常是一个事件循环)sleep(5);// 关闭消息队列if (mq_close(mq) == -1) {perror("mq_close");exit(EXIT_FAILURE);}return 0;
}

五、POSIX消息队列的多线程安全特性

5.1 多线程安全概述

多线程安全是指多个线程可以同时访问和修改共享数据,而不会导致数据不一致或程序崩溃。在多线程编程中,线程安全问题主要源于对共享资源的并发访问,如果没有适当的同步机制,可能会导致数据竞争、死锁或程序异常终止。POSIX消息队列作为一种进程间通信机制,其操作是线程安全的,可以在多线程环境中安全使用。这意味着多个线程可以同时调用mq_send()mq_receive()等函数,而不会导致消息队列内部状态的不一致或数据损坏。

5.2 线程安全实现机制

POSIX消息队列通过内部锁机制确保多线程环境下的数据一致性。具体来说,当多个线程同时访问消息队列时,内核会自动对消息队列的操作进行加锁,确保每个操作都是原子的。这种锁机制通常包括互斥锁(mutex)和条件变量(condition variable),用于保护消息队列的读写操作。例如,当一个线程正在向消息队列发送消息时,其他线程的发送或接收操作会被阻塞,直到当前操作完成。这种机制有效地避免了数据竞争,确保了消息队列的线程安全性。

此外,POSIX消息队列还支持优先级机制,允许开发者根据消息的优先级进行排序和处理。在多线程环境中,这种优先级机制同样受到锁的保护,确保高优先级消息能够被及时处理,而不会因为线程竞争导致延迟。

5.3 示例代码

以下是一个简单的示例代码,展示如何在多线程环境中使用POSIX消息队列进行线程间通信。该示例创建了两个线程,一个线程负责发送消息,另一个线程负责接收消息。

#include <stdio.h>
#include <pthread.h>
#include <mqueue.h>
#include <string.h>
#include <stdlib.h>#define QUEUE_NAME "/my_queue"
#define MAX_MSG_SIZE 1024void* sender_thread(void* arg) {mqd_t mq = mq_open(QUEUE_NAME, O_WRONLY);if (mq == (mqd_t)-1) {perror("mq_open");exit(EXIT_FAILURE);}char buffer[MAX_MSG_SIZE];for (int i = 0; i < 10; i++) {snprintf(buffer, MAX_MSG_SIZE, "Message %d", i);if (mq_send(mq, buffer, strlen(buffer) + 1, 0) == -1) {perror("mq_send");}printf("Sent: %s\n", buffer);}mq_close(mq);return NULL;
}void* receiver_thread(void* arg) {mqd_t mq = mq_open(QUEUE_NAME, O_RDONLY);if (mq == (mqd_t)-1) {perror("mq_open");exit(EXIT_FAILURE);}char buffer[MAX_MSG_SIZE];for (int i = 0; i < 10; i++) {if (mq_receive(mq, buffer, MAX_MSG_SIZE, NULL) == -1) {perror("mq_receive");}printf("Received: %s\n", buffer);}mq_close(mq);return NULL;
}int main() {pthread_t sender, receiver;struct mq_attr attr;attr.mq_flags = 0;attr.mq_maxmsg = 10;attr.mq_msgsize = MAX_MSG_SIZE;attr.mq_curmsgs = 0;mqd_t mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0644, &attr);if (mq == (mqd_t)-1) {perror("mq_open");exit(EXIT_FAILURE);}pthread_create(&sender, NULL, sender_thread, NULL);pthread_create(&receiver, NULL, receiver_thread, NULL);pthread_join(sender, NULL);pthread_join(receiver, NULL);mq_close(mq);mq_unlink(QUEUE_NAME);return 0;
}

在这个示例中,sender_thread线程负责向消息队列发送消息,而receiver_thread线程负责从消息队列接收消息。由于POSIX消息队列的线程安全特性,两个线程可以同时操作消息队列而不会导致数据不一致或程序崩溃。

六、POSIX消息队列的案例分析

6.1 案例一:生产者-消费者模型

  • 场景描述:生产者线程不断生成消息并发送到消息队列,消费者线程从消息队列中接收消息并处理。
  • 实现步骤:创建消息队列、启动生产者与消费者线程、发送与接收消息、关闭与删除消息队列。
  • 代码示例:展示生产者-消费者模型的完整代码实现,包括线程创建、消息发送与接收及资源释放等。
#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>#define QUEUE_NAME "/prod_cons_queue"
#define NUM_MESSAGES 10mqd_t mq;void *producer(void *arg) {for (int i = 0; i < NUM_MESSAGES; ++i) {char message[64];snprintf(message, sizeof(message), "Message %d", i);if (mq_send(mq, message, strlen(message) + 1, 1) == -1) { // 优先级为1perror("mq_send");exit(EXIT_FAILURE);}printf("Producer sent: %s\n", message);sleep(1); // 模拟生产间隔}return NULL;
}void *consumer(void *arg) {char buffer[64];unsigned int prio;for (int i = 0; i < NUM_MESSAGES; ++i) {if (mq_receive(mq, buffer, sizeof(buffer), &prio) == -1) {perror("mq_receive");exit(EXIT_FAILURE);}printf("Consumer received: %s, Priority: %u\n", buffer, prio);}return NULL;
}int main() {pthread_t prod_thread, cons_thread;struct mq_attr attr;attr.mq_flags = 0;attr.mq_maxmsg = 10;attr.mq_msgsize = 64;attr.mq_curmsgs = 0;mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0644, &attr);if (mq == (mqd_t)-1) {perror("mq_open");exit(EXIT_FAILURE);}pthread_create(&prod_thread, NULL, producer, NULL);pthread_create(&cons_thread, NULL, consumer, NULL);pthread_join(prod_thread, NULL);pthread_join(cons_thread, NULL);mq_close(mq);mq_unlink(QUEUE_NAME);return 0;
}

6.2 案例二:日志处理系统

  • 场景描述:日志处理系统需要实时收集、存储及分析系统日志。使用POSIX消息队列实现日志的异步传输与处理。
  • 实现步骤:创建日志消息队列、日志生成线程将日志消息发送到队列、日志处理线程从队列中接收日志并进行分析与存储。
  • 代码示例:展示日志处理系统的完整代码实现,包括日志生成、传输、处理及存储等。
#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <time.h>#define QUEUE_NAME "/log_queue"
#define MAX_LOG_SIZE 256
#define NUM_LOGS 10mqd_t mq;void *log_generator(void *arg) {for (int i = 0; i < NUM_LOGS; i++) {char log_message[MAX_LOG_SIZE];time_t now;struct tm *tm_info;time(&now);tm_info = localtime(&now);strftime(log_message, sizeof(log_message), "%Y-%m-%d %H:%M:%S", tm_info);snprintf(log_message + strlen(log_message), sizeof(log_message) - strlen(log_message), " - Log entry %d", i);if (mq_send(mq, log_message, strlen(log_message) + 1, 0) == -1) {perror("mq_send");continue;}printf("Generated log: %s\n", log_message);sleep(1); // Simulate time between log entries}return NULL;
}void *log_processor(void *arg) {char buffer[MAX_LOG_SIZE];unsigned int prio;while (1) {if (mq_receive(mq, buffer, sizeof(buffer), &prio) == -1) {perror("mq_receive");continue;}printf("Processed log: %s\n", buffer);// Simulate log storage or analysis// In a real application, you would write this to a file or database}return NULL;
}int main() {pthread_t generator_thread, processor_thread;struct mq_attr attr;// 设置消息队列属性attr.mq_flags = 0;attr.mq_maxmsg = 10;attr.mq_msgsize = MAX_LOG_SIZE;attr.mq_curmsgs = 0;// 创建或打开消息队列mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0644, &attr);if (mq == (mqd_t)-1) {perror("mq_open");exit(EXIT_FAILURE);}// 创建日志生成线程pthread_create(&generator_thread, NULL, log_generator, NULL);// 创建日志处理线程pthread_create(&processor_thread, NULL, log_processor, NULL);// 等待日志生成线程完成pthread_join(generator_thread, NULL);// 注意:日志处理线程设计为无限循环,需要外部干预来终止// 在实际应用中,你可能需要引入信号处理或其他机制来优雅地关闭线程// 清理消息队列if (mq_close(mq) == -1) {perror("mq_close");exit(EXIT_FAILURE);}if (mq_unlink(QUEUE_NAME) == -1) {perror("mq_unlink");exit(EXIT_FAILURE);}return 0;
}

6.3 案例三:实时监控系统

  • 场景描述
    在现代分布式系统中,实时监控系统是确保系统稳定性和性能的关键组件。该系统需要实时收集、处理及展示系统状态信息,包括CPU使用率、内存占用、网络流量等关键指标。为了确保监控数据的及时性和高效性,采用异步通信机制是必要的。POSIX消息队列作为一种高效的进程间通信(IPC)机制,能够实现监控数据的异步传输与处理,从而避免数据采集与处理之间的直接耦合,提高系统的响应速度和可扩展性。

  • 实现步骤

  1. 创建监控数据消息队列

    • 使用mq_open函数创建一个POSIX消息队列,指定队列名称、权限标志和最大消息大小。例如:
      mqd_t mq = mq_open("/monitor_queue", O_CREAT | O_RDWR, 0666, NULL);
      if (mq == (mqd_t)-1) {perror("mq_open");exit(EXIT_FAILURE);
      }
      
    • 设置队列属性,如最大消息数量和消息优先级,以满足监控系统的需求。
  2. 数据采集线程将监控数据发送到队列

    • 创建一个独立的数据采集线程,负责定期收集系统状态信息。例如,使用pthread_create函数创建线程:
      pthread_t collector_thread;
      if (pthread_create(&collector_thread, NULL, data_collector, NULL) != 0) {perror("pthread_create");exit(EXIT_FAILURE);
      }
      
    • 在数据采集线程中,将收集到的监控数据封装成消息,并通过mq_send函数发送到消息队列。例如:
      struct monitor_data data = { /* 填充监控数据 */ };
      if (mq_send(mq, (const char *)&data, sizeof(data), 0) == -1) {perror("mq_send");
      }
      
  3. 数据处理线程从队列中接收数据并进行分析与展示

    • 创建另一个独立的数据处理线程,负责从消息队列中接收数据并进行处理。例如:
      pthread_t processor_thread;
      if (pthread_create(&processor_thread, NULL, data_processor, NULL) != 0) {perror("pthread_create");exit(EXIT_FAILURE);
      }
      
    • 在数据处理线程中,使用mq_receive函数从消息队列中接收数据,并进行相应的分析与展示。例如:
      struct monitor_data data;
      ssize_t bytes_read = mq_receive(mq, (char *)&data, sizeof(data), NULL);
      if (bytes_read > 0) {// 进行数据分析与展示analyze_and_display(data);
      } else {perror("mq_receive");
      }
      
  4. 资源清理与线程管理

    • 在系统关闭或不再需要监控时,使用mq_closemq_unlink函数关闭和删除消息队列,确保资源得到正确释放。例如:
      mq_close(mq);
      mq_unlink("/monitor_queue");
      
    • 使用pthread_join函数等待数据采集和数据处理线程完成,确保线程安全退出。例如:
      pthread_join(collector_thread, NULL);
      pthread_join(processor_thread, NULL);
      

通过上述步骤,实时监控系统能够高效地收集、传输和处理系统状态信息,确保系统的稳定性和性能。

  • 代码示例:展示实时监控系统的完整代码实现,包括数据采集、传输、处理及展示等。
#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>#define QUEUE_NAME "/monitor_queue"
#define MAX_DATA_SIZE 256
#define NUM_DATA_POINTS 100mqd_t mq;typedef struct {double cpu_usage;double memory_usage;time_t timestamp;
} SystemData;void *data_collector(void *arg) {for (int i = 0; i < NUM_DATA_POINTS; i++) {SystemData data;// Simulate CPU and memory usagedata.cpu_usage = (double)(rand() % 100);data.memory_usage = (double)(rand() % 100);data.timestamp = time(NULL);if (mq_send(mq, (char *)&data, sizeof(data), 0) == -1) {perror("mq_send");continue;}printf("Collected data: CPU=%.2f%%, Memory=%.2f%%\n", data.cpu_usage, data.memory_usage);usleep(100000); // Simulate time between data points}return NULL;
}void *data_processor(void *arg) {char buffer[MAX_DATA_SIZE];unsigned int prio;while (1) {if (mq_receive(mq, buffer, sizeof(buffer), &prio) == -1) {perror("mq_receive");continue;}SystemData *data = (SystemData *)buffer;printf("Processed data: CPU=%.2f%%, Memory=%.2f%%, Timestamp=%ld\n",data->cpu_usage, data->memory_usage, data->timestamp);// Simulate data analysis or visualization// In a real application, you might analyze and display this data}return NULL;
}int main() {pthread_t collector_thread, processor_thread;struct mq_attr attr;// 设置消息队列属性attr.mq_flags = 0;attr.mq_maxmsg = 10;attr.mq_msgsize = sizeof(SystemData);attr.mq_curmsgs = 0;// 创建或打开消息队列mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0644, &attr);if (mq == (mqd_t)-1) {perror("mq_open");exit(EXIT_FAILURE);}// 创建数据采集线程pthread_create(&collector_thread, NULL, data_collector, NULL);// 创建数据处理线程pthread_create(&processor_thread, NULL, data_processor, NULL);// 等待数据采集线程完成pthread_join(collector_thread, NULL);// 注意:数据处理线程设计为无限循环,需要外部干预来终止// 在实际应用中,你可能需要引入信号处理或其他机制来优雅地关闭线程// 清理消息队列if (mq_close(mq) == -1) {perror("mq_close");exit(EXIT_FAILURE);}if (mq_unlink(QUEUE_NAME) == -1) {perror("mq_unlink");exit(EXIT_FAILURE);}return 0;
}

七、POSIX消息队列的性能优化与测试

7.1 性能优化策略

  • 减少内存拷贝:通过使用零拷贝技术或内存映射文件减少消息传输过程中的内存拷贝次数。零拷贝技术可以通过直接在内核空间和用户空间之间传递数据指针来实现,避免了数据在用户空间和内核空间之间的多次拷贝。例如,在Linux系统中,可以使用sendfile()系统调用实现零拷贝传输。内存映射文件(mmap)则通过将文件映射到进程的地址空间,使得进程可以直接访问文件数据,而无需通过系统调用进行数据拷贝。

  • 提高并发处理能力:通过优化锁机制、使用无锁数据结构或增加线程数等方式提高系统的并发处理能力。锁机制的优化可以通过使用读写锁(pthread_rwlock_t)或自旋锁(pthread_spinlock_t)来减少锁争用。无锁数据结构如无锁队列(lock-free queue)可以在高并发场景下显著提高性能。此外,合理增加线程数,利用多核CPU的并行处理能力,也可以有效提升系统的并发处理能力。

  • 优化消息队列属性:根据应用场景合理设置消息队列的最大消息数、单消息最大长度等属性,避免资源浪费。例如,在实时系统中,可以通过mq_setattr()函数设置消息队列的最大消息数(mq_maxmsg)和单消息最大长度(mq_msgsize),以确保系统在高负载下仍能保持稳定的性能。同时,合理设置消息队列的优先级(mq_prio)可以确保高优先级消息能够及时处理。

7.2 性能测试方法

  • 基准测试:测试不同消息大小、优先级及线程数下的系统性能基准值。基准测试可以通过发送和接收不同大小的消息(如1KB、10KB、100KB等),记录消息的传输延迟和吞吐量。同时,可以测试不同优先级消息的处理顺序,确保高优先级消息能够优先处理。此外,通过调整线程数(如1、4、8、16等),测试系统的并发处理能力。

  • 压力测试:模拟高并发场景下的系统性能表现,评估系统的稳定性和可靠性。压力测试可以通过增加并发线程数或消息发送频率,模拟系统在高负载下的表现。例如,可以模拟1000个并发线程同时发送消息,观察系统的响应时间、消息丢失率及CPU和内存的使用情况。通过压力测试,可以发现系统的性能瓶颈,并进行相应的优化。

  • 对比测试:与其他IPC机制(如管道、共享内存等)进行对比测试,评估POSIX消息队列的性能优势。对比测试可以通过在同一硬件环境下,分别测试POSIX消息队列、管道和共享内存的传输延迟、吞吐量及资源占用情况。例如,可以测试在相同消息大小和并发线程数下,POSIX消息队列与管道的性能差异。通过对比测试,可以评估POSIX消息队列在不同应用场景下的适用性及性能优势。

八、结论与展望

8.1 研究成果总结

本文深入探讨了POSIX消息队列的原理、特性及其在Linux系统中的应用。通过构建生产者-消费者模型、日志处理系统及实时监控系统等案例,展示了POSIX消息队列在不同应用场景下的实际应用效果。研究结果表明,POSIX消息队列以其高效、灵活和可移植性在分布式系统中发挥关键作用。

8.2 存在问题与不足

尽管POSIX消息队列具有诸多优点,但在实际应用中仍存在一些问题与不足。例如,在高并发场景下,消息队列的锁机制可能成为性能瓶颈;在消息量较大时,内存占用可能较高;在跨平台开发中,不同操作系统对POSIX消息队列的支持程度可能存在差异等。

8.3 未来研究方向

针对POSIX消息队列存在的问题与不足,未来研究可从以下几个方面展开:一是优化锁机制,提高并发处理能力;二是研究内存管理策略,降低内存占用;三是加强跨平台兼容性研究,提高代码的可移植性;四是探索POSIX消息队列与其他IPC机制的融合应用,发挥各自优势,提升系统整体性能。


研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)


参考文献

[1] 消息队列(MQ)详细介绍
[2] 28个案例问题分析—16—消息队列的作用和意义–RabbitMq_rabbitmq消息队列的作用-CSDN博客
[3] posix 消息队列_mob64ca1415bcee的技术博客_51CTO博客
[4] POSIX消息队列
[5] 消息队列原理
[6] POSIX消息队列
[7] Linux进程间通信中的POSIX消息队列
[8] POSIX消息队列详解与示例
[9] 第52章 POSIX 消息队列
[10] POSIX 消息队列
[11] 《网络编程卷2:进程间通信》第五章:POSIX消息队列深度解析与高性能实践

相关文章:

[学习]POSIX消息队列的原理与案例分析(完整示例代码)

POSIX消息队列的原理与案例分析 文章目录 POSIX消息队列的原理与案例分析摘要关键词一、引言1.1 研究背景与意义1.2 国内外研究现状1.3 研究内容与方法 二、POSIX消息队列的基本原理2.1 消息队列概述2.2 POSIX消息队列的特性2.2 POSIX消息队列的特性2.3 POSIX消息队列的内部机制…...

IDC数据中心动力环境监控系统解决方案

文档围绕 IDC 数据中心动力环境监控系统解决方案展开,先介绍数据中心分级,包括国家规范的 A/B/C 级和美国 TIA-942 标准的 Tier1-Tier4 级,强调动环监控对数据中心的重要性。接着阐述系统架构,涵盖底端设备层、采集层、接入层、服务层、应用层,具备数据采集、分析、可视化…...

WebSphere Application Server(WAS)8.5.5教程第五讲

续前篇&#xff01; 一、Web 应用部署与类加载策略 Web 应用部署与类加载策略是 WebSphere Application Server&#xff08;WAS&#xff09;日常管理的核心部分&#xff0c;尤其对运行大型企业级 Java 应用&#xff08;如 BAW&#xff09;非常关键。本讲将分两部分讲解&#…...

Golang中的runtime.LockOSThread 和 runtime.UnlockOSThread

在runtime中有runtime.LockOSThread 和 runtime.UnlockOSThread 两个函数&#xff0c;这两个函数有什么作用呢&#xff1f;我们看一下标准库中对它们的解释。 runtime.LockOSThread // LockOSThread wires the calling goroutine to its current operating system thread. // T…...

计算圆周率 (python)

使用模特卡罗方法&#xff08;模拟法&#xff09;&#xff0c;模拟撒点100000次&#xff0c;计算圆周率π 输入格式: 一个整数&#xff0c;表示随机数种子 输出格式: 计算的π值&#xff0c;结果小数点后保留5位数字 输入样例: 在这里给出一组输入。例如&#xff1a; 10…...

机器学习EM算法原理及推导

在机器学习与统计推断中&#xff0c;我们经常会遇到“缺失数据”或“潜在变量”&#xff08;latent variables&#xff09;的情形&#xff1a;样本并非完全可观测&#xff0c;而部分信息被隐藏或丢失。这种情况下&#xff0c;直接对观测数据做极大似然估计&#xff08;Maximum …...

Linux项目部署全攻略:从环境搭建到前后端部署实战

Linux项目部署全攻略&#xff1a;从环境搭建到前后端部署实战 注&#xff1a;根据黑马程序员javawebAI视频课程总结&#xff1a; 视频地址 详细讲义地址 一、Linux基础入门&#xff1a;为什么选择Linux&#xff1f; 要成为一名Java开发工程师&#xff0c;掌握Linux是企业级…...

人工智能重塑医疗健康:从辅助诊断到个性化治疗的全方位变革

人工智能正在以前所未有的速度改变着医疗健康领域&#xff0c;从影像诊断到药物研发&#xff0c;从医院管理到远程医疗&#xff0c;AI 技术已渗透到医疗服务的各个环节。本文将深入探讨人工智能如何赋能医疗健康产业&#xff0c;分析其在医学影像、临床决策、药物研发、个性化医…...

ubuntu系统 | dify+ollama+deepseek搭建本地应用

1、安装 Ollama 下载并安装 Ollama (llm) wangqiangwangqiang:~$ curl -fsSL https://ollama.ai/install.sh | bash >>> Installing ollama to /usr/local >>> Downloading Linux amd64 bundle0.3% curl -fsSL https://ollama.ai/install.sh &#xff08;下…...

NHANES最新指标推荐:C-DII

文章题目&#xff1a;Non-linear relationship between the childrens dietary inflammatory index and asthma risk: identifying a critical inflection point in US children and adolescents DOI&#xff1a;10.3389/fnut.2025.1538378 中文标题&#xff1a;儿童饮食炎症指…...

【PhysUnits】4.4 零类型(Z0)及其算术运算(zero.rs)

一、源码 该代码定义了一个类型系统中的零类型Z0&#xff0c;并为其实现了基本的算术运算&#xff08;加法、减法、乘法、除法&#xff09;。这是一个典型的类型级编程示例&#xff0c;使用Rust的类型系统在编译期进行数学运算。 //! 零类型(Z0)及其算术运算实现 //! //! 本…...

【大模型面试每日一题】Day 23:如何设计一个支持多模态(文本+图像)的大模型架构?

【大模型面试每日一题】Day 23&#xff1a;如何设计一个支持多模态&#xff08;文本图像&#xff09;的大模型架构&#xff1f; &#x1f4cc; 题目重现 &#x1f31f;&#x1f31f;&#x1f31f; 面试官&#xff1a;我们需要构建一个同时处理文本和图像的多模态大模型&#…...

BUUCTF PWN刷题笔记(持续更新!!)

ciscn_2019_c_1 64位&#xff0c;没有开启保护。点进去没发现明显的漏洞函数&#xff0c;考虑泄露libc基地址的rop构造。先看看有多少gadget 估计也够用了。puts函数只接受一个参数&#xff0c;观看汇编看看用的哪个寄存器传输的参数。 用的是edi。但是我们怎么找到so的版本呢…...

Tare使用MCP|Win11安装UV

servers/src/git at main modelcontextprotocol/servers GitHub 进入Installation | uv 打开powershell 运行 &#xff08;如果要删除 文章中也有删除的链接&#xff09; powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" …...

小白的进阶之路-人工智能从初步到精通pytorch的基本流程详解-1

Pytorch大致流程一览表: 主题内容1 准备数据数据几乎可以是任何东西,但在开始之前,我们将创建一条简单的直线2 建立模型在这里,我们将创建一个模型来学习数据中的模式,我们还将选择损失函数,优化器并构建一个训练循环。3 拟合模型我们已经有了数据和模型,现在让模型尝试…...

linux kernel 编译

1. 获取内核源码 从官方仓库获取: 访问kernel.org下载所需版本的Linux内核源码压缩包(如linux-x.y.z.tar.xz)。或者使用Git克隆特定版本: bash git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.gitcd linux-stablegit checkout vX.Y.Z # 切…...

【氮化镓】偏置对GaN HEMT 单粒子效应的影响

2025年5月19日,西安电子科技大学的Ling Lv等人在《IEEE Transactions on Electron Devices》期刊发表了题为《Single-Event Effects of AlGaN/GaN HEMTs Under Different Biases》的文章,基于实验和TCAD仿真模拟方法,研究了单粒子效应对关断状态、半开启状态和开启状态下AlG…...

Assistants API

一、前言 0.1、从轰动一时的 OpenAI DevDay 说起 2023 年 11 月 6 日,OpenAI DevDay 发表了一系列新能力,其中包括:GPT Store 和 Assistants API 这一波操作一度被认为是创业公司终结者 0.2、GPTs 和 Assistants API 本质是降低开发门槛 可操控性和易用性之间的权衡与折中…...

AcWing 223. 阿九大战朱最学——扩展欧几里得算法

题目来源 223. 阿九大战朱最学 - AcWing题库 题目描述 自从朱最学搞定了 QQ 农场以后&#xff0c;就开始捉摸去 QQ 牧场干些事业&#xff0c;不仅在自己的牧场养牛&#xff0c;还到阿九的牧场放牛&#xff01; 阿九很生气&#xff0c;有一次朱最学想知道阿九牧场奶牛的数量…...

开发指南116-font-size: 0的使用

平台前台的css样式里有几个地方用到了font-size: 0&#xff0c;这是个使用小技巧。原理说明&#xff1a;font-size 属性用于定义元素中文本的大小。当设置 font-size: 0 时&#xff0c;意味着该元素内的文本将不占据空间。当元素的 font-size 设置为零时&#xff0c;该元素内的…...

算法-数对的使用

1、数对可用于数组排序中&#xff0c;并且可记忆化排序前的元素下标 #include<iostream> #include<string> #include<bits/stdc.h> using namespace std; typedef long long ll; const int N 2e5 10; pair<int, int> a[N]; void solve() {ll n;cin …...

EmoBox:我与 CodeBuddy 共创的 Emoji 表情分类小工具

我正在参加CodeBuddy「首席试玩官」内容创作大赛&#xff0c;本文所使用的 CodeBuddy 免费下载链接&#xff1a;腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 最近我萌生了一个想法&#xff0c;想做一个小而美的工具——一个叫「EmoBox」的 emoji 表情分类应用&#xff0…...

力扣HOT100之二叉树:199. 二叉树的右视图

这道题没啥好说的&#xff0c;首先定义一个向量来保存每一层的最后一个元素&#xff0c;直接用层序遍历&#xff08;广度优先搜索&#xff09;遍历二叉树&#xff0c;然后将每一层的最后一个元素加入到这个向量中即可。属于是二叉树层序遍历的模板题。 /*** Definition for a …...

力扣992做题笔记

左神做法的理论依据 我们可以通过 集合的包含关系 和 具体示例枚举 来直观理解这一推导过程。以下结合题目示例 1 进行详细说明&#xff1a; 示例 1 分析 输入&#xff1a;nums [1,2,1,2,3], k 2 目标&#xff1a;计算恰好包含 2 种不同整数 的子数组个数。 步骤一集合 A…...

特征值与特征向量的计算——PCA的数学基础

特征值与特征向量 定义 令 A {\bm A} A为 n n n \times n nn矩阵&#xff0c;如果存在非零向量 x {\bm x} x使得 A x λ x (1) {\bm A}{\bm x} \lambda {\bm x}\tag{1} Axλx(1) 成立&#xff0c;则称数 λ \lambda λ是矩阵 A {\bm A} A的特征值&#xff0c;称非零向量 x…...

vue2.0 的计算属性

个人简介 &#x1f468;‍&#x1f4bb;‍个人主页&#xff1a; 魔术师 &#x1f4d6;学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全栈发展 &#x1f6b4;个人状态&#xff1a; 研发工程师&#xff0c;现效力于政务服务网事业 &#x1f1e8;&#x1f1f3;人生格言&…...

【数根】2022-1-24

缘由程序设计 -- 数根&#xff08;2&#xff09;-编程语言-CSDN问答 void 数根() {//缘由https://ask.csdn.net/questions/7635593?spm1005.2025.3001.5141std::string n;int m 0, a 0;std::cin >> n;while (n[a] ! \0)m n[a] - 0;a 0;while (m)a m - m / 10 * 10…...

【Android】一键创建Keystore + Keystore 参数说明 + 查询SHA256(JDK Keytool Keystore)

一键创建 Android Keystore 的实用方法与参数详解 在 Android 应用开发与发布中&#xff0c;**Keystore&#xff08;签名文件&#xff09;**扮演着至关重要的角色。本文将介绍如何通过 .bat 脚本一键创建 Keystore 文件&#xff0c;并详细讲解每一个参数的含义&#xff0c;帮助…...

laravel 通过Validator::make验证后,如何拿到验证后的值

在 Laravel 中&#xff0c;通过 Validator::make 创建的验证器实例验证数据后&#xff0c;可以通过以下方式获取验证后的值&#xff1a; 使用 validate() 方法 调用验证器实例的 validate() 方法&#xff0c;会返回经过验证的数据数组。如果验证失败&#xff0c;该方法会抛出 V…...

centos把jar包配置成服务并设置开机自启

1.准备好jar包&#xff0c;启动路径&#xff0c;日志路径 2.编写启动脚步 vim /etc/systemd/system/test.service [Unit] Descriptionlapis Requiresnetwork.target remote-fs.target ##启动优先级&#xff0c;在下面的服务之后启动 Afterkafka.service zookeeper.service n…...

CentOS相关操作hub(更新中)

CentOS介绍&#xff1a; CentOS&#xff08;Community Enterprise Operating System&#xff09;是基于 Red Hat Enterprise Linux&#xff08;RHEL&#xff09;源代码编译的开源企业级操作系统&#xff0c;提供与 RHEL 二进制兼容的功能 完全兼容 RHEL&#xff0c;可直接使用…...

数据库(一):分布式数据库

定义 分布式数据库&#xff08;Distributed Database&#xff09; 是指&#xff1a; 数据分布在多个物理位置&#xff0c;但对用户透明&#xff0c;表现为一个统一逻辑数据库的系统。 结构模式&#xff08;三层模式扩展&#xff09; 层次作用对应实体用户层提供统一视图&…...

人工智能(AI)与BIM:建筑业创新实践的深度融合

一、BIM在建筑领域的发展现状与挑战 作为建筑数字化的核心技术&#xff0c;BIM通过三维模型集成建筑全生命周期信息&#xff0c;已成为工程设计、施工及运维的标准工作流程。当前&#xff0c;BIM在碰撞检测、施工模拟、成本管控等场景的应用已较为成熟&#xff0c;但行业仍面临…...

FIR数字滤波器设计与实现

低通滤波器的设计与实现 打开Matlab ,运行命令filterDesigner&#xff0c;选择FIR 最小二乘或者其它&#xff0c;设置采样频率&#xff0c;和低通滤波器截止频率。点击设计滤波器&#xff0c;如图1&#xff1a; 点击目标生成.C头文件&#xff0c;滤波器系数如下&#xff1a; …...

软件架构之-论高并发下的可用性技术

论高并发下的可用性技术 摘要正文摘要 ;2023年2月,本人所在集团公司承接了长三角地区某省渔船图纸电子化审查系统项目开发,该项目旨在为长三角地区渔船建造设计院、以及渔船审图机构提供一个便捷化的服务平台。在此项目中,我作为项目组成员参与了项目建设工作,并担任系统架…...

阻塞队列:线程安全与生产者消费者模型解析

一、阻塞队列 阻塞队列就是基于普通队列做出扩展 1.线程安全的 如果针对一个已经满了的队列进行入队列&#xff0c;此时入队列操作就会阻塞&#xff0c;一直阻塞到队列不满&#xff08;其他线程出队列元素&#xff09;之后 如果针对一个已经空了的队列进行出队列&#xff0c…...

【时时三省】(C语言基础)用函数实现模块化程序设计

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 为什么要用函数&#xff1f; 已经能够编写一些简单的C程序&#xff0c;但是如果程序的功能比较多&#xff0c;规模比较大&#xff0c;把所有的程序代码都写在一个主函数(main函数)中&#x…...

基于CATIA参数化圆锥建模的自动化插件开发实践——NX建模之圆锥体命令的参考与移植(一)

引言​​ 在CATIA二次开发领域&#xff0c;Python因其灵活性和丰富的库支持逐渐成为高效工具开发的首选语言。本文将以笔者开发的​​CATIA锥体自动化建模工具​​为例&#xff0c;参考NX软件中高效锥体创建命令&#xff0c;深度解析基于PySide6 GUI框架与pycatia接口库的集成…...

天才简史——Paolo Fiorini与他的速度障碍法

一、背景 第一次“认识”Paolo Fiorini教授是看了DMP的体积避障论文《Dynamic Movement Primitives: Volumetric Obstacle Avoidance》&#xff0c;而且他的学生Michele Ginesi将相关代码开源了&#xff0c;后来查阅了Paolo Fiorini相关资料才发现他竟然是速度障碍法的作者&am…...

第五天的尝试

目录 一、每日一言 二、练习题 三、效果展示 四、下次题目 五、总结 一、每日一言 毅力是永久的享受。 没有人是一座孤岛&#xff0c;每个人都是这块大陆的一部分。 二、练习题 import numpy as np import matplotlib.pyplot as plt array np.array([1,2,3,4,5]) plt.plot…...

大小端模式和消息的加密解密

大小端模式 知识点一 什么是大小端模式 // 大端模式 // 是指数据的高字节保存在内存的低地址中 // 而数据的低字节保存在内存的高地址中 // 这样的存储模式有点儿类似于把数据当作字符串顺序处理 // 地址由小向大增加,数据从高位往低位放 …...

(1) 查看端口状态

1. lsof 和 netstat 命令的区别 1.1 lsof 概念&#xff1a;只有在 root 的命令下才能执行&#xff0c;否则无内容显示&#xff1b;root 命令下显示完全 lsof -i: 8080 1.2 netstat 普通用户下显示不完全&#xff0c;root 命令下显示完全 netstat -tunlp | grep 8080 1.3…...

【C++]string模拟实现

#pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<assert.h> using namespace std; namespace liu {class string{public:using iterator char*;using const_iterator const char*;//string();//无参构造 string(const string&…...

Linux动静态库制作与原理

什么是库 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复用的代码。现实中每个程序都要依赖很多基础的底层库&#xff0c;不可能每个人的代码都从零开始&#xff0c;因此库的存在意义非同寻常。 本质上来说库是一种可执行代码的二进制形式&#xff0c;可以被操作系统…...

ArkUI Tab组件开发深度解析与应用指南

ArkUI Tab组件开发深度解析与应用指南 一、组件架构与核心能力 ArkUI的Tabs组件采用分层设计结构&#xff0c;由TabBar&#xff08;导航栏&#xff09;和TabContent&#xff08;内容区&#xff09;构成&#xff0c;支持底部、顶部、侧边三种导航布局模式。组件具备以下核心特…...

winrar 工具测试 下载 与安装

https://zhuanlan.zhihu.com/p/680852417 https://www.angusj.com/resourcehacker/#download 点击String Table&#xff0c;在展开列表中找到80:2052展开&#xff0c;删除1277行。点击右上方编译按钮&#xff0c;并保存。...

代码随想录算法训练营第四十四天

卡码网题目: 99. 岛屿数量100. 岛屿的最大面积 其他: 今日总结 往期打卡 99. 岛屿数量 跳转: 99. 岛屿数量 学习: 代码随想录公开讲解 问题: 给定一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的矩阵&#xff0c;你需要计算岛屿的数量。岛屿由水…...

每日Prompt:自拍生成摇头娃娃

提示词 将这张照片变成一个摇头娃娃&#xff1a;头部稍微放大&#xff0c;保持面部准确&#xff0c;身体卡通化。[把它放在书架上]。...

制作我的计算器

1. 界面布局 新建项目 MyCalculator&#xff0c;开始布局。 2. 静态布局 代码如下&#xff1a; // etc/pages/Index.ets Entry Component struct Index {build() {Column() {/*** 运算区*/Column() {TextInput({ text: 12x13 }).height(100%).fontSize(32).enabled(false).f…...

如何查看 Ubuntu开机是否需要密码

要查看 Ubuntu 开机是否需要密码&#xff0c;可以通过以下方法进行判断&#xff1a; 1. 检查自动登录设置 图形界面操作&#xff1a; 进入系统设置&#xff08;Settings&#xff09;→ 用户账户&#xff08;User Accounts&#xff09;→ 解锁设置&#xff08;输入当前用户密码…...