注解 Futex 内核态实现

    百图画鸿蒙 + 百文说内核 + 百万注源码  => 挖透鸿蒙内核源码
    鸿蒙研究站 | 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)
上级 290450bf
......@@ -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;
......
......@@ -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到来
......
......@@ -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);
......
......@@ -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;
......
git add -A
git commit -m ' 注解Futex内核态实现
git commit -m ' 注解 Futex 内核态实现
百图画鸿蒙 + 百文说内核 + 百万注源码 => 挖透鸿蒙内核源码
鸿蒙研究站 | http://weharmonyos.com (国内)
| https://weharmony.github.io (国外)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册