9.任务调度

news2024/9/23 1:34:46

一、开启任务调度器

在这里插入图片描述

1.函数 vTaskStartScheduler()

在这里插入图片描述
函数 vTaskStartScheduler()用于启动任务调度器,任务调度器启动后,FreeRTOS 便会开始
进行任务调度,除非调用函数 xTaskEndScheduler()停止任务调度器,否则不会再返回。函数
vTaskStartScheduler()的代码如下所示:

void vTaskStartScheduler( void )
{
 BaseType_t xReturn;
 
 /* 如果启用静态内存管理,则优先使用静态方式创建空闲任务 */
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
{
 StaticTask_t * pxIdleTaskTCBBuffer = NULL;
 StackType_t * pxIdleTaskStackBuffer = NULL;
 uint32_t ulIdleTaskStackSize;
 
 vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer,
 &pxIdleTaskStackBuffer,
 &ulIdleTaskStackSize);
 xIdleTaskHandle = xTaskCreateStatic( prvIdleTask,
 configIDLE_TASK_NAME,
 ulIdleTaskStackSize,
 ( void * ) NULL,
 portPRIVILEGE_BIT,
 pxIdleTaskStackBuffer,
 pxIdleTaskTCBBuffer);
 
 if( xIdleTaskHandle != NULL )
 {
 xReturn = pdPASS;
 }
 else
 {
 xReturn = pdFAIL;
 }
}
#else
 /* 未启用静态内存管理,则使用动态方式创建空闲任务 */
{
 xReturn = xTaskCreate( prvIdleTask,
 configIDLE_TASK_NAME,
 configMINIMAL_STACK_SIZE,
 ( void * ) NULL,
 portPRIVILEGE_BIT,
 &xIdleTaskHandle);
 }
#endif
 
 /* 如果启用软件定时器,则需要创建定时器服务任务 */
#if ( configUSE_TIMERS == 1 )
{
 if( xReturn == pdPASS )
 {
 xReturn = xTimerCreateTimerTask();
 }
 else
 {
 mtCOVERAGE_TEST_MARKER();
 }
}
#endif
 
 if( xReturn == pdPASS )
 {
#ifdef FREERTOS_TASKS_C_ADDITIONS_INIT
{
 /* 此函数用于添加一些附加初始化,不用理会 */
 freertos_tasks_c_additions_init();
}
#endif
 
 /* FreeRTOS 关闭中断,
 * 以保证在开启任务任务调度器之前或过程中,SysTick 不会产生中断,
 * 在第一个任务开始运行时,会重新打开中断。
 */
 portDISABLE_INTERRUPTS();
 
#if ( configUSE_NEWLIB_REENTRANT == 1 )
{
 /* Newlib 相关 */
 _impure_ptr = &( pxCurrentTCB->xNewLib_reent );
}
#endif
 /* 初始化一些全局变量
 * xNextTaskUnblockTime: 下一个距离取消任务阻塞的时间,初始化为最大值
 * xSchedulerRunning: 任务调度器运行标志,设为已运行
 * xTickCount: 系统使用节拍计数器,宏 configINITIAL_TICK_COUNT 默认为 0
 * */
 xNextTaskUnblockTime = portMAX_DELAY;
 xSchedulerRunning = pdTRUE;
 xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;
 
 /* 为任务运行时间统计功能初始化功能时基定时器
 * 是否启用该功能,可在 FreeRTOSConfig.h 文件中进行配置
 */
 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
 /* 调试使用,不用理会 */
 traceTASK_SWITCHED_IN();
 
 /* 设置用于系统时钟节拍的硬件定时器(SysTick)
 * 会在这个函数中进入第一个任务,并开始任务调度
 * 任务调度开启后,便不会再返回
 */
 if( xPortStartScheduler() != pdFALSE )
 {
 }
 else
 {
 
 }
 }
 else
 {
 /* 动态方式创建空闲任务和定时器服务任务(如果有)时,因分配给 FreeRTOS 的堆空间
 * 不足,导致任务无法成功创建 */
 configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
 }
 
 /* 防止编译器警告,不用理会 */
 ( void ) xIdleTaskHandle;
 
 /* 调试使用,不用理会 */
 ( void ) uxTopUsedPriority;
}

