鸿蒙轻内核M核源码分析系列三 数据结构-任务排序链表

往期知识点记录:

  • 鸿蒙(HarmonyOS)应用层开发(北向)知识点汇总

在鸿蒙轻内核源码分析系列一和系列二,我们分析了双向循环链表、优先级就绪队列的源码。本文会继续给读者介绍鸿蒙轻内核源码中重要的数据结构:任务排序链表TaskSortLinkAttr。鸿蒙轻内核的任务排序链表,用于任务延迟到期/超时唤醒等业务场景,是一个非常重要、非常基础的数据结构。本文中所涉及的源码,以OpenHarmony LiteOS-M内核为例。

1 任务排序链表

我们先看下任务排序链接的数据结构。任务排序链表是一个环状的双向链表数组,任务排序链表属性结构体TaskSortLinkAttr作为双向链表的头结点,指向双向链表数组的第一个元素,还维护游标信息,记录当前的位置信息。我们先看下排序链表属性的结构体的定义。

1.1 任务排序链表属性结构体定义

kernel\include\los_task.h头文件中定义了排序链表属性的结构体TaskSortLinkAttr。该结构体定义了排序链表的头节点LOS_DL_LIST *sortLink,游标UINT16 cursor,还有一个保留字段,暂时没有使用。

源码如下:

typedef struct {LOS_DL_LIST *sortLink;UINT16      cursor;UINT16      reserved;
} TaskSortLinkAttr;

在文件kernel\src\los_task.c中定义了排序链表属性结构体TaskSortLinkAttr类型的全局变量g_taskSortLink,该全局变量的成员变量sortLink作为排序链表的头结点,指向一个长度为32的环状的双向链表数组,成员变量cursor作为游标记录环状数组的当前游标位置。源代码如下。

LITE_OS_SEC_BSS  TaskSortLinkAttr                    g_taskSortLink;

我们使用示意图来讲述一下。任务排序链表是环状双向链表数组,长度为32,每一个元素是一个双向链表,挂载任务LosTaskCB的链表节点timerList。任务LosTaskCB的成员变量idxRollNum记录数组的索引和滚动数。全局变量g_taskSortLink的成员变量cursor记录当前游标位置,每过一个Tick,游标指向下一个位置,转一轮需要32 ticks。当运行到的数组位置,双向链表不为空,则把第一个节点维护的滚动数减1。这样的数据结构类似钟表表盘,也称为时间轮

我们举个例子来说明,基于时间轮实现的任务排序链表是如何管理任务延迟超时的。假如当前游标cursor为1,当一个任务需要延时72 ticks,72=2*32+8,表示排序索引sortIndex为8,滚动数rollNum为2。会把任务插入数组索引为sortIndex+cursor=9的双向链表位置,索要9处的双向链表维护节点的滚动为2。随着Tick时间的进行,从当前游标位置运行到数组索引位置9,历时8 ticks。运行到9时,如果滚动数大于0,则把滚动数减1。等再运行2轮,共需要72 ticks,任务就会延迟到期,可以从排序链表移除。每个数组元素对应的双向链表的第一个链表节点的滚动数表示需要转多少轮,节点任务才到期。第二个链表节点的滚动数需要加上第一个节点的滚动数,表示第二个节点需要转的轮数。依次类推。

示意图如下:

1.2 任务排序链表宏定义

OS_TSK_SORTLINK_LEN头文件中定义了一些和任务排序链表相关的宏定义。延迟任务双向链表数组的长度定义为32,高阶bit位位数为5,低阶bit位位数为27。对于任务的超时时间,取其高27位作为滚动数,低5位作为数组索引。

源码如下:

    /*** 延迟任务双向链表数组的数量(桶的数量):32*/#define OS_TSK_SORTLINK_LEN                         32/*** 高阶bit位数目:5*/#define OS_TSK_HIGH_BITS                            5U/*** 低阶bit位数目:27*/#define OS_TSK_LOW_BITS                             (32U - OS_TSK_HIGH_BITS)/*** 滚动数最大值:0xFFFF FFDF,1111 0111 1111 1111 1111 1111 1101 1111*/#define OS_TSK_MAX_ROLLNUM                          (0xFFFFFFFFU - OS_TSK_SORTLINK_LEN)/*** 任务延迟时间数的位宽:5*/#define OS_TSK_SORTLINK_LOGLEN                      5/*** 延迟任务的桶编号的掩码:31、0001 1111*/#define OS_TSK_SORTLINK_MASK                        (OS_TSK_SORTLINK_LEN - 1U)/*** 滚动数的高阶掩码:1111 1000 0000 0000 0000 0000 0000 0000*/#define OS_TSK_HIGH_BITS_MASK                       (OS_TSK_SORTLINK_MASK << OS_TSK_LOW_BITS)/*** 滚动数的低阶掩码:0000 0111 1111 1111 1111 1111 1111 1111*/#define OS_TSK_LOW_BITS_MASK                        (~OS_TSK_HIGH_BITS_MASK)

