0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

鸿蒙内核源码:32级优先级的进程和线程调度

鸿蒙系统HarmonyOS 来源:my.oschina 作者:鸿蒙内核源码分析 2021-04-24 11:18 次阅读

先看四个宏定义,进程和线程(线程就是任务)最高和最低优先级定义,[0,31]区间,即32级,优先级用于调度,CPU根据这个来决定先运行哪个进程和任务。

#define OS_PROCESS_PRIORITY_HIGHEST      0 //进程最高优先级
#define OS_PROCESS_PRIORITY_LOWEST       31 //进程最低优先级
#define OS_TASK_PRIORITY_HIGHEST    0 //任务最高优先级,软时钟任务就是最高级任务,见于 OsSwtmrTaskCreate
#define OS_TASK_PRIORITY_LOWEST     31 //任务最低优先级

为何进程和线程都是32个优先级?

回答这个问题之前,先回答另一个问题,为什么人类几乎所有的文明都是用十进制的计数方式。答案掰手指就知道了,因为人有十根手指头。玛雅人的二十进制那是把脚指头算上了,但其实也算是十进制的表示。

这是否说明一个问题,认知受环境的影响,方向是怎么简单/方便怎么来。这也可以解释为什么人类语言发音包括各种方言对妈妈这个词都很类似,因为婴儿说mama是最容易的。注意认识这点很重要!

而计算机的世界是二进制的,是是非非,清清楚楚,特别的简单,二进制已经最简单了,到底啦,不可能有更简单的了。还记得双向链表篇中说过的吗,因为简单所以才不简单啊,大道若简,计算机就靠着这01码,表述万千世界。

但人类的大脑不擅长存储,二进制太长了数到100就撑爆了大脑,记不住,为了记忆和运算方便,编程常用靠近10进制的 16进制来表示 ,0x9527ABCD看着比 0011000111100101010100111舒服多了。

应用开发和内核开发有哪些区别?

区别还是很大的,这里只说一点,就是对位的控制能力,内核会出现大量的按位运算(&,|,~,^) , 一个变量的不同位表达不同的含义,但这在应用程序员那是很少看到的,他们用的更多的是逻辑运算(&&,||,!)

#define OS_TASK_STATUS_INIT         0x0001U //初始化状态
#define OS_TASK_STATUS_READY        0x0002U //就绪状态的任务都将插入就绪队列
#define OS_TASK_STATUS_RUNNING      0x0004U //运行状态
#define OS_TASK_STATUS_SUSPEND      0x0008U //挂起状态
#define OS_TASK_STATUS_PEND         0x0010U //阻塞状态

这是任务各种状态(注者后续将比如成贴标签)表述,将它们还原成二进制就是:

0000000000000001 =0x0001U

0000000000000010 =0x0002U

0000000000000100 =0x0004U

0000000000001000 =0x0008U

0000000000010000 =0x0010U

发现二进制这边的区别没有,用每一位来表示一种不同的状态,1表示是,0表示不是。

这样的好处有两点:

1.可以多种标签同时存在比如 0x07 = 0b00000111,对应以上就是任务有三个标签(初始,就绪,和运行),进程和线程在运行期间是允许多种标签同时存在的。

2.节省了空间,一个变量就搞定了,如果是应用程序员要实现这三个标签同时存在,习惯上要定义三个变量的,因为你的排他性颗粒度是一个变量而不是一个位。

而对位的管理/运算就需要有个专门的管理器:位图管理器 (见源码 los_bitmap.c )

什么是位图管理器?

直接上部分代码,代码关键地方都加了中文注释,简单说就是对位的各种操作,比如如何在某个位上设1?如何找到最高位为1的是哪个位置?这些函数都是有大用途的。