从上面的代码可以看出,函数 vTaskStartScheduler()主要做了六件事情。

  1. 创建空闲任务,根据是否支持静态内存管理,使用静态方式或动态方式创建空闲任务。
  2. 创建定时器服务任务,创建定时器服务任务需要配置启用软件定时器,创建定时器服务
    任务,同样是根据是否配置支持静态内存管理,使用静态或动态方式创建定时器服务任务。
  3. 关闭中断,使用 portDISABLE_INTERRUPT()关闭中断,这种方式只关闭受 FreeRTOS 管
    理的中断。关闭中断主要是为了防止 SysTick 中断在任务调度器开启之前或过程中,产生中断。
    FreeRTOS 会在开始运行第一个任务时,重新打开中断。
  4. 初始化一些全局变量,并将任务调度器的运行标志设置为已运行。
  5. 初始化任务运行时间统计功能的时基定时器,任务运行时间统计功能需要一个硬件定时
    器提供高精度的计数,这个硬件定时器就在这里进行配置,如果配置不启用任务运行时间统计
    功能的,就无需进行这项硬件定时器的配置。
  6. 最后就是调用函数 xPortStartScheduler()。

2.函数xPortStartScheduler()

在这里插入图片描述
函数 xPortStartScheduler()完成启动任务调度器中与硬件架构相关的配置部分,以及启动第
一个任务,具体的代码如下所示:

BaseType_t xPortStartScheduler( void )
{
#if ( configASSERT_DEFINED == 1 )
{
 /* 检测用户在 FreeRTOSConfig.h 文件中对中断相关部分的配置是否有误,代码省略 */
}
#endif
 
 /* 设置 PendSV 和 SysTick 的中断优先级为最低优先级 */
 portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI;
 portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI;
 
 /* 配置 SysTick
 * 清空 SysTick 的计数值
 * 根据 configTICK_RATE_HZ 配置 SysTick 的重装载值
 * 开启 SysTick 计数和中断
 */
 vPortSetupTimerInterrupt();
 
 /* 初始化临界区嵌套次数计数器为 0 */
 uxCriticalNesting = 0;
 
 /* 使能 FPU
 * 仅 ARM Cortex-M4/M7 内核 MCU 才有此行代码
 * ARM Cortex-M3 内核 MCU 无 FPU
 */
 prvEnableVFP();
 
 /* 在进出异常时,自动保存和恢复 FPU 相关寄存器
 * 仅 ARM Cortex-M4/M7 内核 MCU 才有此行代码
 * ARM Cortex-M3 内核 MCU 无 FPU
 */
 *( portFPCCR ) |= portASPEN_AND_LSPEN_BITS;
 
 /* 启动第一个任务 */
 prvStartFirstTask();
 
 /* 不会返回这里 */
 return 0;
}

函数 xPortStartScheduler()的解析如下所示:

  1. 在启用断言的情况下,函数 xPortStartScheduler()会检测用户在 FreeRTOSConfig.h 文件
    中对中断的相关配置是否有误,感兴趣的读者请自行查看这部分的相关代码。
  2. 配置 PendSV 和 SysTick 的中断优先级为最低优先级,请参考 4.3.1 小节。
  3. 调用函数 vPortSetupTimerInterrupt()配置 SysTick,函数 vPortSetupTimerInterrupt()首先会
    将 SysTick 当 前 计 数 值 清 空 , 并 根 据 FreeRTOSConfig.h 文件中配置的
    configSYSTICK_CLOCK_HZ(SysTick 时钟源频率)和 configTICK_RATE_HZ(系统时钟节拍
    频率)计算并设置 SysTick 的重装载值,然后启动 SysTick 计数和中断。
  4. 初始化临界区嵌套计数器为 0。
  5. 调用函数 prvEnableVFP()使能 FPU,因为 ARM Cortex-M3 内核 MCU 无 FPU,此函数
    仅在 ARM Cortex-M4/M7 内核 MCU 平台上被调用,执行改函数后 FPU 被开启。
  6. 接下来将 FPCCR 寄存器的[31:30]置 1,这样在进出异常时,FPU 的相关寄存器就会自
    动地保存和恢复,同样地,因为 ARM Cortex-M3 内核 MCU 无 FPU,此当代码仅在 ARM Cortex-M4/M7 内核 MCU 平台上被调用。
  7. 调用函数 prvStartFirstTask()启动第一个任务。

