2024年3月

1、准备材料

正点原子stm32f407探索者开发板V2.4

STM32CubeMX软件(
Version 6.10.0

Keil µVision5 IDE(
MDK-Arm

野火DAP仿真器

2、学习目标

了解 FreeRTOS 相关知识,
并熟悉使用 STM32CubeMX 软件配置 FreeRTOS 工程的流程

3、前提知识


读者须知
:本系列教程中关于 FreeRTOS 的学习重点为应用,不会剖析源码内容,利用 STM32CubeMX 软件生成的 FreeRTOS 工程源代码中一般不会直接调用 FreeRTOS 的 API 函数,而是调用了 CMSIS-RTOS 封装好的上层API函数,但此系列教程主要介绍属于 FreeRTOS 的各类 API 函数及其应用,对于 CMSIS-RTOS 的 API 函数仅简单描述,读者需自行理解,另外对于常见硬件原理不再详细说明,感兴趣读者可阅读笔者的 “
STM32CubeMX+STM32F4系列教程


3.1、FreeRTOS简介

FreeRTOS是一个完全免费且开源的嵌入式实时操作系统(Real-Time Operating System,简称RTOS) ,其一般将任务称为线程,以下列表为FreeRTOS的一些特点,笔者认为学习FreeRTOS的应用正是掌握和理解下列各个特性

  1. 抢占式(pre-emptive)或合作式(co-operative)任务调度方式
  2. 非常灵活的优先级管理
  3. 灵活、快速且轻量化的任务通知机制(task notification)
  4. 消息队列(Queue)
  5. 二值信号量(Binary Semaphores)
  6. 计数信号量(Counting Semaphores)
  7. 互斥量(Mutex)
  8. 递归互斥量(Recursive Mutex)
  9. 软件定时器(Timers)
  10. 事件组(Events)
  11. 时间节拍钩子函数
  12. 空闲任务钩子函数
  13. 栈溢出检测
  14. 任务运行时间统计收集
  15. 完整的中断嵌套模型
  16. 用于低功耗的无节拍(Tickless)特性

RTOS主要应用于对实时性有要求的嵌入式系统,
所谓实时性就是任务完成的时间是确定的,实时性又分为软实时和硬实时

软实时指任务完成时间是确定的,但是如果任务超时了也不会对整个系统产生破坏性影响;硬实时是指任务完成时间是确定的,但是如果任务超时未完成则会对整个系统产生灾难性影响,
基于FreeRTOS开发的系统可以完成硬实时的要求

3.2、源码函数命名规律

FreeRTOS源码中函数命名规律
:FreeRTOS源码中各个函数并非随机命名,而是有规律的命名,这样方便使用者看到名字就能获得该函数更多的信息,其函数名一般由 ① 函数返回值类型简写,② 函数所在文件 和 ③ 函数作用名称这三部分组成

① 函数返回值类型简写主要有:

  1. 'u'表示'unsigned'
  2. 'c'表示'char'
  3. 's'表示'int16_t(short)'
  4. 'l'表示'int32_t(long)'
  5. 'p'表示指针类型变量
  6. 'x'表示'BaseType_t'结构体和其他非标准类型的变量名
  7. 'uc'表示'UBaseType_t'结构体
  8. 'v'表示'void'
  9. 'prv'表示私有函数无返回值

这些简写可以自由组合在一起,例如 'pc' 表示 'char *' 类型,'uc' 表示 'unsigned char' 类型

② 函数所在文件:

  1. 'CoRoutine'表示该函数定义在'coroutine.c'文件中的
  2. 'EventGroup'表示该函数定义在'event_groups.c'文件中的
  3. 'List'表示该函数定义在'list.c'文件中的
  4. 'Queue'表示该函数定义在'queue.c'文件中的
  5. 'StreamBuffer'表示该函数定义在'stream_buffer.c'文件中的
  6. 'Task'表示该函数定义在'tasks.c'文件中的
  7. 'Timer'表示该函数定义在'timers.c'文件中的
  8. 'Port'表示该函数定义在'port.c'或'heap_x.c'文件中的

举几个例子:

  1. xTaskCreate 表示函数返回值为 BaseType_t 结构体类型,函数被定义在 'tasks.c' 文件中,函数作用为“创建”
  2. vTaskSuspend 表示函数返回值为 void 类型,函数被定义在 'tasks.c' 文件中,函数作用为“挂起”
  3. prvTaskIsTaskSuspended 表示该函数为私有函数,仅能在 'tasks.c' 文件中使用,函数作用为“判断任务是否被挂起”

4、动手创建一个FreeRTOS空工程

4.1、CubeMX相关配置

4.1.1、工程基本配置

打开STM32CubeMX软件,单击ACCESS TO MCU SELECTOR选择开发板MCU(选择你使用开发板的主控MCU型号),选中MCU型号后单击页面右上角Start Project开始工程,具体如下图所示

开始工程之后在配置主页面System Core/RCC中配置HSE/LSE晶振,在System Core/SYS中配置Debug模式,
因为系统滴答定时器SysTick要被FreeRTOS所使用,所以需要配置HAL库的时基源为除系统滴答定时器SysTick外的选项,笔者这里选择了基本定时器TIM6,这两个时基源均为1ms
,具体配置如下图所示

4.1.2、时钟树配置

系统时钟使用8MHz外部高速时钟HSE,HCLK、PCLK1和PCLK2均设置为STM32F407能达到的最高时钟频率,具体如下图所示

4.1.3、外设参数配置

单击Pinout & Configuration页面左边功能分类栏目Middleware and SoftwarePacks中的FREERTOS,在模式配置栏中将其接口设置为CMSIS_V2(CMSIS_V2只是对CMSIS_V1的某些功能进行了扩展)

下方的Configuration配置页面中可以对9个选项卡关于FreeRTOS的所有参数做配置,
目前均保持默认即可
,具体配置如下图所示

以下列表为对上图所示这9个配置选项卡及其包含的参数做简单的介绍

  1. Tasks and Queues:任务和队列管理
  2. Timers and Semaphores :定时器和信号量管理
  3. Mutexes:互斥量管理
  4. Events:事件组管理
  5. FreeRTOS Heap Usage:FreeRTOS内存使用详情
  6. Config parameters:Config参数配置(对应FreeRTOSConfig.h中变量名config开始的宏定义)
  7. Include parameters:Include参数配置(对应FreeRTOSConfig.h中变量名INCLUDE开始的宏定义)
  8. Advanced settings:高级设置
  9. User Constants:用户自定义常量

对于上面9个选项卡中的前4个,CubeMX都提供了图形化配置的界面,在对应的页面中可以通过按钮非常方便的增加/删除任务、队列、定时器、信号量、互斥量和事件组等实例,而不需要用户在程序中手动编程生成实例

FreeRTOS Heap Usage 选项卡提供了一个显示当前FreeRTOS内存使用详情的页面,该页面无参数可配置,仅仅显示内存占用信息和剩余可用堆大小

Config parameters 和 Include parameters 选项卡中的参数分别对应 FreeRTOSConfig.h 文件中 config 开头和 INCLUDE 开头的宏定义,
用来设置FreeRTOS的相关参数及功能裁剪,下面是所有参数列表,读者可简单浏览,后面遇到需要修改的具体参数可以回过头来寻找,如下述列表所示

参数 函数功能
Config parameters/ MPU/FPU
ENABLE_MPU 设置是否使用MPU内存保护单元
ENABLE_FPU 设置是否使用FPU浮点数单元
Kernel settings
USE_PREEMPTION 设置任务调度方式,Enable 表示使用抢占式调度,Disable 表示使用合作式调度
CPU_CLOCK_HZ 设置MCU的HCLK始终频率,默认为系统时钟且不可修改
TICK_RATE_HZ 设置FreeRTOS滴答定时器中断频率,默认为1000Hz(1ms),设置范围为1-1000
MAX_PRIORITIES 设置最高优先级,值越大内核花销的内存空间就越多,总是建议将此常量设为能够用到的最小值,STM32CubeMX软件中默认为56且不可修改
MINIMAL_STACK_SIZE 设置空闲任务使用的堆栈大小,默认为128words
MAX_TASK_NAME_LEN 设置任务名的最大长度(包括'\0'结束符),如果创建任务时传入的任务名字符串长度超过该参数定义的长度,则任务名会被自动截断,默认为16
USE_16_BIT_TICKS 设置节拍数据类型TickType_t的具体类型,Enable 表示设置类型为 uint16_t,Disable 表示设置类型为 uint32_t
IDLE_SHOULD_YIELD 设置空闲任务是否对同优先级的任务主动让出CPU使用权
USE_MUTEXES 设置是否使用互斥量
USE_RECURSIVE_MUTEXES 设置是否使用递归互斥量
USE_COUNTING_SEMAPHORES 设置是否使用计数信号量
QUEUE_REGISTRY_SIZE 设置可注册队列和信号量的最大数量,默认为8,设置范围为0-255
USE_APPLICATION_TASK_TAG 设置是否使用应用程序的任务标签
ENABLE_BACKWARD_COMPATIBILITY 设置是否向后兼容旧版本
USE_PORT_OPTIMISED_TASK_SELECTION 设置任务调度时,选择下一个任务的方法,Disable 表示使用通用方法,不依赖硬件,此处使用 CMSIS-RTOS V2 时该参数默认为 Disable 且不可修改
USE_TICKLESS_IDLE 设置是否使用无节拍(tickless)低功耗模式
USE_TASK_NOTIFICATIONS 设置是否使用任务通知功能
RECORD_STACK_HIGH_ADDRESS 设置是否将栈的起始地址保存到每个任务的任务控制块中
Memory management settings
Memory_Allocation 设置内存分配方式,默认为 Dynamic / Static 且不可修改
TOTAL_HEAP_SIZE 设置FreeRTOS总的堆空间大小,设置范围为 512B~128KB
Memory_Management_scheme 设置内存管理方案,有 heap_x.h 共计5中可选方案,默认选择 heap_4.h
Hook function related definitions
USE_IDLE_HOOK 设置是否使用空闲任务钩子函数 vApplicationIdleHook()
USE_TICK_HOOK 设置是否使用滴答定时器钩子函数 vApplicationTickHook()
USE_MALLOC_FAILED_HOOK 设置是否使用内存分配失败钩子函数 vApplicationMallocFailedHook()
USE_DAEMON_TASK_STARTUP_HOOK 设置是否使用内存分配失败钩子函数 vApplicationMallocFailedHook()
CHECK_FOR_STACK_OVERFLOW 设置是否使用守护任务启动钩子函数 vApplicationDaemonTaskStartupHook()
Run time and task stats gathering related definitions
GENERATE_RUN_TIME_STATS 设置是否启动任务运行时间统计功能,启用后可以通过 vTaskGetRunTimeStats() API 函数读取这些信息
USE_TRACE_FACILITY 设置是否启用可视化和跟踪调试,默认为 Enabled
USE_STATS_FORMATTING_FUNCTIONS 设置是否编译 vTaskList() 和 vTaskGetRunTimeStats() API 函数,将 USE_TRACE_FACILITY 和 USE_STATS_FORMATTING_FUNCTIONS 设置为 1 将编译构建这两个函数,设置为 0 将不编译构建
Co-routine related definitions
USE_CO_ROUTINES 设置是否使用协程
MAX_CO_ROUTINE_PRIORITIES 设置协程最大优先级
Software timer definitions
USE_TIMERS 设置是否使用软件定时器
TIMER_TASK_PRIORITY 设置定时器服务任务优先级
TIMER_QUEUE_LENGTH 设置定时器指令队列长度
TIMER_TASK_STACK_DEPTH 设置定时器服务任务栈空间大小
Interrupt nesting behaviour configuration
LIBRARY_LOWEST_INTERRUPT_PRIORITY 设置最低中断优先级
LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 设置系统能管理的最高中断优先级
Added with 10.2.1 support
MESSAGE_BUFFER_LENGTH_TYPE 设置消息缓冲区长度类型
USE_POSIX_ERRNO 设置使用POSIX标准的错误编号
CMSIS-RTOS V2 flags
(仅适用于 FreeRTOS >= 10.3.1 版本)
THREAD_SUSPEND_RESUME 设置是否使用 CMSIS-RTOS2 的线程挂起 osThreadSuspend() 和恢复 osThreadResume() API 函数
USE_OS2_THREAD_ENUMERATE 设置是否使用 CMSIS-RTOS2 的返回线程枚举数量 osThreadEnumerate() API 函数
USE_OS2_EVENTFLAGS_FROM_ISR 设置是否使用来自 ISR 的 CMSIS-RTOS2 函数 osEventFlagsSet() 和 osEventFlagsClear() 操作
USE_OS2_THREAD_FLAGS 设置是否从应用程序映像中排除 CMSIS-RTOS2 线程标志 API 函数
USE_OS2_TIMER 设置是否从应用程序映像中排除 CMSIS-RTOS2 定时器 API 函数
USE_OS2_MUTEX 设置是否从应用程序映像中排除 CMSIS-RTOS2 Mutex API 函数数
Include parameters/ Include definitions
vTaskPrioritySet 设置是否包含 vTaskPrioritySet() API 函数
uxTaskPriorityGet 设置是否包含 uxTaskPriorityGet() API 函数
vTaskDelete 设置是否包含 vTaskDelete() API 函数
vTaskCleanUpResources 设置是否包含 vTaskCleanUpResources() API 函数
vTaskSuspend 设置是否包含 vTaskSuspend() API 函数
vTaskDelayUntil 设置是否包含 vTaskDelayUntil() API 函数
vTaskDelay 设置是否包含 vTaskDelay() API 函数
xTaskGetSchedulerState 设置是否包含 xTaskGetSchedulerState() API 函数
xTaskResumeFromlSR 设置是否包含 xTaskResumeFromlSR() API 函数
xQueueGetMutexHolder 设置是否包含 xQueueGetMutexHolder() API 函数,默认启用(某些工具链(例如MDK-ARM)可能需要编译cmsis_os2.c)
pcTaskGetTaskName 设置是否包含 pcTaskGetTaskName() API 函数
uxTaskGetStackHighWaterMark 设置是否包含 uxTaskGetStackHighWaterMark() API 函数
xTaskGetCurrentTaskHandle 设置是否包含 xTaskGetCurrentTaskHandle() API 函数
eTaskGetState 设置是否包含 eTaskGetState() API 函数
xEventGroupSetBitFromlSR 设置是否包含 xEventGroupSetBitFromlSR() API 函数,INCLUDE_xTimerPendFunctionCall 必须设置为 1 使 xEventGroupSetBitFromISR() 函数可用
xTimerPendFunctionCall 设置是否包含 xTimerPendFunctionCall() API 函数,configUSE_TIMERS 必须设置为 1 使 xTimerPendFunctionCall() 函数可用
xTaskAbortDelay 设置是否包含 xTaskAbortDelay() API 函数
xTaskGetHandle 设置是否包含 xTaskGetHandle() API 函数
uxTaskGetStackHighWaterMark2 设置是否包含 uxTaskGetStackHighWaterMark2() API 函数,适用于支持 FreeRTOS >= 10.2.1 的系列

Advanced settings 选项卡中只有两个参数,
USE_NEWLIB_REENTRANT
用于配置 Newlib 相关内容,一般不使用;
Use FW pack heap file
用于配置是否使用固件包提供的堆管理文件(heap_x.c),不使用的话需要由用户自己提供堆管理文件,这里一般选择使用

User Constants 选项卡可以创建一些用户需要使用的常量参数,创建的常量将以宏定义的形式被定义在main.h文件中

4.1.4、外设中断配置

当启用了FREERTOS之后,整个系统的NVIC会自动发生一些变化,
FreeRTOS使用的系统服务可挂起请求中断和系统滴答定时器中断将被强制开启,均为最低优先级且不可设置
,这两个中断对于FreeRTOS来说是相当重要的,系统滴答定时器会为FreeRTOS提供时间基准,系统服务可挂起请求中断用于任务切换等管理

另外HAL库的时基源TIM6中断也会被强制打开不可关闭,但是其中断的优先级仍可调节,
我们将TIM6中断设置为硬件最高优先级0,其他均按照默认中断优先级即可
,具体配置如下图所示

将启用FreeRTOS之后的NVIC与启用之前的NVIC对比可以发现,在配置页面多了一列“Uses FreeRTOS functions”,在增加的这一列的某个硬件中断后面勾选选项框则会改变该硬件中断的抢占优先级,读者目前仅作了解,有关中断具体管理会在后续教程中讲到

4.2、生成代码

4.2.1、配置Project Manager页面

单击进入Project Manager页面,在左边Project分栏中修改工程名称、工程目录和工具链,然后在Code Generator中勾选“Gnerate peripheral initialization as a pair of 'c/h' files per peripheral”,最后单击页面右上角GENERATE CODE生成工程,具体如下图所示

4.2.2、工程代码结构分析

打开生成的工程代码,观察其目录结构,发现在Core目录下相比较以往的工程增加了freertos.c 和 stm32f4xx_hal_timebase_tim.c 两个文件,在工程下还增加了Middlewares/FreeRTOS的源码文件,
该源码文件下所有文件均无需用户修改
,生成工程代码具体工程目录结构如下图所示

为了减少在使用不同的第三方RTOS嵌入式操作系统(eg:FreeRTOS、UCOS等)对用户应用代码带来的差异,ARM公司为RTOS内核制定了一套通用的接口协议CMSIS-RTOS(cmsis_osx.c),在该文件中规定了RTOS中使用的某些功能函数的统一名称及参数等等

在应用上,用户只需要调用CMSS-RTOS规定的API函数来对任务进行操作
,而CMSS-RTOS规定的API函数会使用不同第三方RTOS嵌入式操作系统的接口函数对CMSS-CORE(HAL库等函数)操作,最终控制底层MCU,其中CMSS、RTOS和MCU等的关系图如下图所示
(注释1)

接下来我们来看看空的FreeRTOS工程初始化流程
,打开main.c文件,在主函数中分别执行了以下几个函数,下面简单介绍下这些空工程中就使用到的函数,读者了解即可

最后调用osKernelStart()函数时会先创建一个空闲任务,然后启动FreeRTOS调度器,将STM32内核控制权交给FreeRTOS调度器,
FreeRTOS调度器启动之后必须要至少有一个任务在不断运行
,之后调度器就会按照一定的任务优先级顺序执行用户定义的各种任务,
每个任务都应该是一个死循环,所以程序不会运行到osKernelStart()函数之后的任何部分

读者在使用FreeRTOS时,如果需要自己手动创建某些任务、信号量、互斥量等可以直接在freertos.c文件中实现即可,也可以在CubeMX图形化配置界面中创建(推荐后者)

MX_FREERTOS_Init()函数中,ST公司也使用者贴心的提供了各种不同功能的沙箱代码段,当在CubeMX图形化配置界面中创建了对应的实例,该沙箱代码段中就会出现对应实现的程序,如下图所示

4.3、烧录验证

使用STM32CubeMX生成工程代码后,不做任何修改,直接单击KEIL软件的编译按钮应该可以顺利通过,通过编译信息可知出现0错误和0警告,如下图所示

单击魔术手,在debug选项卡中选择使用DAP下载器(该下载器无需下载驱动),单击后方的设置可以在其中看到识别到的下载器,具体如下图所示

将接入电源的开发板通过DAP下载器与PC连接,单击KEIL软件的LOAD按钮将程序烧录进入开发板MCU中,等待下方进度条走完,可以发现 Flash Load finished ,由于是空工程因此无任何现象发生,具体如下图所示

5、FreeRTOS API 与
CMSIS-RTOS API
对照表

FreeRTOS API 函数功能 CMSIS-RTOS API
About Task
xTaskCreate() 动态分配内存创建任务 osThreadNew()
xTaskCreateStatic() 静态分配内存创建任务 osThreadNew()
vTaskSuspend() 挂起某个任务 osThreadSuspend()
vTaskResume() 将某个任务从挂起状态恢复 osThreadResume()
vTaskPrioritySet() 设置任务优先级 osThreadSetPriority()
uxTaskPriorityGet() 获取任务优先级 osThreadGetPriority()
vTaskDelay() 延时函数 osDelay()
vTaskDelayUntil() 延时函数,用于实现一个任务固定执行周期 osDelayUntil()
xTaskGetTickCount() 获取滴答信号当前计数值 osKernelGetTickCount()
xTaskAbortDelay() 终止任务延时,退出阻塞状态 ---
vTaskDelete() 任务删除函数 osThreadTerminate()/osThreadExit()
vTaskStartScheduler() 启动调度器 osKernelStart()
vTaskSuspendAll() 挂起调度器 osKernelLock()
xTaskResumeAll() 恢复调度器 osKernelUnlock()
taskYIELD() 让位于另一项同等优先级的任务 osThreadYield()
About MessageQueue
xQueueCreate() 动态分配内存创建队列 osMessageQueueNew()
xQueueCreateStatic() 静态分配内存创建队列 osMessageQueueNew()
xQueueSend() 向队列后方发送数据 osMessageQueuePut()
xQueueSendToBack() 向队列后方发送数据 osMessageQueuePut()
xQueueSendToFront() 向队列前方发送数据 osMessageQueuePut()
xQueueOverwrite() 向长度为1的队发送数据 osMessageQueueGet()
xQueueReceive() 从队列头部接收数据单元,接收的数据同时会从队列中删除 osMessageQueueGet()
xQueuePeek() 从队列头部接收数据单元,不从队列中删除接收的单元 osMessageQueueGet()
uxQueueMessagesWaiting() 查询队列有效数据单元个数 osMessageQueueGetCount()
vQueueDelete() 删除队列 osMessageQueueDelete()
xQueueReset() 将队列重置为其原始空状态 osMessageQueueReset()
About Semaphores
About Mutexs
About EventGroup
About Task_Notifications
About Stream_Buffer
About Timer
About LowPower

6、注释详解

注释1
:图片来源
Getting Started with STM32 - Introduction to FreeRTOS

注释2
:图片来源
CMSIS-Core Device Templates

参考资料

STM32Cube高效开发教程(高级篇)

1)
Next Terminal
介绍

官网:
https://next-terminal.typesafe.cn/
GitHub:
https://github.com/dushixiang/next-terminal

想必经常玩服务器的都了解过
堡垒机
,类似于跳板机,但与跳板机的侧重点不同。堡垒机的主要功能是控制和监控对内部网络的远程访问。它提供严格的访问控制、会话审计和监控等功能。而跳板机更侧重于提供一个安全的中转平台,用于跳转到内部网络的其他服务器或设备。

今天我给大家分享一款好用安全的开源交互审计系统 -
Next Terminal
Next-terminal
号称下一代堡垒机,它支持多协议如 RDP、SSH、VNC 等,并可以对正在进行的会话实时监控,还可以进行事后审计,包括查看日志和查看命令视频记录。
image.png

2)
Next Terminal
特性

  1. 免费开源:
    Next Terminal 在 GitHub 上已收获 4000+ Star。

  2. 多协议支持:
    您可以在一套系统中访问 RDP、SSH、VNC、TELNET 等协议资产,无需插件,一个浏览器即可。

  3. 实时监控:
    您可以随时查看到目前正在活跃的会话,并进行监控和阻断。针对字符协议,您甚至可以限制禁止某些命令的执行和记录。

  4. 事后审计:
    Next Terminal 观察并记录所有环境中的每个在线资源、连接、交互会话和其他安全事件。这些事件被记录在结构化的审计日志中,便于查看正在发生的事情和责任人。