2 任务排序链表操作

我们分析下任务排序链表的操作,包含初始化,插入,删除,滚动数更新,获取下一个到期时间等。

2.1 初始化排序链表

在系系统内核初始化启动阶段,在函数UINT32 OsTaskInit(VOID)中初始化任务排序链表。该函数的调用关系如下,main.c:main() --> kernel\src\los_init.c:LOS_KernelInit() --> kernel\src\los_task.c:OsTaskInit()

初始化排序链表函数的源码如下:

LITE_OS_SEC_TEXT_INIT UINT32 OsTaskInit(VOID)
{UINT32 size;UINT32 index;LOS_DL_LIST *listObject = NULL;......
⑴  size = sizeof(LOS_DL_LIST) * OS_TSK_SORTLINK_LEN;listObject = (LOS_DL_LIST *)LOS_MemAlloc(m_aucSysMem0, size);
⑵  if (listObject == NULL) {(VOID)LOS_MemFree(m_aucSysMem0, g_taskCBArray);return LOS_ERRNO_TSK_NO_MEMORY;}⑶  (VOID)memset_s((VOID *)listObject, size, 0, size);
⑷  g_taskSortLink.sortLink = listObject;g_taskSortLink.cursor = 0;for (index = 0; index < OS_TSK_SORTLINK_LEN; index++, listObject++) {
⑸      LOS_ListInit(listObject);}return LOS_OK;
}

⑴处代码计算需要申请的双向链表的内存大小,OS_TSK_SORTLINK_LEN为32,即需要为32个双向链表节点申请内存空间。然后申请内存,⑵处申请内存失败时返回相应错误码。⑶处初始化申请的内存区域为0等。⑷处把申请的双向链表节点赋值给g_taskSortLink的链表节点.sortLink,作为排序链表的头节点,游标.cursor初始化为0。然后⑸处的循环,调用LOS_ListInit()函数把双向链表数组每个元素都初始化为双向循环链表。

2.2 插入排序链表

插入排序链表的函数为OsTaskAdd2TimerList()。在任务等待互斥锁/信号量等资源时,都需要调用该函数将任务加入到对应的排序链表中。该函数包含两个入参,第一个参数LosTaskCB *taskCB用于指定要延迟的任务,第二个参数UINT32 timeout指定超时等待时间。

源码如下:

LITE_OS_SEC_TEXT VOID OsTaskAdd2TimerList(LosTaskCB *taskCB, UINT32 timeout)
{LosTaskCB *taskDelay = NULL;LOS_DL_LIST *listObject = NULL;UINT32 sortIndex;UINT32 rollNum;⑴  sortIndex = timeout & OS_TSK_SORTLINK_MASK;rollNum = (timeout >> OS_TSK_SORTLINK_LOGLEN);
⑵  (sortIndex > 0) ? 0 : (rollNum--);
⑶  EVALUATE_L(taskCB->idxRollNum, rollNum);
⑷  sortIndex = (sortIndex + g_taskSortLink.cursor);sortIndex = sortIndex & OS_TSK_SORTLINK_MASK;
⑸  EVALUATE_H(taskCB->idxRollNum, sortIndex);
⑹  listObject = g_taskSortLink.sortLink + sortIndex;
⑺  if (listObject->pstNext == listObject) {LOS_ListTailInsert(listObject, &taskCB->timerList);} else {
⑻      taskDelay = LOS_DL_LIST_ENTRY((listObject)->pstNext, LosTaskCB, timerList);do {
⑼          if (UWROLLNUM(taskDelay->idxRollNum) <= UWROLLNUM(taskCB->idxRollNum)) {UWROLLNUMSUB(taskCB->idxRollNum, taskDelay->idxRollNum);} else {
⑽              UWROLLNUMSUB(taskDelay->idxRollNum, taskCB->idxRollNum);break;}⑾          taskDelay = LOS_DL_LIST_ENTRY(taskDelay->timerList.pstNext, LosTaskCB, timerList);} while (&taskDelay->timerList != (listObject));⑿      LOS_ListTailInsert(&taskDelay->timerList, &taskCB->timerList);}
}