二、FreeRTOS 启动第一个任务

在这里插入图片描述

1.函数 prvStartFirstTask()

函数 prvStartFirstTask()用于初始化启动第一个任务前的环境,主要是重新设置 MSP 指针,
并使能全局中断,具体的代码如下所示:

__asm void prvStartFirstTask( void )
{
 /* 8 字节对齐 */
 PRESERVE8
 
 ldr r0, =0xE000ED08 /* 0xE000ED08 为 VTOR 地址 */
 ldr r0, [ r0 ] /* 获取 VTOR 的值 */
 ldr r0, [ r0 ] /* 获取 MSP 的初始值 */
 
 /* 初始化 MSP */
 msr msp, r0
 /* 使能全局中断 */
 cpsie i
 cpsie f
 dsb
 isb
 
 /* 调用 SVC 启动第一个任务 */
 svc 0
 nop
 nop
}

从上面的代码可以看出,函数 prvStartFirstTask()是一段汇编代码,解析如下所示:

  1. 首先是使用了 PRESERVE8,进行 8 字节对齐,这是因为,栈在任何时候都是需要 4 字
    节对齐的,而在调用入口得 8 字节对齐,在进行 C 编程的时候,编译器会自动完成的对齐的操
    作,而对于汇编,就需要开发者手动进行对齐。

  2. 接下来的三行代码是为了获得 MSP 指针的初始值,那么这里就能够引出两个问题:
    (1) 什么是 MSP 指针?
    程序在运行过程中需要一定的栈空间来保存局部变量等一些信息。当有信息保存到栈中时,
    MCU 会自动更新 SP 指针,使 SP 指针指向最后一个入栈的元素,那么程序就可以根据 SP 指针
    来从栈中存取信息。对于正点原子的 STM32F1、STM32F4、STM32F7 和 STM32H7 开发板上使
    用的 ARM Cortex-M 的 MCU 内核来说,ARM Cortex-M 提供了两个栈空间,这两个栈空间的堆
    栈指针分别是 MSP(主堆栈指针)和 PSP(进程堆栈指针)。在 FreeRTOS 中 MSP 是给系统栈
    空间使用的,而 PSP 是给任务栈使用的,也就是说,FreeRTOS 任务的栈空间是通过 PSP 指向
    的,而在进入中断服务函数时,则是使用 MSP 指针。当使用不同的堆栈指针时,SP 会等于当
    前使用的堆栈指针。
    在这里插入图片描述

(2) 为什么是 0xE00ED08?
0xE00ED08 是 VTOR(向量表偏移寄存器)的地址,VTOR 中保存了向量表的偏移地址。
一般来说向量表是从其实地址 0x00000000 开始的,但是在有情况下,可能需要修改或重定向向
量表的首地址,因此 ARM Corten-M 提供了 VTOR 对向量表进行从定向。而向量表是用来保存
中断异常的入口函数地址,即栈顶地址的,并且向量表中的第一个字保存的就是栈底的地址,
在 start_stm32xxxxxx.s 文件中有如下定义:

__Vectors DCD __initial_sp ; 栈底指针
 DCD Reset_Handler ; Reset Handler
 DCD NMI_Handler ; NMI Handler
 DCD HardFault_Handler ; Hard Fault Handler
 DCD MemManage_Handler ; MPU Fault Handler

以上就是向量表(只列出前几个)的部分内容,可以看到向量表的第一个元素就是栈指针
的初始值,也就是栈底指针。

在了解了这两个问题之后,接下来再来看看代码。首先是获取 VTOR 的地址,接着获取
VTOR 的值,也就是获取向量表的首地址,最后获取向量表中第一个字的数据,也就是栈底指
针了。
在这里插入图片描述

  1. 在获取了栈顶指针后,将 MSP 指针重新赋值为栈底指针。这个操作相当于丢弃了程序
    之前保存在栈中的数据,因为FreeRTOS从开启任务调度器到启动第一个任务都是不会返回的,
    是一条不归路,因此将栈中的数据丢弃,也不会有影响。
  2. 重新赋值 MSP 后,接下来就重新使能全局中断,因为之前在函数 vTaskStartScheduler()
    中关闭了受 FreeRTOS 的中断。
  3. 最后使用 SVC 指令,并传入系统调用号 0,触发 SVC 中断。