官网在线体验地址:
https://next.typesafe.cn/
账号:test
密码:test
image.png

3)检查 Docker 环境

安装 Docker 软件

# 高版本 Docker 安装
curl -fsSL https://get.docker.com/ | sh
# 关闭防火墙
systemctl disable --now firewalld
setenforce 0
# 启用 Docker
systemctl enable --now docker

检查 Docker 服务

systemctl status docker

image.png

开启 IPv4 forwarding

echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
systemctl restart network
sysctl net.ipv4.ip_forward

4)安装 Docker-Compose

下载 Docker-Compose 软件包

curl -L https://github.com/docker/compose/releases/download/v2.2.2/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose

Docker-Compose 增加执行权限

chmod +x /usr/local/bin/docker-compose

检查 Docker-Compose 版本

docker-compose -v

5)下载
Next Terminal
镜像

docker pull mysql:8.0
docker pull dushixiang/guacd:latest
docker pull dushixiang/next-terminal:latest

6)使用 Docker-Cli 部署

# 创建数据存储目录
mkdir -p /docker/next-terminal/data
# 1) 搭建 Guacd - 开源的远程桌面网关
docker run -d \
--name guacd \
-v /docker/next-terminal/data:/usr/local/next-terminal/data \
--restart always \
dushixiang/guacd:latest
 