⑴处代码计算等待时间timeout的低5位作为数组索引,高27位作为滚动数rollNum。这2行代码数学上的意义,就是把等待时间处于32得到的商作为滚动数,余数作为数组索引。⑵处代码,如果余数为0,可以整除时,滚动数减1。减1设计的原因是,在函数VOID OsTaskScan(VOID)中,每一个tick到来时,如果滚动数大于0,滚动数减1,并继续滚动一圈。后文会分析该函数VOID OsTaskScan(VOID)

⑶处代码把滚动数赋值给任务taskCB->idxRollNum的低27位。⑷处把数组索引加上游标,然后执行⑸赋值给任务taskCB->idxRollNum的高5位。⑹根据数组索引获取双向链表头结点,⑺如果此处双向链表为空,直接插入链表里。如果链表不为空,执行⑻获取第一个链表节点对应的任务taskDelay,然后遍历循环双向链表,把任务插入到合适的位置。⑼处如果待插入任务taskCB的滚动数大于等于当前链表节点对应任务的滚动数,则从待插入任务taskCB的滚动数中减去当前链表节点对应任务的滚动数,然后执行⑾获取下一个节点继续遍历。⑽处如果待插入任务taskCB的滚动数小于当前链表节点对应任务的滚动数,则从当前链表节点对应任务的滚动数中减去待插入任务taskCB的滚动数,然后跳出循环。执行⑿,完成任务插入。插入过程,可以结合上文的示意图进行理解。

2.3 从排序链表中删除

从排序链表中删除的函数为VOID OsTimerListDelete(LosTaskCB *taskCB)。在任务恢复/删除等场景中,需要调用该函数将任务从任务排序链表中删除。该函数包含一个参数LosTaskCB *taskCB,用于指定要从排序链表中删除的任务。

源码如下:

LITE_OS_SEC_TEXT VOID OsTimerListDelete(LosTaskCB *taskCB)
{LOS_DL_LIST  *listObject = NULL;LosTaskCB  *nextTask = NULL;UINT32 sortIndex;⑴  sortIndex = UWSORTINDEX(taskCB->idxRollNum);
⑵  listObject = g_taskSortLink.sortLink + sortIndex;⑶  if (listObject != taskCB->timerList.pstNext) {
⑷      nextTask = LOS_DL_LIST_ENTRY(taskCB->timerList.pstNext, LosTaskCB, timerList);UWROLLNUMADD(nextTask->idxRollNum, taskCB->idxRollNum);}⑸  LOS_ListDelete(&taskCB->timerList);
}

⑴处代码获取待从排序链表中删除的任务对应的数字索引。⑵处代码获取排序链表的头节点listObject。⑶处代码判断待删除节点是否是最后一个节点,如果不是最后一个节点,执行执行⑷处代码获取待删除节点的下一个节点对应的任务nextTask,在下一个节点的滚动数中增加待删除节点的滚动数,然后执行⑸处代码执行删除操作。如果是最后一个节点,直接执行⑸处代码删除该节点即可。

2.4 获取下一个超时到期时间

获取下一个超时到期时间的函数为OsTaskNextSwitchTimeGet(),我们分析下其代码。

源码如下:

UINT32 OsTaskNextSwitchTimeGet(VOID)
{LosTaskCB *taskCB = NULL;UINT32 taskSortLinkTick = LOS_WAIT_FOREVER;LOS_DL_LIST *listObject = NULL;UINT32 tempTicks;UINT32 index;⑴  for (index = 0; index < OS_TSK_SORTLINK_LEN; index++) {
⑵      listObject = g_taskSortLink.sortLink + ((g_taskSortLink.cursor + index) % OS_TSK_SORTLINK_LEN);
⑶      if (!LOS_ListEmpty(listObject)) {
⑷          taskCB = LOS_DL_LIST_ENTRY((listObject)->pstNext, LosTaskCB, timerList);
⑸          tempTicks = (index == 0) ? OS_TSK_SORTLINK_LEN : index;
⑹          tempTicks += (UINT32)(UWROLLNUM((UINT32)taskCB->idxRollNum) * OS_TSK_SORTLINK_LEN);
⑺          if (taskSortLinkTick > tempTicks) {taskSortLinkTick = tempTicks;}}}return taskSortLinkTick;
}