2.函数 vPortSVCHandler()

在这里插入图片描述
在这里插入图片描述

当使能了全局中断,并且手动触发 SVC 中断后,就会进入到 SVC 的中断服务函数中。SVC
的中断服务函数为 vPortSVCHandler(),该函数在 port.c 文件中有定义,具体的代码如下所示
:

__asm void vPortSVCHandler( void )
{
 /* 8 字节对齐 */
 PRESERVE8
 
 /* 获取任务栈地址 */
 ldr r3, = pxCurrentTCB /* r3 指向优先级最高的就绪态任务的任务控制块 */
 ldr r1, [ r3 ] /* r1 为任务控制块地址 */
 ldr r0, [ r1 ] /* r0 为任务控制块的第一个元素(栈顶) */
 
 /* 模拟出栈,并设置 PSP */
 ldmia r0 !, { r4 - r11 } /* 任务栈弹出到 CPU 寄存器 */
 msr psp, r0 /* 设置 PSP 为任务栈指针 */
 isb
 
 /* 使能所有中断 */
 mov r0, # 0
 msr basepri,
 
 /* 使用 PSP 指针,并跳转到任务函数 */
 orr r14, # 0xd
 bx r14
}

从上面代码中可以看出,函数 vPortSVCHandler()就是用来跳转到第一个任务函数中去的,
该函数的具体解析如下:

  1. 首先通过 pxCurrentTCB 获取优先级最高的就绪态任务的任务栈地址,优先级最高的就
    绪态任务就是系统将要运行的任务。pxCurrentTCB 是一个全局变量,用于指向系统中优先级最
    高的就绪态任务的任务控制块,在前面创建 start_task 任务、空闲任务、定时器处理任务时自动
    根据任务的优先级高低进行赋值的,具体的赋值过程在后续分析任务创建函数时,会具体分析。
    这里举个例子,在《FreeRTOS 移植实验》中,start_task 任务、空闲任务、定时器处理任务
    的优先级如下表所示:
    在这里插入图片描述
    在这里插入图片描述
    从上表可以看出,在《FreeRTOS 移植实验》中,定时器处理任务的任务优先级为 31,是系
    统中优先级最高的任务,因此当进入 SVC 中断时,pxCurrentTCB 就是指向了定时器处理任务
    的任务控制块。

接着通过获取任务控制块中的第一个元素,得到该任务的栈顶指针,任务控制块的相关内
容,请查看第 5.5 小节《FreeRTOS 任务控制块》。

  1. 接下来通过任务的栈顶指针,将任务栈中的内容出栈到 CPU 寄存器中,任务栈中的内
    容在调用任务创建函数的时候,已经初始化了。然后再设置 PSP 指针,那么,这么一来,任务
    的运行环境就准备好了。
  2. 通过往 BASEPRI 寄存器中写 0,允许中断。
  3. 最后通过两条汇编指令,使 CPU 跳转到任务的函数中去执行,代码如下所示:
orr r14, # 0xd
 bx r14

要弄清楚这两条汇编代码,首先要清楚 r14 寄存器是干什么用的。通常情况下,r14 为链接
寄存器(LR),用于保存函数的返回地址。但是在异常或中断处理函数中,r14 为 EXC_RETURN
(关于 r14 寄存器的相关内容,感兴趣的读者请自行查阅相关资料),EXC_RETURN 各比特位
的描述如下表所示:
在这里插入图片描述
因为此时是在 SVC 的中断服务函数中,因此此时的 r14 应为 EXC_RETURN,将 r14 与 0xd
作或操作,然后将值写入 r14,那么就是将 r14 的值设置为了 0xFFFFFFED 或 0xFFFFFFED(具
体看是否使用了浮点单元),即返回后进入线程模式,并使用 PSP。这里要注意的是,SVC 中断
服务函数的前面,将 PSP 指向了任务栈。