# 2) 搭建 next-terminal
docker run -d \
--name next-terminal \
-e DB=sqlite \
-e GUACD_HOSTNAME=guacd \
-e GUACD_PORT=4822 \
-p 8088:8088 \
-v /etc/localtime:/etc/localtime \
-v /docker/next-terminal/data:/usr/local/next-terminal/data \
--link guacd \
--restart always \
dushixiang/next-terminal:latest

7)使用 Docker-compose 部署

创建数据目录

mkdir -p /docker/next-terminal/data

授权数据目录

chmod -R 777 /docker/next-terminal/

编辑 docker-compose.yaml 文件

[root@blog next-terminal] cd /docker/next-terminal
[root@blog next-terminal] vim docker-compose.yaml
version: '3.3'
services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: next-terminal
      MYSQL_USER: next-terminal
      MYSQL_PASSWORD: next-terminal
      MYSQL_ROOT_PASSWORD: next-terminal
    volumes:
      - ./data/mysql:/var/lib/mysql
    restart:
          always
  guacd:
    image: registry.cn-beijing.aliyuncs.com/dushixiang/guacd:latest
    volumes:
      - ./data:/usr/local/next-terminal/data
    restart:
          always
  next-terminal:
    image: registry.cn-beijing.aliyuncs.com/dushixiang/next-terminal:latest
    environment:
      DB: mysql
      MYSQL_HOSTNAME: 192.168.80.8	# 你的 IP 地址
      MYSQL_PORT: 3306
      MYSQL_USERNAME: next-terminal
      MYSQL_PASSWORD: next-terminal
      MYSQL_DATABASE: next-terminal
      GUACD_HOSTNAME: guacd
      GUACD_PORT: 4822
    ports:
      - "8088:8088"
    volumes:
      - /etc/localtime:/etc/localtime
      - ./data:/usr/local/next-terminal/data
    depends_on:
      - mysql
    restart:
      always