//对状态字的某一标志位进行置1操作
VOID LOS_BitmapSet(UINT32 *bitmap, UINT16 pos)
{
    if (bitmap == NULL) {
        return;
    }

    *bitmap |= 1U << (pos & OS_BITMAP_MASK);//在对应位上置1
}
//对状态字的某一标志位进行清0操作
VOID LOS_BitmapClr(UINT32 *bitmap, UINT16 pos)
{
    if (bitmap == NULL) {
        return;
    }

    *bitmap &= ~(1U << (pos & OS_BITMAP_MASK));//在对应位上置0
}
/********************************************************
杂项算术指令
CLZ 用于计算操作数最高端0的个数,这条指令主要用于一下两个场合
  计算操作数规范化(使其最高位为1)时需要左移的位数
  确定一个优先级掩码中最高优先级
********************************************************/
//获取状态字中为1的最高位 例如: 00110110 返回 5
UINT16 LOS_HighBitGet(UINT32 bitmap)
{
    if (bitmap == 0) {
        return LOS_INVALID_BIT_INDEX;
    }

    return (OS_BITMAP_MASK - CLZ(bitmap));
}
//获取状态字中为1的最低位, 例如: 00110110 返回 2
UINT16 LOS_LowBitGet(UINT32 bitmap)
{
    if (bitmap == 0) {
        return LOS_INVALID_BIT_INDEX;
    }

    return CTZ(bitmap);//
}
位图在哪些地方应用?

内核很多模块在使用位图,这里只说进程和线程模块,还记得开始的问题吗,为何进程和线程都是32个优先级?因为他们的优先级是由位图管理的,管理一个UINT32的变量,所以是32级,一个位一个级别,最高位优先级最低。

    UINT32          priBitMap;          /**< BitMap for recording the change of task priority, //任务在执行过程中优先级会经常变化,这个变量用来记录所有曾经变化
                                             the priority can not be greater than 31 */   //过的优先级,例如 ..01001011 曾经有过 0,1,3,6 优先级

这是任务控制块中对调度优先级位图的定义,注意一个任务的优先级在运行过程中可不是一成不变的,内核会根据运行情况而改变它的,这个变量是用来保存这个任务曾经有过的所有优先级历史记录。

比如 任务A的优先级位图是 00000001001011 ,可以看出它曾经有过四个调度等级记录,那如果想知道优先级最低的记录是多少时怎么办呢?

诶,上面的位图管理器函数UINT16 LOS_HighBitGet(UINT32 bitmap)就很有用啦 ,它返回的是1在高位出现的位置,可以数一下是 6

因为任务的优先级0最大,所以最终的意思就是A任务曾经有过的最低优先级是6

一定要理解位图的操作,内核中大量存在这类代码,尤其到了汇编层,对寄存器的操作大量的出现。