⑴处代码循环遍历双向链表数组,⑵处代码从当前游标位置开始获取排序链表的头节点listObject。⑶处代码判断排序链表是否为空,如果排序链表为空,则继续遍历下一个数组。如果链表不为空,⑷处代码获取排序链表的第一个链表节点对应的任务。⑸处如果遍历的数字索引为0,tick数目使用32,否则使用具体的数字索引。⑹处获取任务的滚动数,计算出需要的等待时间,加上⑸处计算出的不足滚动一圈的时间。⑺处计算出需要等待的最小时间,即下一个最快到期的时间。

3 排序链表和Tick时间的关系

任务加入到排序链表后,时间一个tick一个tick的逝去,排序链表中的滚动数该如何更新呢?

时间每走过一个tick,系统就会调用Tick中断的处理函数OsTickHandler(),该函数在kernel\src\los_tick.c文件中实现。下面是该函数的代码片段,⑴处代码分别任务的超时到期情况。

LITE_OS_SEC_TEXT VOID OsTickHandler(VOID)
{
#if (LOSCFG_BASE_CORE_TICK_HW_TIME == 1)platform_tick_handler();
#endifg_ullTickCount++;#if (LOSCFG_BASE_CORE_TIMESLICE == 1)OsTimesliceCheck();
#endif⑴  OsTaskScan();  // task timeout scan#if (LOSCFG_BASE_CORE_SWTMR == 1)(VOID)OsSwtmrScan();
#endif
}

详细分析下函数OsTaskScan(),来了解排序链表和tick时间的关系。函数在kernel\base\los_task.c文件中实现,代码片段如下:

LITE_OS_SEC_TEXT VOID OsTaskScan(VOID)
{LosTaskCB *taskCB = NULL;BOOL needSchedule = FALSE;LOS_DL_LIST *listObject = NULL;UINT16 tempStatus;UINTPTR intSave;intSave = LOS_IntLock();⑴  g_taskSortLink.cursor = (g_taskSortLink.cursor + 1) % OS_TSK_SORTLINK_LEN;listObject = g_taskSortLink.sortLink + g_taskSortLink.cursor;
⑵  if (listObject->pstNext == listObject) {LOS_IntRestore(intSave);return;}⑶  for (taskCB = LOS_DL_LIST_ENTRY((listObject)->pstNext, LosTaskCB, timerList);&taskCB->timerList != (listObject);) {tempStatus = taskCB->taskStatus;
⑷      if (UWROLLNUM(taskCB->idxRollNum) > 0) {UWROLLNUMDEC(taskCB->idxRollNum);break;}⑸      LOS_ListDelete(&taskCB->timerList);
⑹      if (tempStatus & OS_TASK_STATUS_PEND) {taskCB->taskStatus &= ~(OS_TASK_STATUS_PEND);LOS_ListDelete(&taskCB->pendList);taskCB->taskSem = NULL;taskCB->taskMux = NULL;}
⑺      else if (tempStatus & OS_TASK_STATUS_EVENT) {taskCB->taskStatus &= ~(OS_TASK_STATUS_EVENT);}
⑻      else if (tempStatus & OS_TASK_STATUS_PEND_QUEUE) {LOS_ListDelete(&taskCB->pendList);taskCB->taskStatus &= ~(OS_TASK_STATUS_PEND_QUEUE);} else {taskCB->taskStatus &= ~(OS_TASK_STATUS_DELAY);}⑼      if (!(tempStatus & OS_TASK_STATUS_SUSPEND)) {taskCB->taskStatus |= OS_TASK_STATUS_READY;OsHookCall(LOS_HOOK_TYPE_MOVEDTASKTOREADYSTATE, taskCB);OsPriqueueEnqueue(&taskCB->pendList, taskCB->priority);needSchedule = TRUE;}if (listObject->pstNext == listObject) {break;}taskCB = LOS_DL_LIST_ENTRY(listObject->pstNext, LosTaskCB, timerList);}LOS_IntRestore(intSave);⑽  if (needSchedule) {LOS_Schedule();}
}