运行
Next Terminal
容器

# 基于 docker-compose.yaml 启动并运行服务
docker compose -f docker-compose.yaml up -d

验证
Next Terminal
容器状态

docker ps -a

8)访问
Next Terminal

基于浏览器访问 Next Terminal,打开方式依然为
本地IP
:
端口号
( 此处端口为 8088 )

首次打开需要输入账号密码,
Next Terminal
的初始账号和密码分别为:admin/admin。
image.png
image.png

9)
Next Terminal
基本操作

9.1)修改密码

在左侧菜单栏找到
个人中心
修改密码

image.png

9.2)新建资产

可以在
资产管理
新建资产,即新建一台管理终端。
我这里选择新建一台本地的 Linux 虚拟机。
image.png

新建后如果想进行编辑也还可以进行编辑,信息填写正确后可以
点击接入
,进会进入新的页面。在这里可以通过 SSH 操作刚刚填写的服务器,界面看起来还挺不错的。

image.png
image.png
image.png

9.3)会话审计

可以查看
在线会话
,并可以进行监控和断开的操作。

可以监控用户正在对服务器执行的命令,实时同步的。
image.png

断开会话后,也可以在历史会话中查看,
并可以进行回放操作。

123.gif

9.4)日志审计

image.png

9.5)系统设置