说了这么多,FreeRTOS 对于进入中断后 r14 为 EXC_RETURN 的具体应用就是,通过判断
EXC_RETURN 的 bit4 是否为 0,来判断任务是否使用了浮点单元。

最后通过 bx r14 指令,跳转到任务的任务函数中执行,执行此指令,CPU 会自动从 PSP 指
向的栈中出栈 R0、R1、R2、R3、R12、LR、PC、xPSR 寄存器,并且如果 EXC_RETURN 的
bit4 为 0(使用了浮点单元),那么 CPU 还会自动恢复浮点寄存器。

三、任务切换

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

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

相关文章

【对角线遍历】python

没啥思路 class Solution:def findDiagonalOrder(self, mat: List[List[int]]) -> List[int]:mlen(mat)nlen(mat[0])ret[]if len(mat)0:return retcount0#mn-1是对角线总数while count<mn-1:#x和y的和刚好是count数#偶数为右上走if count%20:xcount if(count<m)else (…

Django 里html模板

Django 提供两种方式让程序员自定义html模板。 第一种方法 在项目文件夹里的urls.py进行添加 修改代码如下 from django.contrib import admin from django.urls import path from app01 import views # 得添加这行urlpatterns [path(xxx/, views.home), # 添加这行path(…

有一个3x4的矩阵,要求用函数编写程序求出其中值最大的那个元素,以及其所在的行号和列号

常量和变量可以用作函数实参&#xff0c;同样数组元素也可以作函数实参&#xff0c;其用法与变量相同。数组名也可以作实参和形参&#xff0c;传递的是数组的起始地址。 用数组元素作函数实参&#xff1a; 由于实参可以是表达式&#xff0c;而数组元素可以是表达式的组…

如何在Windows 10上对硬盘进行碎片整理?这里提供步骤

随着时间的推移&#xff0c;由于文件系统中的碎片&#xff0c;硬盘驱动器可能会开始以较低的效率运行。为了加快驱动器的速度&#xff0c;你可以使用内置工具在Windows 10中对其进行碎片整理和优化。方法如下。 什么是碎片整理 随着时间的推移&#xff0c;组成文件的数据块&a…

电机控制系列模块解析(22)—— 零矢量刹车

一、零矢量刹车 基本概念 逆变器通常采用三相桥式结构&#xff0c;包含六个功率开关元件&#xff08;如IGBT或MOSFET&#xff09;&#xff0c;分为上桥臂和下桥臂。每个桥臂由两个反并联的开关元件组成&#xff0c;上桥臂和下桥臂对应于电机三相绕组的正负端。正常工作时&…

原哥花了1个多月的时间终于开发了一款基于android studio的原生商城app

大概讲一下这个app实现的功能和前后端技术架构。 功能简介 广告展示商品展示跳转淘宝联盟优惠卷购买发布朋友圈宝妈知识资讯商品搜索朋友圈展示/点赞/评论登陆注册版本升级我的个人资料商品和资讯收藏我的朋友圈意见反馈 安卓端技术选型 Arouter组件化daggerrxjavaretrofit…

技术面试,项目实战,求职利器

之前找工作一直想找一个能真正系统性学开发的地方&#xff0c;之前毕业找工作的时候无意间碰到下面这个网站&#xff0c;感觉还挺不错的&#xff0c;用上面的技术实战内容应对技术面试&#xff0c;也算是求职利器了。有需要的可以自取&#xff1a; https://how2j.cn?p156336 实…

基于springboot+vue的智慧外贸平台

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

基础技术-ELF系列(1)-ELF文件基础

成就更好的自己 本篇是基础技术系列中ELF相关技术的首篇文章。 尽管网上有许多关于ELF相关内容的文章&#xff0c;但总体而言&#xff0c;要么是一些非常基础且重复性强的内容&#xff0c;要么直接深入探讨相对高深的主题&#xff0c;缺乏系统化分析和解释。 接下来&#xf…

Redis - 缓存场景