⑴处代码更新全局变量g_taskSortLink的游标,指向双向链表数组下一个位置,然后获取该位置的双向链表头结点listObject
⑵如果链表为空,则返回。如果双向链表不为空,则执行
⑶循环遍历每一个链表节点。
⑷处如果链表节点的滚动数大于0,则滚动数减1,说明任务还需要继续等待一轮。如果链表节点的滚动数等于0,说明任务超时到期,执行
⑸从排序链表中删除。接下来需要根据任务状态分别处理,
⑹处如果代码是阻塞状态,取消阻塞状态,并从阻塞链表中删除。
⑺处如果任务阻塞在事件中,取消阻塞状态。
⑻如果任务阻塞在队列,从阻塞链表中删除,取消阻塞状态,如果不是上述状态,取消延迟状态OS_TASK_STATUS_DELAY
⑼处如果代码是挂起状态,设置任务为就绪状态,加入任务就绪队列,设置需要重新调度标记。
⑽如果设置需要重新调度,调用调度函数触发任务调度。


小结

掌握鸿蒙轻内核的排序链表TaskSortLinkAttr这一重要的数据结构,会给进一步学习、分析鸿蒙轻内核源代码打下了基础,让后续的学习更加容易。

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

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

相关文章

运动耳机怎么选购?解密最值得购买的五大品牌!

​现在大家都越来越注重健康&#xff0c;运动成了很多人的日常&#xff0c;不管是轻松跑跑步还是来点高强度训练&#xff0c;都能让人身心都受益。运动时候如果能听点音乐&#xff0c;那感觉就更好了&#xff0c;能让运动效果更上一层楼。但是&#xff0c;那种塞进耳朵里的耳机…

Cmake之2.4版本重要特性及用法实例(十)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…

S3C2440开发板:时钟,PWM定时器控制蜂鸣器发声

时钟 时钟和电源管理模块由三部分组成&#xff1a;时钟控制&#xff0c;USB 控制和电源控制。 S3C2440A 中的时钟控制逻辑可以产生必须的时钟信号&#xff0c;包括 CPU 的 FCLK&#xff0c;AHB 总线外设的 HCLK 以及 APB 总线外设的 PCLK。S3C2440A 包含两个锁相环&#xff08…

数据结构:(LeetCode203)移除链表元素

给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5]示例 2&#xff1a; 输入&#xff1…

深度学习之 OpenCV 图像边缘检测算法解析及代码演示

引言 在计算机视觉领域&#xff0c;边缘检测是一种重要的图像预处理技术&#xff0c;用于识别图像中对象的边界。边缘检测有助于提取图像的关键特征&#xff0c;这对于后续的图像分析&#xff08;如物体识别、运动检测等&#xff09;至关重要。OpenCV 是一个强大的计算机视觉库…

这个俄罗斯平台爆火跨境圈,据说大卖已冲!无货源卖家又行了?

近几年&#xff0c;俄罗斯电商市场的增长速度非常惊人&#xff0c;从宏观来看&#xff0c;俄罗斯已经超越东南亚各国&#xff0c;成为2023年网上零售销售额增长最快的国家。据权威数据分析机构Statista的预测&#xff0c;2024年俄罗斯电子商务市场的收入预计能够达到337.6亿美元…

11、Django Admin启用对计算字段的过滤

重新定义admin.py中的Hero管理模型如下&#xff1a; admin.register(Hero) class HeroAdmin(admin.ModelAdmin):list_display ("name", "is_immortal", "category", "origin", "is_very_benevolent")list_filter ("…

代码随想录算法训练营第32天 动态规划part01| 题目:理论基础 、 509. 斐波那契数 、70. 爬楼梯 、 746. 使用最小花费爬楼梯

代码随想录算法训练营第32天 动态规划part01| 题目&#xff1a;理论基础 、 509. 斐波那契数 、70. 爬楼梯 、 746. 使用最小花费爬楼梯 文章来源&#xff1a;代码随想录 理论 题目名称&#xff1a;509. 斐波那契数 斐波那契数&#xff0c;通常用 F(n) 表示&#xff0c;形成的…

沐渥科技:两显氮气柜和三显氮气柜要怎么选择?

两显氮气柜通常指的是控制面板上有两个LED数码显示界面&#xff0c;用于显示温度和湿度&#xff1b;三显氮气柜则有三个LED数码显示界面&#xff0c;能够直观地显示出温度、湿度和含氧量。这样的设计便于用户快速全面地了解柜内环境状态&#xff0c;不需要额外的操作即可掌握所…

