diff --git a/kernel/base/include/los_futex_pri.h b/kernel/base/include/los_futex_pri.h index 196b46c91d01717ab463a2caaef06a7f7e15cc12..78c30af731c7b9c47ed7e133caccf44f5341c963 100644 --- a/kernel/base/include/los_futex_pri.h +++ b/kernel/base/include/los_futex_pri.h @@ -3,7 +3,25 @@ * @brief * @link @verbatim - @endverbatim + FUTEX_WAIT + 这个操作用来检测有uaddr指向的futex是否包含关心的数值val,如果是,则继续sleep直到FUTEX_WAKE操作触发。 + 加载futex的操作是原子的。这个加载,从比较关心的数值,到开始sleep,都是原子的,与另外一个对于同一个 + futex的操作是线性的,串行的,严格按照顺序来执行的。如果线程开始sleep,就表示有一个waiter在futex上。 + 如果futex的值不匹配,回调直接返回失败,错误代码是EAGAIN。 + + 与期望值对比的目的是为了防止丢失唤醒的操作。如果另一个线程在基于前面的数值阻塞调用之后,修改了这个值, + 另一个线程在数值改变之后,调用FUTEX_WAIT之前执行了FUTEX_WAKE操作,这个调用的线程就会观察到数值变换并且无法唤醒。 + 这里的意思是,调用FUTEX_WAIT需要做上面的一个操作,就是检测一下这个值是不是我们需要的,如果不是就等待, + 如果是就直接运行下去。之所以检测是为了避免丢失唤醒,也就是防止一直等待下去,比如我们在调用FUTEX_WAIT之前, + 另一个线程已经调用了FUTEX_WAKE,那么就不会有线程调用FUTEX_WAKE,调用FUTEX_WAIT的线程就永远等不到信号了,也就永远唤醒不了了。 + + 如果timeout不是NULL,就表示指向了一个特定的超时时钟。这个超时间隔使用系统时钟的颗粒度四舍五入, + 可以保证触发不会比定时的时间早。默认情况通过CLOCK_MONOTONIC测量,但是从Linux 4.5开始,可以在futex_op中设置 + FUTEX_CLOCK_REALTIME使用CLOCK_REALTIME测量。如果timeout是NULL,将会永远阻塞。 + + 注意:对于FUTEX_WAIT,timeout是一个关联的值。与其他的futex设置不同,timeout被认为是一个绝对值。 + 使用通过FUTEX_BITSET_MATCH_ANY特殊定义的val3传入FUTEX_WAIT_BITSET可以获得附带timeout的FUTEX_WAIT的值。 + @endverbatim * @version * @author weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点 * @date 2021-11-24 @@ -43,30 +61,6 @@ #define _LOS_FUTEX_PRI_H #include "los_list.h" -/*! - * @brief - * @link - @verbatim - FUTEX_WAIT - 这个操作用来检测有uaddr指向的futex是否包含关心的数值val,如果是,则继续sleep直到FUTEX_WAKE操作触发。 - 加载futex的操作是原子的。这个加载,从比较关心的数值,到开始sleep,都是原子的,与另外一个对于同一个 - futex的操作是线性的,串行的,严格按照顺序来执行的。如果线程开始sleep,就表示有一个waiter在futex上。 - 如果futex的值不匹配,回调直接返回失败,错误代码是EAGAIN。 - - 与期望值对比的目的是为了防止丢失唤醒的操作。如果另一个线程在基于前面的数值阻塞调用之后,修改了这个值, - 另一个线程在数值改变之后,调用FUTEX_WAIT之前执行了FUTEX_WAKE操作,这个调用的线程就会观察到数值变换并且无法唤醒。 - 这里的意思是,调用FUTEX_WAIT需要做上面的一个操作,就是检测一下这个值是不是我们需要的,如果不是就等待, - 如果是就直接运行下去。之所以检测是为了避免丢失唤醒,也就是防止一直等待下去,比如我们在调用FUTEX_WAIT之前, - 另一个线程已经调用了FUTEX_WAKE,那么就不会有线程调用FUTEX_WAKE,调用FUTEX_WAIT的线程就永远等不到信号了,也就永远唤醒不了了。 - - 如果timeout不是NULL,就表示指向了一个特定的超时时钟。这个超时间隔使用系统时钟的颗粒度四舍五入, - 可以保证触发不会比定时的时间早。默认情况通过CLOCK_MONOTONIC测量,但是从Linux 4.5开始,可以在futex_op中设置 - FUTEX_CLOCK_REALTIME使用CLOCK_REALTIME测量。如果timeout是NULL,将会永远阻塞。 - - 注意:对于FUTEX_WAIT,timeout是一个关联的值。与其他的futex设置不同,timeout被认为是一个绝对值。 - 使用通过FUTEX_BITSET_MATCH_ANY特殊定义的val3传入FUTEX_WAIT_BITSET可以获得附带timeout的FUTEX_WAIT的值。 - @endverbatim -*/ #define FUTEX_WAIT 0 ///< 原子性的检查 uaddr 中计数器的值是否为 val,如果是则让任务休眠,直到 FUTEX_WAKE 或者超时(time-out)。 //也就是把任务挂到 uaddr 相对应的等待队列上去。 #define FUTEX_WAKE 1 ///< 最多唤醒 val 个等待在 uaddr 上任务。 @@ -79,13 +73,13 @@ #define FUTEX_PRIVATE 128 //私有快锁(以虚拟地址进行哈希) #define FUTEX_MASK 0x3U - +/// 每个futex node对应一个被挂起的task ,key值唯一标识一把用户态锁,具有相同key值的node被queue_list串联起来表示被同一把锁阻塞的task队列。 typedef struct { - UINTPTR key; /* private:uvaddr | 私有锁,用虚拟地址 shared:paddr | 共享锁,用物理地址 */ + UINTPTR key; /* private:uvaddr | 私有锁,用虚拟地址 shared:paddr | 共享锁,用物理地址*/ UINT32 index; /* hash bucket index | 哈希桶索引 OsFutexKeyToIndex */ UINT32 pid; /* private:process id shared:OS_INVALID(-1) | 私有锁:进程ID , 共享锁为 -1 */ - LOS_DL_LIST pendList; /* point to pendList in TCB struct | 挂到任务阻塞链表上,挂起任务*/ - LOS_DL_LIST queueList; /* thread list blocked by this lock | 挂等待这把锁的任务*/ + LOS_DL_LIST pendList; /* point to pendList in TCB struct | 指向 TCB 结构中的 pendList, 通过它找到任务*/ + LOS_DL_LIST queueList; /* thread list blocked by this lock | 挂等待这把锁的任务,上面挂的是 FutexNode.queueList*/ LOS_DL_LIST futexList; /* point to the next FutexNode | 下一把Futex锁*/ } FutexNode; diff --git a/kernel/base/include/los_task_pri.h b/kernel/base/include/los_task_pri.h index 0acfa2bd8895de43661537df2b591570406b1516..d79bfeeaea81abe9aab277f00b6211dbcb98fd9d 100644 --- a/kernel/base/include/los_task_pri.h +++ b/kernel/base/include/los_task_pri.h @@ -285,8 +285,8 @@ STATIC INLINE BOOL OsTaskIsUserMode(const LosTaskCB *taskCB) #define OS_TASK_WAIT_GID (1 << 2U) ///< 等待组ID #define OS_TASK_WAIT_SEM (OS_TASK_WAIT_GID + 1) ///< 等待信号量发生 #define OS_TASK_WAIT_QUEUE (OS_TASK_WAIT_SEM + 1) ///< 等待队列到来 -#define OS_TASK_WAIT_JOIN (OS_TASK_WAIT_QUEUE + 1) ///< 等待 -#define OS_TASK_WAIT_SIGNAL (OS_TASK_WAIT_JOIN + 1) ///< +#define OS_TASK_WAIT_JOIN (OS_TASK_WAIT_QUEUE + 1) ///< 等待联结到来 +#define OS_TASK_WAIT_SIGNAL (OS_TASK_WAIT_JOIN + 1) ///< 等待普通信号到来 #define OS_TASK_WAIT_LITEIPC (OS_TASK_WAIT_SIGNAL + 1) ///< 等待liteipc到来 #define OS_TASK_WAIT_MUTEX (OS_TASK_WAIT_LITEIPC + 1) ///< 等待MUTEX到来 #define OS_TASK_WAIT_FUTEX (OS_TASK_WAIT_MUTEX + 1) ///< 等待FUTEX到来 diff --git a/kernel/base/ipc/los_futex.c b/kernel/base/ipc/los_futex.c index 294e19196eaa4f67502f890c70992dc2ec1a6721..6fb6c0dcbbdaf98bef9093ae477a1b311046a4f1 100644 --- a/kernel/base/ipc/los_futex.c +++ b/kernel/base/ipc/los_futex.c @@ -97,8 +97,8 @@ #ifdef LOSCFG_KERNEL_VM -#define OS_FUTEX_FROM_FUTEXLIST(ptr) LOS_DL_LIST_ENTRY(ptr, FutexNode, futexList) -#define OS_FUTEX_FROM_QUEUELIST(ptr) LOS_DL_LIST_ENTRY(ptr, FutexNode, queueList) +#define OS_FUTEX_FROM_FUTEXLIST(ptr) LOS_DL_LIST_ENTRY(ptr, FutexNode, futexList) // 通过快锁节点找到结构体 +#define OS_FUTEX_FROM_QUEUELIST(ptr) LOS_DL_LIST_ENTRY(ptr, FutexNode, queueList) // 通过队列节点找到结构体 #define OS_FUTEX_KEY_BASE USER_ASPACE_BASE ///< 进程用户空间基址 #define OS_FUTEX_KEY_MAX (USER_ASPACE_BASE + USER_ASPACE_SIZE) ///< 进程用户空间尾址 @@ -227,10 +227,10 @@ STATIC INLINE UINT32 OsFutexKeyToIndex(const UINTPTR futexKey, const UINT32 flag UINT32 index = LOS_HashFNV32aBuf(&futexKey, sizeof(UINTPTR), FNV1_32A_INIT);//获取哈希桶索引 if (flags & FUTEX_PRIVATE) { - index &= FUTEX_HASH_PRIVATE_MASK; + index &= FUTEX_HASH_PRIVATE_MASK;//将index锁定在 0 ~ 63号 } else { index &= FUTEX_HASH_SHARED_MASK; - index += FUTEX_INDEX_SHARED_POS;//共享锁索引 + index += FUTEX_INDEX_SHARED_POS;//共享锁索引,将index锁定在 64 ~ 79号 } return index; @@ -242,29 +242,29 @@ STATIC INLINE VOID OsFutexSetKey(UINTPTR futexKey, UINT32 flags, FutexNode *node node->index = OsFutexKeyToIndex(futexKey, flags);//哈希桶索引 node->pid = (flags & FUTEX_PRIVATE) ? LOS_GetCurrProcessID() : OS_INVALID;//获取进程ID,共享快锁时 快锁节点没有进程ID } - +//析构参数节点 STATIC INLINE VOID OsFutexDeinitFutexNode(FutexNode *node) { node->index = OS_INVALID_VALUE; node->pid = 0; LOS_ListDelete(&node->queueList); } - +/// 新旧两个节点交换 futexList 位置 STATIC INLINE VOID OsFutexReplaceQueueListHeadNode(FutexNode *oldHeadNode, FutexNode *newHeadNode) { LOS_DL_LIST *futexList = oldHeadNode->futexList.pstPrev; - LOS_ListDelete(&oldHeadNode->futexList); - LOS_ListHeadInsert(futexList, &newHeadNode->futexList); - if ((newHeadNode->queueList.pstNext == NULL) || (newHeadNode->queueList.pstPrev == NULL)) { - LOS_ListInit(&newHeadNode->queueList); + LOS_ListDelete(&oldHeadNode->futexList);//将旧节点从futexList链表上摘除 + LOS_ListHeadInsert(futexList, &newHeadNode->futexList);//将新节点从头部插入futexList链表 + if ((newHeadNode->queueList.pstNext == NULL) || (newHeadNode->queueList.pstPrev == NULL)) {//新节点前后没有等待这把锁的任务 + LOS_ListInit(&newHeadNode->queueList);//初始化等锁任务链表 } } - +/// 将参数节点从futexList上摘除 STATIC INLINE VOID OsFutexDeleteKeyFromFutexList(FutexNode *node) { LOS_ListDelete(&node->futexList); } - +/// 从哈希桶中删除快锁节点 STATIC VOID OsFutexDeleteKeyNodeFromHash(FutexNode *node, BOOL isDeleteHead, FutexNode **headNode, BOOL *queueFlags) { FutexNode *nextNode = NULL; @@ -273,8 +273,8 @@ STATIC VOID OsFutexDeleteKeyNodeFromHash(FutexNode *node, BOOL isDeleteHead, Fut return; } - if (LOS_ListEmpty(&node->queueList)) { - OsFutexDeleteKeyFromFutexList(node); + if (LOS_ListEmpty(&node->queueList)) {//如果没有任务在等锁 + OsFutexDeleteKeyFromFutexList(node);//从快锁链表上摘除 if (queueFlags != NULL) { *queueFlags = TRUE; } @@ -282,10 +282,10 @@ STATIC VOID OsFutexDeleteKeyNodeFromHash(FutexNode *node, BOOL isDeleteHead, Fut } /* FutexList is not NULL, but the header node of queueList */ - if (node->futexList.pstNext != NULL) { - if (isDeleteHead == TRUE) { - nextNode = OS_FUTEX_FROM_QUEUELIST(LOS_DL_LIST_FIRST(&node->queueList)); - OsFutexReplaceQueueListHeadNode(node, nextNode); + if (node->futexList.pstNext != NULL) {//是头节点 + if (isDeleteHead == TRUE) {//是否要删除头节点 + nextNode = OS_FUTEX_FROM_QUEUELIST(LOS_DL_LIST_FIRST(&node->queueList));//取出第一个快锁节点 + OsFutexReplaceQueueListHeadNode(node, nextNode);//两个节点交换位置 if (headNode != NULL) { *headNode = nextNode; } @@ -302,18 +302,18 @@ EXIT: VOID OsFutexNodeDeleteFromFutexHash(FutexNode *node, BOOL isDeleteHead, FutexNode **headNode, BOOL *queueFlags) { FutexHash *hashNode = NULL; - + //通过key找到桶号 UINT32 index = OsFutexKeyToIndex(node->key, (node->pid == OS_INVALID) ? 0 : FUTEX_PRIVATE); if (index >= FUTEX_INDEX_MAX) { return; } - hashNode = &g_futexHash[index]; + hashNode = &g_futexHash[index];//找到hash桶 if (OsMuxLockUnsafe(&hashNode->listLock, LOS_WAIT_FOREVER)) { return; } - if (node->index != index) { + if (node->index != index) {//快锁节点桶号需和哈希桶号一致 goto EXIT; } @@ -348,7 +348,7 @@ STATIC FutexNode *OsFutexDeleteAlreadyWakeTaskAndGetNext(const FutexNode *node, return tempNode; } -/// 插入一把新Futex锁进哈希 +/// 插入一把新Futex锁到哈希桶中 STATIC VOID OsFutexInsertNewFutexKeyToHash(FutexNode *node) { FutexNode *headNode = NULL; @@ -505,12 +505,12 @@ STATIC FutexNode *OsFindFutexNode(const FutexNode *node) FutexNode *headNode = NULL; for (futexList = futexList->pstNext; - futexList != &(hashNode->lockList); + futexList != &(hashNode->lockList);//判断循环结束条件,相等时说明跑完一轮了 futexList = futexList->pstNext) { - headNode = OS_FUTEX_FROM_FUTEXLIST(futexList); - if ((headNode->key == node->key) && (headNode->pid == node->pid)) { - return headNode; - } + headNode = OS_FUTEX_FROM_FUTEXLIST(futexList);//拿到快锁节点实体 + if ((headNode->key == node->key) && (headNode->pid == node->pid)) {//已经存在这个节点,注意这里的比较 + return headNode;//是key和pid 一起比较,因为只有这样才能确定唯一性 + }//详细讲解请查看 鸿蒙内核源码分析(内核态锁篇) | 如何实现快锁Futex(下) } return NULL; @@ -524,7 +524,7 @@ STATIC INT32 OsFindAndInsertToHash(FutexNode *node) INT32 ret; headNode = OsFindFutexNode(node); - if (headNode == NULL) {//没有找到 + if (headNode == NULL) {//没有找到,说明这是一把新锁 OsFutexInsertNewFutexKeyToHash(node); LOS_ListInit(&(node->queueList)); return LOS_OK; @@ -609,13 +609,13 @@ STATIC INT32 OsFutexDeleteTimeoutTaskNode(FutexHash *hashNode, FutexNode *node) } return LOS_ETIMEDOUT; } - +/// 将快锁节点插入任务 STATIC INT32 OsFutexInsertTaskToHash(LosTaskCB **taskCB, FutexNode **node, const UINTPTR futexKey, const UINT32 flags) { INT32 ret; - *taskCB = OsCurrTaskGet(); - *node = &((*taskCB)->futex); - OsFutexSetKey(futexKey, flags, *node); + *taskCB = OsCurrTaskGet(); //获取当前任务 + *node = &((*taskCB)->futex); //获取当前任务的快锁节点 + OsFutexSetKey(futexKey, flags, *node);//设置参数 key index pid ret = OsFindAndInsertToHash(*node); if (ret) { @@ -625,33 +625,33 @@ STATIC INT32 OsFutexInsertTaskToHash(LosTaskCB **taskCB, FutexNode **node, const LOS_ListInit(&((*node)->pendList)); return LOS_OK; } -//将当前任务挂入等待链表中 +/// 将当前任务挂入等待链表中 STATIC INT32 OsFutexWaitTask(const UINT32 *userVaddr, const UINT32 flags, const UINT32 val, const UINT32 timeOut) { INT32 futexRet; UINT32 intSave, lockVal; LosTaskCB *taskCB = NULL; FutexNode *node = NULL; - UINTPTR futexKey = OsFutexFlagsToKey(userVaddr, flags); - UINT32 index = OsFutexKeyToIndex(futexKey, flags); + UINTPTR futexKey = OsFutexFlagsToKey(userVaddr, flags);//通过地址和flags 找到 key + UINT32 index = OsFutexKeyToIndex(futexKey, flags);//通过key找到哈希桶 FutexHash *hashNode = &g_futexHash[index]; - if (OsFutexLock(&hashNode->listLock)) { + if (OsFutexLock(&hashNode->listLock)) {//操作快锁节点链表前先上互斥锁 return LOS_EINVAL; } - - if (LOS_ArchCopyFromUser(&lockVal, userVaddr, sizeof(UINT32))) { + //userVaddr必须是用户空间虚拟地址 + if (LOS_ArchCopyFromUser(&lockVal, userVaddr, sizeof(UINT32))) {//将值拷贝到内核空间 PRINT_ERR("Futex wait param check failed! copy from user failed!\n"); futexRet = LOS_EINVAL; goto EXIT_ERR; } - if (lockVal != val) { + if (lockVal != val) {//对参数内部逻辑检查 futexRet = LOS_EBADF; goto EXIT_ERR; } - - if (OsFutexInsertTaskToHash(&taskCB, &node, futexKey, flags)) { + //注意第二个参数 FutexNode *node = NULL + if (OsFutexInsertTaskToHash(&taskCB, &node, futexKey, flags)) {// node = taskCB->futex futexRet = LOS_NOK; goto EXIT_ERR; } @@ -662,7 +662,7 @@ STATIC INT32 OsFutexWaitTask(const UINT32 *userVaddr, const UINT32 flags, const OsSchedLock(); LOS_SpinUnlock(&g_taskSpin); - futexRet = OsFutexUnlock(&hashNode->listLock); + futexRet = OsFutexUnlock(&hashNode->listLock);// if (futexRet) { OsSchedUnlock(); LOS_IntRestore(intSave); diff --git a/kernel/base/ipc/los_signal.c b/kernel/base/ipc/los_signal.c index 21f9e993496b9c4c7d5e46b42b299c207a913ed9..8829295e2847e3272329989e2469d89162f8f813 100644 --- a/kernel/base/ipc/los_signal.c +++ b/kernel/base/ipc/los_signal.c @@ -160,17 +160,17 @@ STATIC UINT32 OsPendingTaskWake(LosTaskCB *taskCB, INT32 signo) OsTaskWakeClearPendMask(taskCB); OsSchedTaskWake(taskCB); break; - case OS_TASK_WAIT_SIGNAL: + case OS_TASK_WAIT_SIGNAL://等待普通信号 OsSigWaitTaskWake(taskCB, signo); break; - case OS_TASK_WAIT_LITEIPC: - OsTaskWakeClearPendMask(taskCB); - OsSchedTaskWake(taskCB); + case OS_TASK_WAIT_LITEIPC://等待liteipc信号 + OsTaskWakeClearPendMask(taskCB);//重置任务的等待信息 + OsSchedTaskWake(taskCB);//唤醒任务 break; - case OS_TASK_WAIT_FUTEX: - OsFutexNodeDeleteFromFutexHash(&taskCB->futex, TRUE, NULL, NULL); - OsTaskWakeClearPendMask(taskCB); - OsSchedTaskWake(taskCB); + case OS_TASK_WAIT_FUTEX://等待快锁信号 + OsFutexNodeDeleteFromFutexHash(&taskCB->futex, TRUE, NULL, NULL);//从哈希桶中删除快锁 + OsTaskWakeClearPendMask(taskCB);//重置任务的等待信息 + OsSchedTaskWake(taskCB);//唤醒任务 break; default: break; diff --git a/zzz/git/push.sh b/zzz/git/push.sh index a6e3019184dc4ce8a990a4befee9b50d019db5df..91662ce5051b5f76ddb48d70819b8c68c0f3a213 100644 --- a/zzz/git/push.sh +++ b/zzz/git/push.sh @@ -1,5 +1,5 @@ git add -A -git commit -m ' 注解Futex内核态实现 +git commit -m ' 注解 Futex 内核态实现 百图画鸿蒙 + 百文说内核 + 百万注源码 => 挖透鸿蒙内核源码 鸿蒙研究站 | http://weharmonyos.com (国内) | https://weharmony.github.io (国外)