比如以下这段汇编代码。

    MSR     CPSR_c, #(CPSR_INT_DISABLE | CPSR_SVC_MODE)  @禁止中断并切到管理模式
    LDRH    R1, [R0, #4]  @将存储器地址为R0+4 的低16位数据读入寄存器R1,并将R1的高16 位清零
    ORR     R1, #OS_TASK_STATUS_RUNNING @或指令 R1=R1|OS_TASK_STATUS_RUNNING
    STRH    R1, [R0, #4]  @将寄存器R1中的低16位写入以R0+4为地址的存储器中

编程实例

对数据实现位操作,本实例实现如下功能:

某一标志位置1。

获取标志位为1的最高bit位。

某一标志位清0。

获取标志位为1的最低bit位。

#include "los_bitmap.h"
#include "los_printf.h"

static UINT32 Bit_Sample(VOID)
{
  UINT32 flag = 0x10101010;
  UINT16 pos;

  dprintf("\nBitmap Sample!\n");
  dprintf("The flag is 0x%8x\n", flag);

  pos = 8;
  LOS_BitmapSet(&flag, pos);
  dprintf("LOS_BitmapSet:\t pos : %d, the flag is 0x%0+8x\n", pos, flag);

  pos = LOS_HighBitGet(flag);
  dprintf("LOS_HighBitGet:\t The highest one bit is %d, the flag is 0x%0+8x\n", pos, flag);

  LOS_BitmapClr(&flag, pos);
  dprintf("LOS_BitmapClr:\t pos : %d, the flag is 0x%0+8x\n", pos, flag);

  pos = LOS_LowBitGet(flag);
  dprintf("LOS_LowBitGet:\t The lowest one bit is %d, the flag is 0x%0+8x\n\n", pos, flag);

  return LOS_OK;
}

结果验证

Bitmap Sample!
The flag is 0x10101010
LOS_BitmapSet: pos : 8,  the flag is 0x10101110
LOS_HighBitGet:The highest one bit is 28, the flag is 0x10101110
LOS_BitmapClr: pos : 28, the flag is 0x00101110
LOS_LowBitGet: The lowest one bit is 4, the flag is 0x00101110

编辑:hfy

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10446

    浏览量

    206575
  • 鸿蒙系统
    +关注

    关注

    183

    文章

    2625

    浏览量

    65274
收藏 人收藏

    评论

    相关推荐

    基于优先级抢占系统的QNX调度算法

    调度算法,是基于优先级的。QNX的线程优先级,是一个0-255的数字,数字越大优先级越高。所以,优先级
    发表于 10-31 09:17 561次阅读

    用户线程内核线程

    线程的创建、撤消和调度不需要OS内核的支持,是在语言(如Java)这一处理的;而内核支持
    发表于 01-10 15:01

    嵌入式实时系统中的优先级反转是什么?

    的可抢占式调度策略。系统为每一个任务分配一个优先权,调度程序保证当前运行的进程优先权最高的进程
    发表于 09-17 07:16

    任务优先级问题

    优先级的任务可以通过时间片轮转调度来实现任务切换。在不同优先级的任务中,如果高优先级的任务没有延时,没有等待信号量等使用任务调度
    发表于 04-02 04:35

    关于中断优先级线程优先级的疑问如何解答

    最近在学习FreeRTOS,例程中涉及到中断优先级线程优先级的概念,有几点不太明白想请教下大家。1.中断优先级线程
    发表于 06-18 22:41

    【HarmonyOS】鸿蒙内核源码分析(调度机制篇)

    有以下两种:有更高优先级进程创建或者恢复后,会发生进程调度,此刻就绪列表中最高优先级进程变为运
    发表于 10-14 14:00

    鸿蒙内核源码分析(调度机制篇):Task是如何被调度执行的

    执行. 不允许任何中断发生, 没错,说的是任何事是不能去打断它,否则后果太严重了,这可是内核在切换进程线程的操作啊。在就绪队列里找个最高优先级的task切换
    发表于 11-23 10:53

    鸿蒙内核源码分析(调度队列篇):进程和Task的就绪队列对调度的作用

    进程下的线程优先级可以不一样吗?请先想一下这个问题。进程线程是一对多的父子关系,内核
    发表于 11-23 11:09

    鸿蒙内核源码分析(Task管理篇):task是内核调度的单元

    独立运行、独立调度,当前进程线程调度不受其它进程线程的影响。
    发表于 11-23 14:01

    鸿蒙内核源码分析(Task管理篇):task是内核调度的单元

    轮转调度和FIFO调度方式。鸿蒙内核线程一共有32个优先级(0-31),最高
    发表于 11-24 10:24

    鸿蒙内核源码分析(进程管理篇):进程内核的资源管理单元

    优先级进程阻塞或结束后才能得到调度。每一个用户态进程均拥有自己独立的进程空间,相互之间不可见,实现进程
    发表于 11-24 11:23

    RT-Thread基于优先级的全抢占式调度算法的实现

    一、原理概述RT-Thread 是一款嵌入式实时操作系统(RTOS),同时也是一款优秀的物联网操作系统,相对于裸机的轮询调度算法,它使用的线程(任务)调度算法是基于优先级的全抢占式多
    发表于 04-20 14:17

    rt-thread高优先级线程可以调度执行吗?

    请教下,在rt-thread中,如果低优先级线程中用while(1){}直接死循环,是不是高优先级线程也无法调度执行了?如果高
    发表于 05-13 10:51

    RT-Thread线程优先级链表与位图算法的介绍

    1 线程优先级链表每个线程控制块都带有一个链表成员,根据优先级将thread->slist插入对相应优先级链表中,对于相同
    发表于 05-13 15:38

    鸿蒙内核源码分析:task是内核调度的单元

    进程线程的影响。 鸿蒙内核中的线程采用抢占式调度机制,同时支持时间片轮转
    发表于 11-23 15:51 22次下载
    <b class='flag-5'>鸿蒙</b><b class='flag-5'>内核</b><b class='flag-5'>源码</b>分析:task是<b class='flag-5'>内核</b><b class='flag-5'>调度</b>的单元