image.png

如果有小伙伴正好在寻找一个功能丰富的交互式审计系统,
Next Terminal
将是一个不错的选择。

关于项目的更多细节,可以自行到项目地址进行查看

10)最后

如果你喜欢这篇文章,请记得点赞,收藏,并关注
【开源极客行】
,我将持续分享更多实用的自搭建应用指南。一起,让我们掌握自己的数据,创建自己的数字世界!
如果你在搭建过程中遇到任何问题,或者有任何建议,也欢迎在下方留言,一起探讨和学习。

在Chain of Thought出来后,出现过许多的优化方案例如Tree of thought, Graph of Thought, Algorithm of Thought等等,不过这些优化的出发点都更加"Machine Like",而非"Human Like", 哈哈不是说机器化不好,仅仅是对AGI的一些个人偏好而已。

所以如果我们从人类思考的角度出发,能否把当前模型的思考方式和人类的思考方式进行关联呢?
我先问了下PPLX-70B人类思维有哪些分类(这个问题RAG真的不如模型压缩后回答的效果)

我们再把之前已经聊过的一些引导模型推理思考的prompt模板,以及工具调用的一些prompt方案和上面的人类思维逻辑进行下不完全的类比:

Prompt策略 类比人类思维
Chain of Thought 逻辑思维中的演绎推理
Few Shot Prompt 类比思维
SELF-REFINE,Relfection 自省思维
ReAct,SelfAsk 后续性思维(线性思维?)
情感思维 哈哈夸夸模型会更好

和上面的人类思维模式相比,似乎还少了抽象思维和发散思维,这一章我们就聊聊这两种思考方式如何通过prompt来引导。

抽象思维: Step Back Prompt

  • Take a Step Back: Evoking Reasoning via Abstraction in Large Language Models
  • LARGE LANGUAGE MODELS CAN LEARN RULES

DeepMind提出的新Prompt方式,在思维链之前加了一步抽象(后退一步思考)。通过把原始问题抽象成更高层次的概括性、概念性问题,降低原始问题中的细节对推理的影响,如下