PHP智能匹配轻松预订自习室在线订座系统小程序源码

智能匹配&#xff0c;轻松预订——自习室在线订座系统 &#x1f4da;【开篇&#xff1a;告别排队&#xff0c;迎接智能学习新时代】&#x1f4da; 还在为找不到合适的自习室座位而烦恼吗&#xff1f;是不是每次去图书馆或自习室都要提前好久去排队占位&#xff1f;现在&#…

不同大模型代码解释对比

包含ChatGPT&#xff0c;讯飞星火&#xff0c;通义千问&#xff0c;腾讯元宝&#xff0c;智谱清言。 目标是想让大模型解释一个用于预处理人体骨骼关节三维坐标数据样本进行填补空帧的Python函数。 def f_padding_none(data):s data.copy()# print(pad the null frames with…

笔记整理—uboot番外(6)针对x210的网卡说明

对于x210而言&#xff0c;在三星中与九鼎官方的uboot都默认使用了二号串口。详见CONFIG_SERIALn&#xff08;n1~4&#xff09;。 更改串口后&#xff0c;应先插回原串口&#xff0c;进行iROM打印&#xff0c;内部iNnad校验核失败的信息&#xff0c;之后再插入其串口进行启动。 …

win10添加右键菜单打开VSCode

当进入一个文件夹后&#xff0c;想右键直接打开我的工程&#xff0c;用发现没有vscode项。本文就来介绍如何右键通过vsCode打开项目。步骤1&#xff1a;在桌面新建一个txt文档&#xff0c;用文本编辑器打开 步骤2&#xff1a; 查看vscode所在位置 在桌面找到vscode快捷键图标&…

EasyExcel 文件导出 - 合并某些列值相同的行

文章目录 EasyExcel 文件导出 - 合并某些列值相同的行最终效果实现思路创建单元格合并的策略类使用 EasyExcel 文件导出 - 合并某些列值相同的行 在数据处理与文件导出的过程中&#xff0c;我们常常会遇到各种特定的需求。今天&#xff0c;我们就来探讨一下使用 EasyExcel 进行…

【STM32+HAL库】---- 硬件IIC驱动0.96OLED

硬件开发板&#xff1a;STM32G0B1RET6 软件平台&#xff1a;cubemaxkeilVScode内容原著声明 代码借鉴学习于以下文章&#xff1a; STM32 使用硬件IIC驱动0.96寸4针IOLED显示器&#xff08;HAL库&#xff09; 1 新建cubemax工程 1.1 配置系统时钟RCC 1.2 配置引脚 1.3 导出工…

【盖世汽车-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

SQLite3 数据库

一、SQLite3 命令 1. 基本命令 .tables&#xff1a;用于查看数据库中的表。.headers on/off&#xff1a;开启或者关闭表头&#xff0c;方便查看表结构。.mode column&#xff1a;使列对齐&#xff0c;增强数据的可读性。.width 列宽1 列宽2&#xff1a;设置每一列的列宽&…

抖音无水印视频下载

抖音想必大家都不陌生&#xff0c;大家经常会在抖音上刷到各种有意思的视频&#xff0c;但是我们想保存就会发现会有水印信息&#xff0c;那么我们要如何下载无水印的图片呢&#xff1f; 使用说明 我们可以先点击抖音的分享按钮&#xff0c;复制一下视频的链接 然后打开智游剪…

Spring6梳理5——基于XML管理Bean环境搭建

以上笔记来源&#xff1a; 尚硅谷Spring零基础入门到进阶&#xff0c;一套搞定spring6全套视频教程&#xff08;源码级讲解&#xff09;https://www.bilibili.com/video/BV1kR4y1b7Qc 目录 ①搭建模块 ②引入配置文件 ③创建BeanXML文件 ④创建Java类文件&#xff08;User…

pycharm项目里有中文右下角修改无效怎么办 包括注释里有中文

在我们编写pycharm项目时&#xff0c;可能会遇到项目里面有中文运行导致报错&#xff0c;即使我们在右下角修改编码格式&#xff0c;还是无效的 解决办法&#xff1a;点击左上角File,Settings,找到Editor里面的File Encodings ,圈住的位置都要改为UTF-8点击Apply就可以了