学习资料 学习的黑马程序员哔站项目黑马点评&#xff0c;用作记录和探究原理。 Redis缓存 缓存 &#xff1a;就是数据交换的缓冲区&#xff0c;是存储数据的临时地方&#xff0c;读写性能较高 缓存常见的场景: 数据库查询加速&#xff1a;通过将频繁查询的数据缓存起来&…

论文阅读--ActionCLIP

原来的动作识别问题在于标注太难太贵&#xff0c;将动作表示为短语的latent space太大 本文的贡献&#xff1a;&#xff08;1&#xff09;将CLIP的image encoder换成video encoder&#xff0c;方法与CLIP4Clip几乎一样 &#xff08;2&#xff09;CLIP的ground truth来自于文本…

使用pyqt绘制一个爱心!

使用pyqt绘制一个爱心&#xff01; 介绍效果代码 介绍 使用pyqt绘制一个爱心&#xff01; 效果 代码 import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget from PyQt5.QtGui import QPainter, QPen, QBrush, QColor from PyQt5.QtCore import Qt, Q…

【气象常用】间断时间序列图

效果图&#xff1a; 主要步骤&#xff1a; 1. 数据准备&#xff1a;随机数组 2. 图像绘制&#xff1a;绘制间断的时间序列 详细代码&#xff1a;着急的直接拖到最后有完整代码 步骤一&#xff1a;导入库包及图片存储路径并设置中文字体为宋体&#xff0c;西文为新罗马&…

没有telnet情况下判断主机端口是否开放的方法

没有telnet情况下判断主机端口是否开放的方法 方式一 ssh -v 101.132.64.231 -p 80显示结果 如果有显示 debug1: Connection established. 就说明端口是开放的 端口未开放的情况是显示 方式二 echo >/dev/tcp/101.132.64.231/3306效果如下 如果没有任何输出&#xff0c;…

Redis开发实战

单机部署安装 服务端下载&#xff0c;安装&#xff0c;启动去官网下载最新的版本&#xff1a;http://redis.io/download &#xff0c;这里用的是3.0.2解压后&#xff0c;进入解压好的文件夹redis的安装非常简单&#xff0c;因为已经有现成的Makefile文件&#xff0c;所以直接先…

Photoshop插件(UXP)编写过程中,如何更新sp-checkbox的选中状态

✨问题说明 sp-checkbox是uxpSpectrum UXP Widgets下的一个小组件&#xff0c;内置样式大概是这样&#xff1a; 那么&#xff0c;如果用js动态的改变选中的状态&#xff0c;应该如何做呢&#xff1f; 如果直接是html来写&#xff1a; <sp-checkbox checked>Checked<…

freemarker ftl模板 格式、列表、图片

文章目录 前言一、freemarker实现内容替换二、ftl 模板1.word另存ftl2.编辑ftl文件2.1 了解一下常用的标记及其说明2.2 list处理2.3 红线2.4 图片 总结 前言 固定内容word生成&#xff1a;freemarker ftl模板 动态表格生成&#xff1a;https://blog.csdn.net/mr_wanter/articl…

基于MetaGPT构建LLM多智能体

前言 你好&#xff0c;我是GISer Liu&#xff0c;在上一篇文章中&#xff0c;我们用了两万多字详细拆解了单个Agent的组成&#xff0c;并通过Github Trending订阅智能体理解MetaGPT框架的订阅模块如何解决应用问题&#xff0c;但是对于复杂&#xff0c;并行的任务&#xff0c;单…

Java进阶学习笔记20——枚举

认识枚举&#xff1a; 枚举是一种特殊的类。 枚举类的格式&#xff1a; 说明&#xff1a; 第一行是罗列枚举的对象名称。只能写合法的标识符&#xff08;名称&#xff09;&#xff0c;多个名称用逗号隔开。 这些名称本质上都是常量&#xff0c;每个变量都会记住枚举类的一个…

HIVE3.1.3+ZK+Kerberos+Ranger2.4.0高可用集群部署

目录 一、集群规划 二、介质下载 三、基础环境准备 1、解压文件 2、配置环境变量 四、配置zookeeper 1、创建主体 2、修改zoo.cfg 3、新增jaas.conf 4、新增java.env 5、重启ZK 6、验证ZK 五、配置元数据库 六、安装HIVE 1、创建Hiver的kerberso主体 2…