在RAG场景里,这类prompt策略很适合解决用户query过于细节,导致召回效果不佳的问题。用户在大模型场景的提问往往比在搜索框中的提问更加具体,包含更多条件和细节,举个例子query = “哪些国家在经济陷入低谷时,因为通货膨胀严重,而不得不加息“,如果直接对以上query进行改写,Decompose拆分,会发现都无法召回有效的内容,因为问题本身粒度已经太细了,这时不需要进一步拆分,相反需要
更高层次的总结和抽象
。我们只需要把问题修改成“哪些国家发生过被动加息”,这类更高层次的抽象概念,召回效果立刻起飞。

StepBack推理和COT推理相同,都是通过few-shot prompt来引导模型进行后退思考。
论文对三个不同领域的任务进行了评估,分别是STEM领域的推理任务,知识密集的QA任务,和multi-hop推理任务。其中前者和后两者的prompt模板存在差异,前者是让模型先抽象理论概念,后两者是更通用的提出stepback问题,
个人感觉是归纳推理从特殊到一般的推理抽象过程在不同领域的差异性

STEM推理的few-shot prompt模板如下

知识密集的QA任务和multi-hop推理任务都是使用的以下few-shot prompt模板

测试下了step back prompt中few-shot才是核心,因为在不同领域中归纳推理的思维是不同的,有需要对概念进行抽象,有需要对不同时间,实体进行抽象,有需要对条件进行放宽,以下是论文中在QA场景使用的一些few-shot案例

效果上在PaML-2的模型上进行试验,各个任务上step-back都能相比COT有进一步的显著提升,在多数任务上stepback + RAG都能超越GPT-4的效果。并且prompt效果和few-shot的个数无关,1-shot的效果就很好。

不过在我们的场景中测试,论文中提到的几个stepback的问题其实一定程度上被放大了,导致当前看效果比较一般,主要有以下几个问题

  1. Context Loss:在抽象问题的过程中,模型丢掉了核心的条件,导致问题丢失了核心信息,后面的RAG也随之错误
  2. Abstraction Error: 在垂直领域,模型的归纳推理效果有限,往往在第一步定位principle、concept就存在错误
  3. Timing:Step Back Prompt和Decompose其实是相对对立的关系,各自适合解决一部分问题,在RAG场景中Decompose更适合粗粒度Query,Step Back适合细粒度Query,但想让模型自己决策不同的思考方式难度有点高哦

另一篇论文Large Language Models can Learn Rule思路也有些类似,也是先归纳推理再演绎推理,通过把抽象出的通用知识注入prompt,用来帮助下游推理。这里就不细说了~

发散思维:Diversity of Thought

  • Diversity of Thought Improves Reasoning Abilities of Large Language Models

发散思维简单说就是“一题多解“,“一物多用”, 其实在Self-Consistency这类Ensemble方案中就出现过。Self-Consistency通过让模型随机生成多个推理,从中Major Vote出概率最高的答案,更像是发散思维的对立收敛思维,重心放在从四面八方的各种尝试中抽象问题的核心。

Self-Consistency的重心放在收敛,而Diversity of Thought的重心放在发散。这里论文提出了两个可以发散模型思维的prompt方式

  • Approaches:以XX方式思考,例如数学问题可以让模型直接计算,化简计算,可视化,逆推等等
  • Persona:像谁谁一样思考,例如金融问题可以像Buffett一样思考,数学问题像Turing一样思考,其实每个名人背后也是相关思维思维方式的一种抽象,例如沃伦巴菲特代表是价值投资。莫名出现了拘灵遣将的即视感......

基于以上的多个发散维度,论文给出了两种prompt构建的方案

  • 多个approach拼成一个one-shot让模型推理一次给出多个不同的结果
  • 1个approach作为one-shot让模型推理多次

分别对应以下两种prompt

那如何得到上面的这些approach呢?这里论文也采用了大模型自动构建的方案,在某一类问题中随机采样query,使用以下prompt让模型生成回答该问题可以使用的方案,最后每个领域选择出现频率最大的TopN个Approach用来构建prompt。挖掘approach的prompt如下

效果上,使用发散思维和COT进行配合,在GSM8K,AQUA等推理任务,CommenseQA等常识任务,和BlocksWorld等规划任务上均有显著提升。并且和Self-Consistency的结论相似,以上发散思维的Ensemble数量更多,效果越好。

整体上以上的两种思维逻辑都还相对初步,对比已经比较成熟的演绎推理的COT还有再进一步探索的空间,以及如何在不同场景下让模型选择不同的思维模式,进行思考,并最终收敛到正确的结果也值得再进行尝试。

想看更全的大模型相关论文梳理·微调及预训练数据和框架·AIGC应用,移步Github >>
DecryPrompt

KingbaseES人大金仓数据库介绍:

KingbaseES是一种关系型数据库管理系统,也被称为人大金仓数据库。KingbaseES 是北京人大金仓信息技术股份有限公司研发的,具有自主知识产权的通用数据库产品。

该产品面向事务处理类应用,兼顾各类数据分析类应用,可用做管理信息系统、业务及生产系统、决策支持系统、多维数据分析、全文检索、地理信息系统、图片搜索等的承载数据库。

作为KingbaseES产品系列最新一代版本,KingbaseESV8在系统的可靠性、可用性、性能和兼容性等方面进行了重大改进。

以下是一些关于KingbaseES数据库的特点和功能:

  1. 数据库引擎:KingbaseES是基于开源数据库PostgreSQL开发的,具备PostgreSQL的核心功能。它继承了PostgreSQL的可靠性、稳定性和高度兼容性,同时加入了自主创新的技术和功能。

  2. 兼容性:KingbaseES与其他主流数据库(如Oracle、SQL Server等)具有较高的兼容性,可以迁移和运行现有的应用程序和数据库对象。这使得企业能够更容易地将现有的应用程序和数据迁移到KingbaseES上。

  3. 分布式存储和高可用性:KingbaseES支持分布式数据库和高可用架构,可以实现数据的分布式存储和在多个节点之间的数据复制和同步,提供高可用性和容错性。

  4. 安全性:KingbaseES提供了强大的安全性功能,包括角色和用户管理、访问控制、数据加密等,以确保数据库的安全性和数据的保密性。

  5. 多语言支持:KingbaseES支持多种编程语言和开发工具,包括Java、C#、Python等,使得开发人员能够使用自己熟悉的语言来开发和管理数据库应用程序。

  6. 大数据处理:KingbaseES具备处理大规模数据和高并发访问的能力,支持大数据处理和分析,适用于需要处理海量数据和高性能计算的场景。

