新调度算法(HPF)上线,需重新注解任务调度模块

    百图画鸿蒙 + 百文说内核 + 百万注源码  => 挖透鸿蒙内核源码
    鸿蒙研究站 | http://weharmonyos.com (国内)
              | https://weharmony.github.io (国外)
    oschina | https://my.oschina.net/weharmony
    博客园 | https://www.cnblogs.com/weharmony/
    知乎 | https://www.zhihu.com/people/weharmonyos
    csdn | https://blog.csdn.net/kuangyufei
    51cto | https://harmonyos.51cto.com/column/34
    掘金 | https://juejin.cn/user/756888642000808
    公众号 | 鸿蒙研究站 (weharmonyos)
上级 55d0ace0
......@@ -7,8 +7,9 @@
* 码农的学职生涯,都应精读一遍内核源码。以浇筑好计算机知识大厦的地基,地基纵深的坚固程度,很大程度能决定未来大厦能盖多高。那为何一定要精读细品呢?
* 因为内核代码本身并不太多,都是浓缩的精华,精读是让各个知识点高频出现,不孤立成点状记忆,没有足够连接点的知识点是很容易忘的,点点成线,线面成体,连接越多,记得越牢,如此短时间内容易结成一张高浓度,高密度的系统化知识网,训练大脑肌肉记忆,驻入大脑直觉区,想抹都抹不掉,终生携带,随时调取。跟骑单车一样,一旦学会,即便多年不骑,照样跨上就走,游刃有余。
### 热爱是所有的理由和答案
* 因大学时阅读 `linux 2.6` 内核痛并快乐的经历,一直有个心愿,如何让更多对内核感兴趣的朋友减少阅读时间,加速对计算机系统级的理解,而不至于过早的放弃。但因过程种种,多年一直没有行动,基本要放弃这件事了。恰逢 `2020/9/10` 鸿蒙正式开源,重新激活了多年的心愿,就有那么点如黄河之水一发不可收拾了。
* 目前对内核源码的注解完成 `80%` ,博客分析完成`80+篇`,百图画鸿蒙完成`20张`,空闲时间几乎被占用,每天很充实,时间不够用,连做梦内核代码都在鱼贯而入。加注并整理是件很有挑战的事,时间单位上以年计,已持续一年半,期间得到众多小伙伴的支持与纠错,让此念越发强烈,坚如磐石。:P
* 因大学时阅读 linux 2.6 内核痛并快乐的经历,一直有个心愿,对底层基础技术进行一次系统性的整理,方便自己随时翻看,同时让更多对底层感兴趣的小伙伴减少时间,加速对计算机系统级的理解,而不至于过早的放弃。但因过程种种,多年一直没有行动,基本要放弃这件事了。恰逢 2020/9/10 鸿蒙正式开源,重新激活了多年的心愿,就有那么点如黄河之水一发不可收拾了。
* 包含三部分内容:**注源****写博****画图**, 目前对内核源码的注解完成 80% ,博客分析完成80+篇,百图画鸿蒙完成20张,空闲时间几乎被占用,时间不够用,但每天都很充实,连做梦鸿蒙系统都在鱼贯而入。是件很有挑战的事,时间单位以年计,已持续一年半,期间得到众多小伙伴的支持与纠错,在此谢过 ! :P
### (〃・ิ‿・ิ)ゞ鸿蒙内核开发者
* 感谢开放原子开源基金会,致敬鸿蒙内核开发者提供了如此优秀的源码,一了多年的夙愿,津津乐道于此。从内核一行行的代码中能深深感受到开发者各中艰辛与坚持,及鸿蒙生态对未来的价值,这些是张嘴就来的网络喷子们永远不能体会到的。可以毫不夸张的说鸿蒙内核源码可作为大学:C语言,数据结构,操作系统,汇编语言,计算机系统结构,计算机组成原理,微机接口 七门课程的教学项目。如此宝库,不深入研究实在是暴殄天物,于心不忍,坚信鸿蒙大势所趋,未来可期,其必定成功,也必然成功,誓做其坚定的追随者和传播者。
......@@ -224,8 +225,47 @@
* [标准库 | musl](https://gitee.com/weharmony/third_party_musl)
### 关于 zzz 目录
中文加注版比官方版无新增文件,只多了一个`zzz`的目录,里面放了一些加注所需文件,它与内核代码无关,可以忽略它,取名`zzz`是为了排在最后,减少对原有代码目录级的侵入,`zzz` 的想法源于微信中名称为`AAA`的那帮朋友,你的微信里应该也有他们熟悉的身影吧 :|P
* ![](https://gitee.com/weharmonyos/resources/raw/master/13/cate.png)
中文加注版比官方版无新增文件,只多了一个`zzz`的目录,里面放了一些加注所需文件,它与内核代码无关,可以忽略它,取名`zzz`是为了排在最后,减少对原有代码目录级的侵入,`zzz` 的想法源于微信中名称为`AAA`的那帮朋友,你的微信里应该也有他们熟悉的身影吧 :-)
```
/kernel/liteos_a
├── apps # 用户态的init和shell应用程序
├── arch # 体系架构的目录,如arm等
│ └── arm # arm架构代码
├── bsd # freebsd相关的驱动和适配层模块代码引入,例如USB等
├── compat # 内核接口兼容性目录
│ └── posix # posix相关接口
├── drivers # 内核驱动
│ └── char # 字符设备
│ ├── mem # 访问物理IO设备驱动
│ ├── quickstart # 系统快速启动接口目录
│ ├── random # 随机数设备驱动
│ └── video # framebuffer驱动框架
├── fs # 文件系统模块,主要来源于NuttX开源项目
│ ├── fat # fat文件系统
│ ├── jffs2 # jffs2文件系统
│ ├── include # 对外暴露头文件存放目录
│ ├── nfs # nfs文件系统
│ ├── proc # proc文件系统
│ ├── ramfs # ramfs文件系统
│ └── vfs # vfs层
├── kernel # 进程、内存、IPC等模块
│ ├── base # 基础内核,包括调度、内存等模块
│ ├── common # 内核通用组件
│ ├── extended # 扩展内核,包括动态加载、vdso、liteipc等模块
│ ├── include # 对外暴露头文件存放目录
│ └── user # 加载init进程
├── lib # 内核的lib库
├── net # 网络模块,主要来源于lwip开源项目
├── platform # 支持不同的芯片平台代码,如Hi3516DV300等
│ ├── hw # 时钟与中断相关逻辑代码
│ ├── include # 对外暴露头文件存放目录
│ └── uart # 串口相关逻辑代码
├── security # 安全特性相关的代码,包括进程权限管理和虚拟id映射管理
├── syscall # 系统调用
├── testsuites # 单元测试用例
├── tools # 构建工具及相关配置和代码
└── zzz # 中文注解版新增目录
```
### 官方文档 | 静态站点呈现
* 研究鸿蒙需不断的翻阅资料,吸取别人的精华,其中官方文档必不可少, 为更好的呈现 **OpenHarmony开发者文档** , 特意做了静态站点 [ >> 鸿蒙研究站 | 官方文档](http://weharmonyos.com/openharmony) 来方便搜索,阅读官方资料。
......@@ -234,7 +274,7 @@
![](https://gitee.com/weharmonyos/resources/raw/master/52/4.png)
* [鸿蒙研究站](http://weharmonyos.com) 定位于做一个专注而靠谱的技术站, 没有广告,干净简洁,对鸿蒙研究会持续在上面输出。同时感谢资助鸿蒙研究和网站建设的小伙伴,很温暖。 [ >> 送温暖记录](http://weharmonyos.com/donate.html)
* [鸿蒙研究站](http://weharmonyos.com) 定位于做一个专注而靠谱的技术站,没有广告,干净简洁,极佳阅读体验,持续输出,周周更新。同时感谢资助网站建设的各位小伙伴。 [ >> 我要捐助](http://weharmonyos.com/donate.html)
......
......@@ -86,7 +86,12 @@
#define WRITE_TIMER_REG64(reg, val) ARM_SYSREG64_WRITE(reg, val)
#endif
/*
* 见于 << arm 架构参考手册>> B4.1.21 处 CNTFRQ寄存器表示系统计数器的时钟频率。
* 这个寄存器是一个通用的计时器寄存器。
* MRC p15, 0, <Rt>, c14, c0, 0 ; Read CNTFRQ into Rt
* MCR p15, 0, <Rt>, c14, c0, 0 ; Write Rt to CNTFRQ
*/
UINT32 HalClockFreqRead(VOID)
{
return READ_TIMER_REG32(TIMER_REG_CNTFRQ);
......@@ -124,13 +129,13 @@ UINT64 HalClockGetCycles(VOID)
cntpct = READ_TIMER_REG64(TIMER_REG_CT);
return cntpct;
}
//硬时钟初始化
LITE_OS_SEC_TEXT_INIT VOID HalClockInit(VOID)
{
UINT32 ret;
g_sysClock = HalClockFreqRead();
ret = LOS_HwiCreate(OS_TICK_INT_NUM, MIN_INTERRUPT_PRIORITY, 0, OsTickHandler, 0);
g_sysClock = HalClockFreqRead();//读取CPU的时钟频率
ret = LOS_HwiCreate(OS_TICK_INT_NUM, MIN_INTERRUPT_PRIORITY, 0, OsTickHandler, 0);//创建硬中断定时器
if (ret != LOS_OK) {
PRINT_ERR("%s, %d create tick irq failed, ret:0x%x\n", __FUNCTION__, __LINE__, ret);
}
......
......@@ -76,23 +76,23 @@ STATIC INLINE UINT64 OsGetCurrSchedTimeCycle(VOID)
}
typedef enum {
INT_NO_RESCH = 0x0, /* no needs to schedule */
INT_PEND_RESCH = 0x1, /* pending schedule flag */
INT_PEND_TICK = 0x2, /* pending tick */
INT_NO_RESCH = 0x0, /* no needs to schedule | 无需调度*/
INT_PEND_RESCH = 0x1, /* pending schedule flag | 因阻塞而引起的调度*/
INT_PEND_TICK = 0x2, /* pending tick | 因Tick而引起的调度*/
} SchedFlag;
#define OS_PRIORITY_QUEUE_NUM 32
#define OS_PRIORITY_QUEUE_NUM 32 //队列优先级
typedef struct {
LOS_DL_LIST priQueList[OS_PRIORITY_QUEUE_NUM];
UINT32 readyTasks[OS_PRIORITY_QUEUE_NUM];
UINT32 queueBitmap;
LOS_DL_LIST priQueList[OS_PRIORITY_QUEUE_NUM]; //任务
UINT32 readyTasks[OS_PRIORITY_QUEUE_NUM]; //已就绪任务
UINT32 queueBitmap; //位图
} HPFQueue;
typedef struct {
HPFQueue queueList[OS_PRIORITY_QUEUE_NUM];
HPFQueue queueList[OS_PRIORITY_QUEUE_NUM]; //
UINT32 queueBitmap;
} HPFRunqueue;
//调度运行队列
typedef struct {
SortLinkAttribute timeoutQueue; /* task timeout queue */
HPFRunqueue *hpfRunqueue;
......@@ -103,10 +103,10 @@ typedef struct {
UINT32 schedFlag; /* pending scheduler flag */
} SchedRunqueue;
extern SchedRunqueue g_schedRunqueue[LOSCFG_KERNEL_CORE_NUM];
extern SchedRunqueue g_schedRunqueue[LOSCFG_KERNEL_CORE_NUM];//每个CPU核都有一个属于自己的调度队列
VOID OsSchedExpireTimeUpdate(VOID);
//获取当前CPU
STATIC INLINE SchedRunqueue *OsSchedRunqueue(VOID)
{
return &g_schedRunqueue[ArchCurrCpuid()];
......@@ -220,37 +220,39 @@ typedef struct {
} SchedParam;
typedef struct {
UINT16 policy; /* This field must be present for all scheduling policies and must be the first in the structure */
UINT16 basePrio;
UINT16 priority;
UINT32 initTimeSlice;
UINT32 priBitmap; /**< Bitmap for recording the change of task priority, the priority can not be greater than 31 */
UINT16 policy; /* This field must be present for all scheduling policies and must be the first in the structure
| 所有调度策略都必须存在此字段,并且必须是结构中的第一个字段*/
UINT16 basePrio; ///< 起始优先级
UINT16 priority; ///< 当前优先级
UINT32 initTimeSlice;///< 初始化时间片
UINT32 priBitmap; /**< Bitmap for recording the change of task priority, the priority can not be greater than 31
| 记录任务优先级变化的位图,优先级不能大于31 */
} SchedHPF;
typedef struct {
typedef struct { //调度策略
union {
SchedHPF hpf;
SchedHPF hpf; // 目前只支持 优先级策略(Highest-Priority-First,HPF)
} Policy;
} SchedPolicy;
typedef struct {
VOID (*dequeue)(SchedRunqueue *rq, LosTaskCB *taskCB);
VOID (*enqueue)(SchedRunqueue *rq, LosTaskCB *taskCB);
VOID (*start)(SchedRunqueue *rq, LosTaskCB *taskCB);
VOID (*exit)(LosTaskCB *taskCB);
UINT32 (*wait)(LosTaskCB *runTask, LOS_DL_LIST *list, UINT32 timeout);
VOID (*wake)(LosTaskCB *taskCB);
BOOL (*schedParamModify)(LosTaskCB *taskCB, const SchedParam *param);
UINT32 (*schedParamGet)(const LosTaskCB *taskCB, SchedParam *param);
UINT32 (*delay)(LosTaskCB *taskCB, UINT64 waitTime);
VOID (*yield)(LosTaskCB *taskCB);
UINT32 (*suspend)(LosTaskCB *taskCB);
UINT32 (*resume)(LosTaskCB *taskCB, BOOL *needSched);
UINT64 (*deadlineGet)(const LosTaskCB *taskCB);
VOID (*timeSliceUpdate)(SchedRunqueue *rq, LosTaskCB *taskCB, UINT64 currTime);
INT32 (*schedParamCompare)(const SchedPolicy *sp1, const SchedPolicy *sp2);
VOID (*priorityInheritance)(LosTaskCB *owner, const SchedParam *param);
VOID (*priorityRestore)(LosTaskCB *owner, const LOS_DL_LIST *list, const SchedParam *param);
typedef struct {//调度接口函数
VOID (*dequeue)(SchedRunqueue *rq, LosTaskCB *taskCB); ///< 出队列
VOID (*enqueue)(SchedRunqueue *rq, LosTaskCB *taskCB); ///< 入队列
VOID (*start)(SchedRunqueue *rq, LosTaskCB *taskCB); ///< 开始执行任务
VOID (*exit)(LosTaskCB *taskCB); ///< 任务退出
UINT32 (*wait)(LosTaskCB *runTask, LOS_DL_LIST *list, UINT32 timeout); ///< 任务等待
VOID (*wake)(LosTaskCB *taskCB);///< 任务唤醒
BOOL (*schedParamModify)(LosTaskCB *taskCB, const SchedParam *param);///< 修改调度参数
UINT32 (*schedParamGet)(const LosTaskCB *taskCB, SchedParam *param);///< 获取调度参数
UINT32 (*delay)(LosTaskCB *taskCB, UINT64 waitTime);///< 延时执行
VOID (*yield)(LosTaskCB *taskCB);///< 让出控制权
UINT32 (*suspend)(LosTaskCB *taskCB);///< 挂起任务
UINT32 (*resume)(LosTaskCB *taskCB, BOOL *needSched);///< 恢复任务
UINT64 (*deadlineGet)(const LosTaskCB *taskCB);///< 获取最后期限
VOID (*timeSliceUpdate)(SchedRunqueue *rq, LosTaskCB *taskCB, UINT64 currTime);///< 更新时间片
INT32 (*schedParamCompare)(const SchedPolicy *sp1, const SchedPolicy *sp2); ///< 比较调度参数
VOID (*priorityInheritance)(LosTaskCB *owner, const SchedParam *param);//继承调度参数
VOID (*priorityRestore)(LosTaskCB *owner, const LOS_DL_LIST *list, const SchedParam *param);///< 恢复调度参数
} SchedOps;
/**
......@@ -605,7 +607,7 @@ STATIC INLINE VOID SchedTaskUnfreeze(LosTaskCB *taskCB)
#define OS_SCHEDULER_CLR(cpuid) do { \
g_taskScheduled &= ~(1U << (cpuid)); \
} while (0);
//获取最高优先级任务
STATIC INLINE LosTaskCB *HPFRunqueueTopTaskGet(HPFRunqueue *rq)
{
LosTaskCB *newTask = NULL;
......
......@@ -44,7 +44,7 @@ STATIC VOID IdleTimeSliceUpdate(SchedRunqueue *rq, LosTaskCB *taskCB, UINT64 cur
STATIC INT32 IdleParamCompare(const SchedPolicy *sp1, const SchedPolicy *sp2);
STATIC VOID IdlePriorityInheritance(LosTaskCB *owner, const SchedParam *param);
STATIC VOID IdlePriorityRestore(LosTaskCB *owner, const LOS_DL_LIST *list, const SchedParam *param);
//空闲调度
const STATIC SchedOps g_idleOps = {
.dequeue = IdleDequeue,
.enqueue = IdleEnqueue,
......
......@@ -43,6 +43,8 @@
#define OS_SCHED_READY_MAX 30
#define OS_TIME_SLICE_MIN (INT32)((50 * OS_SYS_NS_PER_US) / OS_NS_PER_CYCLE) /* 50us */
//基于优先数调度算法 Highest-Priority-First(HPF)
STATIC HPFRunqueue g_schedHPF;
STATIC VOID HPFDequeue(SchedRunqueue *rq, LosTaskCB *taskCB);
......@@ -526,7 +528,7 @@ VOID HPFProcessDefaultSchedParamGet(SchedParam *param)
{
param->basePrio = OS_USER_PROCESS_PRIORITY_HIGHEST;
}
//HPF 调度策略初始化
VOID HPFSchedPolicyInit(SchedRunqueue *rq)
{
if (ArchCurrCpuid() > 0) {
......
......@@ -108,11 +108,11 @@ STATIC INLINE VOID SchedTimeoutTaskWake(SchedRunqueue *rq, UINT64 currTime, LosT
LOS_SpinLock(&g_taskSpin);
UINT16 tempStatus = taskCB->taskStatus;
if (tempStatus & (OS_TASK_STATUS_PENDING | OS_TASK_STATUS_DELAY)) {
if (tempStatus & (OS_TASK_STATUS_PENDING | OS_TASK_STATUS_DELAY)) {
taskCB->taskStatus &= ~(OS_TASK_STATUS_PENDING | OS_TASK_STATUS_PEND_TIME | OS_TASK_STATUS_DELAY);
if (tempStatus & OS_TASK_STATUS_PENDING) {
taskCB->taskStatus |= OS_TASK_STATUS_TIMEOUT;
LOS_ListDelete(&taskCB->pendList);
taskCB->taskStatus |= OS_TASK_STATUS_TIMEOUT;
LOS_ListDelete(&taskCB->pendList);
taskCB->taskMux = NULL;
OsTaskWakeClearPendMask(taskCB);
}
......@@ -152,7 +152,7 @@ STATIC INLINE BOOL SchedTimeoutQueueScan(SchedRunqueue *rq)
SortLinkList *sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
UINT64 currTime = OsGetCurrSchedTimeCycle();
while (sortList->responseTime <= currTime) {
while (sortList->responseTime <= currTime) {
LosTaskCB *taskCB = LOS_DL_LIST_ENTRY(sortList, LosTaskCB, sortList);
OsDeleteNodeSortLink(timeoutQueue, &taskCB->sortList);
LOS_SpinUnlock(&timeoutQueue->spinLock);
......@@ -160,16 +160,16 @@ STATIC INLINE BOOL SchedTimeoutQueueScan(SchedRunqueue *rq)
SchedTimeoutTaskWake(rq, currTime, taskCB, &needSched);
LOS_SpinLock(&timeoutQueue->spinLock);
if (LOS_ListEmpty(listObject)) {
break;
if (LOS_ListEmpty(listObject)) {
break;
}
sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
}
LOS_SpinUnlock(&timeoutQueue->spinLock);
return needSched;
return needSched;
}
VOID OsSchedTick(VOID)
......@@ -293,7 +293,7 @@ STATIC LosTaskCB *TopTaskGet(SchedRunqueue *rq)
VOID OsSchedStart(VOID)
{
UINT32 cpuid = ArchCurrCpuid();
UINT32 cpuid = ArchCurrCpuid();
UINT32 intSave;
PRINTK("cpu %d entering scheduler\n", cpuid);
......@@ -306,12 +306,12 @@ VOID OsSchedStart(VOID)
LosTaskCB *newTask = TopTaskGet(rq);
newTask->taskStatus |= OS_TASK_STATUS_RUNNING;
#ifdef LOSCFG_KERNEL_SMP
#ifdef LOSCFG_KERNEL_SMP
/*
* attention: current cpu needs to be set, in case first task deletion
* may fail because this flag mismatch with the real current cpu.
*/
newTask->currCpu = cpuid;
newTask->currCpu = cpuid;
#endif
OsCurrTaskSet((VOID *)newTask);
......@@ -367,13 +367,13 @@ STATIC VOID SchedTaskSwitch(SchedRunqueue *rq, LosTaskCB *runTask, LosTaskCB *ne
{
SchedSwitchCheck(runTask, newTask);
runTask->taskStatus &= ~OS_TASK_STATUS_RUNNING;
runTask->taskStatus &= ~OS_TASK_STATUS_RUNNING;
newTask->taskStatus |= OS_TASK_STATUS_RUNNING;
#ifdef LOSCFG_KERNEL_SMP
/* mask new running task's owner processor */
runTask->currCpu = OS_TASK_INVALID_CPUID;
newTask->currCpu = ArchCurrCpuid();
runTask->currCpu = OS_TASK_INVALID_CPUID;
newTask->currCpu = ArchCurrCpuid();
#endif
OsCurrTaskSet((VOID *)newTask);
......@@ -390,7 +390,7 @@ STATIC VOID SchedTaskSwitch(SchedRunqueue *rq, LosTaskCB *runTask, LosTaskCB *ne
#ifdef LOSCFG_SCHED_DEBUG
UINT64 waitStartTime = newTask->startTime;
#endif
if (runTask->taskStatus & OS_TASK_STATUS_READY) {
if (runTask->taskStatus & OS_TASK_STATUS_READY) {
/* When a thread enters the ready queue, its slice of time is updated */
newTask->startTime = runTask->startTime;
} else {
......@@ -498,7 +498,7 @@ VOID LOS_Schedule(VOID)
SCHEDULER_UNLOCK(intSave);
}
STATIC INLINE LOS_DL_LIST *SchedLockPendFindPosSub(const LosTaskCB *runTask, const LOS_DL_LIST *lockList)
{
LosTaskCB *pendedTask = NULL;
......@@ -507,28 +507,28 @@ STATIC INLINE LOS_DL_LIST *SchedLockPendFindPosSub(const LosTaskCB *runTask, con
INT32 ret = OsSchedParamCompare(pendedTask, runTask);
if (ret < 0) {
continue;
} else if (ret > 0) {
} else if (ret > 0) {
return &pendedTask->pendList;
} else {
} else {
return pendedTask->pendList.pstNext;
}
}
return NULL;
}
LOS_DL_LIST *OsSchedLockPendFindPos(const LosTaskCB *runTask, LOS_DL_LIST *lockList)
{
if (LOS_ListEmpty(lockList)) {
return lockList;
}
LosTaskCB *pendedTask1 = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(lockList));
LosTaskCB *pendedTask1 = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(lockList));
INT32 ret = OsSchedParamCompare(pendedTask1, runTask);
if (ret > 0) {
return lockList->pstNext;
}
LosTaskCB *pendedTask2 = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_LAST(lockList));
LosTaskCB *pendedTask2 = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_LAST(lockList));
ret = OsSchedParamCompare(pendedTask2, runTask);
if (ret <= 0) {
return lockList;
......
......@@ -5,148 +5,148 @@
@verbatim
直接内存访问
直接内存访问(Direct Memory Access,DMA)是计算机科学中的一种内存访问技术。它允许某些电脑内部的
硬件子系统(电脑外设),可以独立地直接读写系统内存,而不需中央处理器(CPU)介入处理
在同等程度的处理器负担下,DMA是一种快速的数据传送方式。很多硬件的系统会使用DMA,包含硬盘控制器、
绘图显卡、网卡和声卡。
DMA是所有现代电脑的重要特色,它允许不同速度的硬件设备来沟通,而不需要依于中央处理器的大量中断负载。
否则,中央处理器需要从来源把每一片段的资料复制到寄存器,然后把它们再次写回到新的地方。在这个时间中,
中央处理器对于其他的工作来说就无法使用。
DMA传输常使用在将一个内存区从一个设备复制到另外一个。当中央处理器初始化这个传输动作,传输动作本身是
由DMA控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存去。像是这样的操作
并没有让处理器工作拖延,使其可以被重新调度去处理其他的工作。DMA传输对于高性能嵌入式系统算法和网络是
很重要的。 举个例子,个人电脑的ISA DMA控制器拥有8个DMA通道,其中的7个通道是可以让计算机的中央处理器所利用。
每一个DMA通道有一个16位地址寄存器和一个16位计数寄存器。要初始化资料传输时,设备驱动程序一起设置DMA通道的
地址和计数寄存器,以及资料传输的方向,读取或写入。然后指示DMA硬件开始这个传输动作。当传输结束的时候,
设备就会以中断的方式通知中央处理器。
"分散-收集"(Scatter-gather)DMA允许在一次单一的DMA处理中传输资料到多个内存区域。相当于把多个简单的DMA要求
串在一起。同样,这样做的目的是要减轻中央处理器的多次输出输入中断和资料复制任务。
DRQ意为DMA要求;DACK意为DMA确认。这些符号一般在有DMA功能的电脑系统硬件概要上可以看到。
它们表示了介于中央处理器和DMA控制器之间的电子信号传输线路。
硬件子系统(电脑外设),可以独立地直接读写系统内存,而不需中央处理器(CPU)介入处理
在同等程度的处理器负担下,DMA是一种快速的数据传送方式。很多硬件的系统会使用DMA,包含硬盘控制器、
绘图显卡、网卡和声卡。
DMA是所有现代电脑的重要特色,它允许不同速度的硬件设备来沟通,而不需要依于中央处理器的大量中断负载。
否则,中央处理器需要从来源把每一片段的资料复制到寄存器,然后把它们再次写回到新的地方。在这个时间中,
中央处理器对于其他的工作来说就无法使用。
DMA传输常使用在将一个内存区从一个设备复制到另外一个。当中央处理器初始化这个传输动作,传输动作本身是
由DMA控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存去。像是这样的操作
并没有让处理器工作拖延,使其可以被重新调度去处理其他的工作。DMA传输对于高性能嵌入式系统算法和网络是
很重要的。 举个例子,个人电脑的ISA DMA控制器拥有8个DMA通道,其中的7个通道是可以让计算机的中央处理器所利用。
每一个DMA通道有一个16位地址寄存器和一个16位计数寄存器。要初始化资料传输时,设备驱动程序一起设置DMA通道的
地址和计数寄存器,以及资料传输的方向,读取或写入。然后指示DMA硬件开始这个传输动作。当传输结束的时候,
设备就会以中断的方式通知中央处理器。
"分散-收集"(Scatter-gather)DMA允许在一次单一的DMA处理中传输资料到多个内存区域。相当于把多个简单的DMA要求
串在一起。同样,这样做的目的是要减轻中央处理器的多次输出输入中断和资料复制任务。
DRQ意为DMA要求;DACK意为DMA确认。这些符号一般在有DMA功能的电脑系统硬件概要上可以看到。
它们表示了介于中央处理器和DMA控制器之间的电子信号传输线路。
缓存一致性问题
DMA会导致缓存一致性问题。想像中央处理器带有缓存与外部内存的情况,DMA的运作则是去访问外部内存,
当中央处理器访问外部内存某个地址的时候,暂时先将新的值写入缓存中,但并未将外部内存的资料更新,
若在缓存中的资料尚未更新到外部内存前发生了DMA,则DMA过程将会读取到未更新的资料。
相同的,如果外部设备写入新的值到外部内存内,则中央处理器若访问缓存时则会访问到尚未更新的资料。
这些问题可以用两种方法来解决:
缓存同调系统(Cache-coherent system):以硬件方法来完成,当外部设备写入内存时以一个信号来通知
缓存控制器某内存地址的值已经过期或是应该更新资料。
非同调系统(Non-coherent system):以软件方法来完成,操作系统必须确认缓存读取时,DMA程序已经
开始或是禁止DMA发生。
第二种的方法会造成DMA的系统负担。
DMA会导致缓存一致性问题。想像中央处理器带有缓存与外部内存的情况,DMA的运作则是去访问外部内存,
当中央处理器访问外部内存某个地址的时候,暂时先将新的值写入缓存中,但并未将外部内存的资料更新,
若在缓存中的资料尚未更新到外部内存前发生了DMA,则DMA过程将会读取到未更新的资料。
相同的,如果外部设备写入新的值到外部内存内,则中央处理器若访问缓存时则会访问到尚未更新的资料。
这些问题可以用两种方法来解决:
缓存同调系统(Cache-coherent system):以硬件方法来完成,当外部设备写入内存时以一个信号来通知
缓存控制器某内存地址的值已经过期或是应该更新资料。
非同调系统(Non-coherent system):以软件方法来完成,操作系统必须确认缓存读取时,DMA程序已经
开始或是禁止DMA发生。
第二种的方法会造成DMA的系统负担。
@endverbatim
* @version
* @author weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点
* @date 2022-04-02
*/
/*
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "los_vm_iomap.h"
#include "los_printf.h"
#include "los_vm_zone.h"
#include "los_vm_common.h"
#include "los_vm_map.h"
#include "los_memory.h"
/// 分配DMA空间
VOID *LOS_DmaMemAlloc(DMA_ADDR_T *dmaAddr, size_t size, size_t align, enum DmaMemType type)
{
VOID *kVaddr = NULL;
if (size == 0) {
return NULL;
}
if ((type != DMA_CACHE) && (type != DMA_NOCACHE)) {
VM_ERR("The dma type = %d is not supported!", type);
return NULL;
}
#ifdef LOSCFG_KERNEL_VM
kVaddr = LOS_KernelMallocAlign(size, align);//不走内存池方式, 直接申请物理页
#else
kVaddr = LOS_MemAllocAlign(OS_SYS_MEM_ADDR, size, align);//从内存池中申请
#endif
if (kVaddr == NULL) {
VM_ERR("failed, size = %u, align = %u", size, align);
return NULL;
}
if (dmaAddr != NULL) {
*dmaAddr = (DMA_ADDR_T)LOS_PaddrQuery(kVaddr);//查询物理地址, DMA直接将数据灌到物理地址
}
if (type == DMA_NOCACHE) {//无缓存模式 , 计算新的虚拟地址
kVaddr = (VOID *)VMM_TO_UNCACHED_ADDR((UINTPTR)kVaddr);
}
return kVaddr;
}
/// 释放 DMA指针
VOID LOS_DmaMemFree(VOID *vaddr)
{
UINTPTR addr;
if (vaddr == NULL) {
return;
}
addr = (UINTPTR)vaddr;
// 未缓存区
if ((addr >= UNCACHED_VMM_BASE) && (addr < UNCACHED_VMM_BASE + UNCACHED_VMM_SIZE)) {
addr = UNCACHED_TO_VMM_ADDR(addr); //转换成未缓存区地址
#ifdef LOSCFG_KERNEL_VM
LOS_KernelFree((VOID *)addr);//
#else
LOS_MemFree(OS_SYS_MEM_ADDR, (VOID *)addr);//内存池方式释放
#endif
} else if ((addr >= KERNEL_VMM_BASE) && (addr < KERNEL_VMM_BASE + KERNEL_VMM_SIZE)) {
#ifdef LOSCFG_KERNEL_VM
LOS_KernelFree((VOID *)addr);
#else
LOS_MemFree(OS_SYS_MEM_ADDR, (VOID *)addr);
#endif
} else {
VM_ERR("addr %#x not in dma area!!!", vaddr);
}
return;
}
/// 将DMA虚拟地址转成物理地址
DMA_ADDR_T LOS_DmaVaddrToPaddr(VOID *vaddr)
{
return (DMA_ADDR_T)LOS_PaddrQuery(vaddr);
}
/*
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "los_vm_iomap.h"
#include "los_printf.h"
#include "los_vm_zone.h"
#include "los_vm_common.h"
#include "los_vm_map.h"
#include "los_memory.h"
/// 分配DMA空间
VOID *LOS_DmaMemAlloc(DMA_ADDR_T *dmaAddr, size_t size, size_t align, enum DmaMemType type)
{
VOID *kVaddr = NULL;
if (size == 0) {
return NULL;
}
if ((type != DMA_CACHE) && (type != DMA_NOCACHE)) {
VM_ERR("The dma type = %d is not supported!", type);
return NULL;
}
#ifdef LOSCFG_KERNEL_VM
kVaddr = LOS_KernelMallocAlign(size, align);//不走内存池方式, 直接申请物理页
#else
kVaddr = LOS_MemAllocAlign(OS_SYS_MEM_ADDR, size, align);//从内存池中申请
#endif
if (kVaddr == NULL) {
VM_ERR("failed, size = %u, align = %u", size, align);
return NULL;
}
if (dmaAddr != NULL) {
*dmaAddr = (DMA_ADDR_T)LOS_PaddrQuery(kVaddr);//查询物理地址, DMA直接将数据灌到物理地址
}
if (type == DMA_NOCACHE) {//无缓存模式 , 计算新的虚拟地址
kVaddr = (VOID *)VMM_TO_UNCACHED_ADDR((UINTPTR)kVaddr);
}
return kVaddr;
}
/// 释放 DMA指针
VOID LOS_DmaMemFree(VOID *vaddr)
{
UINTPTR addr;
if (vaddr == NULL) {
return;
}
addr = (UINTPTR)vaddr;
// 未缓存区
if ((addr >= UNCACHED_VMM_BASE) && (addr < UNCACHED_VMM_BASE + UNCACHED_VMM_SIZE)) {
addr = UNCACHED_TO_VMM_ADDR(addr); //转换成未缓存区地址
#ifdef LOSCFG_KERNEL_VM
LOS_KernelFree((VOID *)addr);//
#else
LOS_MemFree(OS_SYS_MEM_ADDR, (VOID *)addr);//内存池方式释放
#endif
} else if ((addr >= KERNEL_VMM_BASE) && (addr < KERNEL_VMM_BASE + KERNEL_VMM_SIZE)) {
#ifdef LOSCFG_KERNEL_VM
LOS_KernelFree((VOID *)addr);
#else
LOS_MemFree(OS_SYS_MEM_ADDR, (VOID *)addr);
#endif
} else {
VM_ERR("addr %#x not in dma area!!!", vaddr);
}
return;
}
/// 将DMA虚拟地址转成物理地址
DMA_ADDR_T LOS_DmaVaddrToPaddr(VOID *vaddr)
{
return (DMA_ADDR_T)LOS_PaddrQuery(vaddr);
}
......@@ -77,14 +77,14 @@ LITE_OS_SEC_TEXT_INIT STATIC UINT32 EarliestInit(VOID)
return LOS_OK;
}
//硬件早期初始化
LITE_OS_SEC_TEXT_INIT STATIC UINT32 ArchEarlyInit(VOID)
{
UINT32 ret;
/* set system counter freq */
/* set system counter freq | 设置系统计数器频率*/
#ifndef LOSCFG_TEE_ENABLE
HalClockFreqWrite(OS_SYS_CLOCK);
HalClockFreqWrite(OS_SYS_CLOCK); //写寄存器 MCR p15, 0, <Rt>, c14, c0, 0 ; Write Rt to CNTFRQ
#endif
#ifdef LOSCFG_PLATFORM_HWI
......@@ -101,11 +101,11 @@ LITE_OS_SEC_TEXT_INIT STATIC UINT32 ArchEarlyInit(VOID)
return LOS_OK;
}
//平台早期初始化
LITE_OS_SEC_TEXT_INIT STATIC UINT32 PlatformEarlyInit(VOID)
{
#if defined(LOSCFG_PLATFORM_UART_WITHOUT_VFS) && defined(LOSCFG_DRIVERS)
uart_init();
uart_init(); //初始化串口
#endif /* LOSCFG_PLATFORM_UART_WITHOUT_VFS */
return LOS_OK;
......
......@@ -67,7 +67,7 @@ extern UINT32 __heap_end; ///< 堆区结束地址
* System clock (unit: HZ)
*/
#ifndef OS_SYS_CLOCK ///< HZ:是每秒中的周期性变动重复次数的计量
#define OS_SYS_CLOCK (get_bus_clk()) ///< 系统主时钟频率 例如:50000000 即50微
#define OS_SYS_CLOCK (get_bus_clk()) ///< 系统主时钟频率 例如:50000000 即20纳
#endif
/**
* @ingroup los_config
......
git add -A
git commit -m ' 同步官方代码,任务调度模块改动很大
git commit -m ' 新调度算法(HPF)上线,需重新注解任务调度模块
百图画鸿蒙 + 百文说内核 + 百万注源码 => 挖透鸿蒙内核源码
鸿蒙研究站 | http://weharmonyos.com (国内)
| https://weharmony.github.io (国外)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册