总体而言,KingbaseES是一个功能强大、稳定可靠、兼容性好的关系型数据库管理系统。

它在中国企业和政府机构中得到广泛应用,特别是那些希望建立在成熟开源技术基础上,同时具备商业级支持和增强功能需求的组织。

CYQ.Data 框架简介:

CYQ.Data 是一个高性能且功能强大的ORM(对象关系映射)框架,支持包括.NET Core在内的各种.NET版本。

它设计用于与多种数据库如MSSQL、MySQL、Oracle、Sybase、PostgreSQL、DB2、FireBird、SQLite、DaMeng、KingBaseES等,以及格式如Txt、Xml、Access、Excel和FoxPro等工作。

该框架旨在通过提供写日志、操作JSON和分布式缓存等功能,简化数据层操作,无需额外的库如Log4net.dll、newtonjson.dll或Memcached.ClientLibrary.dll。

该框架自豪于其低调但持续15年的更新,强调其长期可靠性和对开发人员寻找稳定且多功能ORM解决方案的支持。

前言:

去年有群友问我,CYQ.Data 支不支国产数据库,那时候,没支持,只是给了些提示,让其自行通过开源代码支持了。

后来问的人多了,就开始支持了。

CYQ.Data 从去年年底开始支持国产数据库,下面就开始介绍相关内容。

1、开源地址:

https://github.com/cyq1162/cyqdata

2、 Nuget 引用

可以通过 nuget 管理器,直接搜 cyqdata,找到对应的 KingBaseES 版本,引入即可。

cyq.data 原始版本和 cyq.data.kingbasees 版本的区别:

cyq.data 原始版本:

  不包含其它数据库驱动,需要自行引用对应数据的驱动:比如使用mysql数据库时,需要再引用 mysql.data.dll 驱动。如果引用 cyq.data.mysql,则里面包含了 mysql.data.dll 驱动。

cyq.data.kingbasees 版本:

  同样,里面集成了对应的数据库驱动,不用再单独引用。

3、支持的版本:

从下图可以看如,由于官方驱动未支持.net 2.0的原因,因此从.net 4.0 一路支持到 .net 8 及以上。

4、数据库链接语句:

随便 CYQ.Data 支持的数据库越多,有些数据库的语句都一样,无法再根据关键字信息来识别,于是在语句上支持了provider:

链接语句示例:

provider=kingbasees;server=127.0.0.1;User Id=system;Password=123456;Database=test;Port=54321;schema=public;

链接语句配置:

{
  "ConnectionStrings": {
    "Conn": "provider=provider=kingbasees;server=127.0.0.1;User Id=system;Password=123456;Database=test;Port=54321;schema=public;"

  }
}

5、使用示例:

框架默认会引用配置中 Conn 的链接,所以在代码中无需指定。

1、无实体:

使用 MAction 操作表、视图,带分页:

using (MAction action = new MAction("表名、视图名、sql查询语句"))
{
    MDataTable dt = action.Select(1, 10, "id>10");
}

使用 MProc 操作原始sql语句或存储过程:

using (MProc proc=new MProc("sql语句、存储过程名"))
{
    MDataTable dt = proc.ExeMDataTable();
}

2、有实体:

A、纯实体

public class Users
{
    public int ID { get; set; }
    public string Name { get; set; }
}

使用:

List<Users> users=DBFast.Select<Users>(1,10,"id>10");

B、实体继承自 CYQ.Data.Orm

public class Users:CYQ.Data.Orm.SimpleOrmBase<Users>
{
    public Users()
    {
        base.SetInit(this, "表名");
    }
    public int ID { get; set; }
    public string Name { get; set; }
}

使用:

using(Users user = new Users())
{
    List<Users> users = user.Select(1, 10, "id>10");
}

以上仅展示查询功能,CYQ.Data 的操作,对十多种数据库,操作都是一致的。

如果还没有学过,可以看 CYQ.Data 相关教程。

1、V4 系列:
https://www.cnblogs.com/cyq1162/category/216965.html

2、V5 系列:
https://www.cnblogs.com/cyq1162/category/852300.html

6、KingBaseES 人大金仓数据库的基础说明:

在安装或使用 KingBaseES 人大金仓数据库时,可以通过安装后的数据库开发工具KStudio管理工具,来管理数据库

启动,输入账号进入界面后:

可以看到,默认都是小写标识,语句的关键字,是通过双引号包括起来。

而且和上篇文章介绍的:
CYQ.Data 支持 DaMeng 达梦数据库
明显不同。

关键信息:

1、KingBaseES 人大金仓数据库 是多数据库、多模式。
2、public
为默认模式,因此,默认情况下,我们可以在 public 模式下新建表即可。

一开始没发现 public 是默认模式,都是新建模式在测试,这让我在一开始的时候,有点绕。

重新看一下这个示例的数据库链接语句:

provider=kingbasees;server=127.0.0.1;User Id=system;Password=123456;Database=test;Port=54321;schema=public;

从这个示例链接中,我们不仅要指定Database,同时也需要指定schema。

做为一篇介绍框架支持数据库的文章,就不过多的介绍介绍数据库本身了,有用到的,可以上官方了解更多。

总结:

CYQ.Data 是一个用于操作数据库的框架,可以方便地连接和管理各种类型的数据库。在操作 KingBaseES 人大金仓数据库时,使用 CYQ.Data 框架可以提供以下功能和优势:

  1. 连接数据库:CYQ.Data 可以轻松地建立与 KingBaseE 数据库的连接,通过简单的配置即可实现连接功能,节省了开发人员的时间和精力。

  2. 执行 SQL 查询:使用 CYQ.Data 可以方便地执行各种 SQL 查询操作,包括查询数据、更新数据、插入数据等,同时还支持事务处理,确保数据操作的准确性和完整性。

  3. 参数化查询:CYQ.Data 支持参数化查询,可以有效防止 SQL 注入攻击,提高数据库操作的安全性。

  4. 数据库事务:通过 CYQ.Data 框架可以轻松管理数据库事务,确保多个操作的原子性,避免数据不一致的情况发生。

总的来说,使用 CYQ.Data 框架操作 KingBaseES 数据库可以简化开发流程,提高开发效率,同时也增强了系统的稳定性和安全性。

pathlib
模块是在
Python3.4
版本中首次被引入到标准库中的,作为一个可选模块。

Python3.6
开始,内置的
open
函数以及
os

shutil

os.path
模块中的各种函数都可以正确地使用
pathlib.Path
对象了。

最初,
pathlib
给人的感觉只是
os.path
的一个不必要的面向对象版本,
不过,当你实际去了解
pathlib
之后,会发现
pathlib
实际上绝不是一个简单的面向对象版本,
而是实实在在的解决了
os.path
存在的一些问题。

1. os.path VS pathlib

1.1. 路径规范化

对于
os.path
来说,路径的分隔用
正斜杆

\
)还是
反斜杠

/
)需要自己根据操作系统来确定。
或者,每一个路径拼接的地方,都用
os.path.join
来连接。
而使用
pathlib
的话,直接用
反斜杠

/
)即可,不用担心操作系统的不同。

比如:

import os

# windows系统中测试

os.path.join("a/b", "c.txt")
# 运行结果 错误
# 'a/b\\c.txt'

os.path.join("a", "b", "c.txt")
# 运行结果 正确
# 'a\\b\\c.txt'

从代码可以看出,每一层文件夹都必须用
join
连接才能正确适应不同系统。
而在
pathlib
中,则不需要考虑这么多。

from pathlib import Path

Path("a/b").joinpath("c.txt")
# WindowsPath('a/b/c.txt')

Path("a").joinpath("b").joinpath("c.txt")
# WindowsPath('a/b/c.txt')

使用
pathlib
,在
windows
或者
linux
中,统一使用
反斜杠

/
)来分隔文件夹。

路径规范化之后的好处就是代码更加简洁。
比如:下面这个重命名文件的例子(
a/b/c/d.csv => a/b/c.csv

# os.path 方式
os.rename(os.path.join("a", "b", "c", "d.csv"), os.path.join("a", "b", "c.csv"))

# pathlib 方式
Path("a/b/c/d.csv").reanme("a/b/c.csv")

哪种方式更清晰简洁不言而喻。

1.2. 字符串和对象

为什么要用对象来表示路径?

先看下面3个字符串变量:

student = '{"name": "databook", "score": "90"}'
graduate_date = "2023-07-01"
home_directory = '/home/databook'

这3个字符串其实代表不同的事物:一种是 JSON blob,一种是日期,一种是文件路径。

再看下面3个用对象表示的变量:

from datetime import date
from pathlib import Path

student = {"name": "databook", "score": "90"}
graduate_date = date(2023, 7, 1)
home_directory = Path('/home/databook')

用字符串来表示变量确实简洁,但也导致每个变量失去了其本身的意义,
程序无法区分这个变量代表的是JSON,还是日期,还是一个路径,从而增加了程序的不确定性。
程序规模大了,或者复杂性提高了之后,存在很大的隐患。

os.path

pathlib
就是这样的关系,
os.path
使用字符串表示路径,
pathlib
使用
Path
对象表示路径。

1.3. 读写文件

pathlib
的路径对象(
Path
)可以直接读写文件,因此也能大大简化读写文件的代码。

不用
pathlib
的读写文件方式:

import os

# 读取文件
fp = os.path.join("a", "b.txt")
with open(fp, "r") as f:
    f.read()

# 写入文件
with open(fp, "w") as f:
    f.write("hello")

使用
pathlib
的话:

from pathlib import Path

# 读取文件
Path("a/b.txt").read_text()

# 写入文件
Path("a/b.txt").write_text("hello)

2. pathlib的性能

pathlib
用面向对象的方式处理路径,难免让人觉得会比传统的方式慢很多,也就是存在性能问题。
那么,
pathlib
到底会比传统方式慢多少?通过下面的简单示例来看看。

传统方式:

def a(d="D:/miniconda3/Lib/site-packages"):
    from os import getcwd, walk

    extension = ".py"
    count = 0
    for root, directories, filenames in walk(d):
        for filename in filenames:
            if filename.endswith(extension):
                count += 1
                
    print(f"{count} Python files found")

if __name__ == "__main__":
    import time

    t0 = time.time()
    a()
    t1 = time.time()
    print(t1 - t0)

# 运行结果:
7875 Python files found
0.31201744079589844

pathlib
方式:

def b(d="D:/miniconda3/Lib/site-packages"):
    from pathlib import Path

    extension = ".py"
    count = 0
    for filename in Path(d).rglob(f"*{extension}"):
        count += 1

    print(f"{count} Python files found")

if __name__ == "__main__":
    import time

    t0 = time.time()
    b()
    t1 = time.time()
    print(t1 - t0)

# 运行结果:
7875 Python files found
0.44898128509521484

读取的标准库中的文件,总共将近
8000
个文件,运行多次后,时间大概相差
0.1秒
左右。
pathlib
的性能确实略逊于传统方式,但是将近
8000
个文件,也只慢了
0.1秒

如果不是大规模处理文件的话,还是用
pathlib
更好。

3. 总结

总的来说,与传统的
os.path
模块相比,
pathlib
提供了一种更现代和面向对象的方式来处理文件路径。
它支持跨平台的文件路径操作,使得开发者可以更容易地编写可移植的代码。
此外,
pathlib
还提供了链式调用的能力,使得代码更加简洁和易读。

因此,为了代码更加简洁、易读和可维护,推荐使用
pathlib
来替代传统的
os.path