更新日期: 2022/06/01 来源: https://gitee.com/weharmony/kernel_liteos_a_note
los_futex_pri.h 文件参考

浏览源代码.

结构体

struct  FutexNode
 每个futex node对应一个被挂起的task ,key值唯一标识一把用户态锁,具有相同key值的node被queue_list串联起来表示被同一把锁阻塞的task队列。 更多...
 

函数

UINT32 OsFutexInit (VOID)
 
VOID OsFutexNodeDeleteFromFutexHash (FutexNode *node, BOOL isDeleteHead, FutexNode **headNode, BOOL *queueFlags)
 从哈希桶上删除快锁 更多...
 
INT32 OsFutexWake (const UINT32 *userVaddr, UINT32 flags, INT32 wakeNumber)
 唤醒一个被指定锁阻塞的线程 更多...
 
INT32 OsFutexWait (const UINT32 *userVaddr, UINT32 flags, UINT32 val, UINT32 absTime)
 设置线程等待 | 向Futex表中插入代表被阻塞的线程的node 更多...
 
INT32 OsFutexRequeue (const UINT32 *userVaddr, UINT32 flags, INT32 wakeNumber, INT32 count, const UINT32 *newUserVaddr)
 调整指定锁在Futex表中的位置 更多...
 

详细描述

 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的值。       
版本
作者
weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点
日期
2021-11-24

在文件 los_futex_pri.h 中定义.

函数说明

◆ OsFutexInit()

UINT32 OsFutexInit ( VOID  )

在文件 los_futex.c144 行定义.

145{
146 INT32 count;
147 UINT32 ret;
148 // 初始化 80个哈希桶
149 for (count = 0; count < FUTEX_INDEX_MAX; count++) {
150 LOS_ListInit(&g_futexHash[count].lockList); // 初始化双向链表,上面挂 FutexNode
151 ret = LOS_MuxInit(&(g_futexHash[count].listLock), NULL);//初始化互斥锁
152 if (ret) {
153 return ret;
154 }
155 }
156
157 return LOS_OK;
158}
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListInit(LOS_DL_LIST *list)
Definition: los_list.h:104
LITE_OS_SEC_TEXT UINT32 LOS_MuxInit(LosMux *mutex, const LosMuxAttr *attr)
初始化互斥锁
Definition: los_mux.c:262
FutexHash g_futexHash[FUTEX_INDEX_MAX]
80个哈希桶
Definition: los_futex.c:121
signed int INT32
Definition: los_typedef.h:60
unsigned int UINT32
Definition: los_typedef.h:57
函数调用图:

◆ OsFutexNodeDeleteFromFutexHash()

VOID OsFutexNodeDeleteFromFutexHash ( FutexNode node,
BOOL  isDeleteHead,
FutexNode **  headNode,
BOOL queueFlags 
)

从哈希桶上删除快锁

在文件 los_futex.c302 行定义.

303{
304 FutexHash *hashNode = NULL;
305 //通过key找到桶号
306 UINT32 index = OsFutexKeyToIndex(node->key, (node->pid == OS_INVALID) ? 0 : FUTEX_PRIVATE);
307 if (index >= FUTEX_INDEX_MAX) {
308 return;
309 }
310
311 hashNode = &g_futexHash[index];//找到hash桶
312 if (OsMuxLockUnsafe(&hashNode->listLock, LOS_WAIT_FOREVER)) {
313 return;
314 }
315
316 if (node->index != index) {//快锁节点桶号需和哈希桶号一致
317 goto EXIT;
318 }
319
320 OsFutexDeleteKeyNodeFromHash(node, isDeleteHead, headNode, queueFlags);
321
322EXIT:
323 if (OsMuxUnlockUnsafe(OsCurrTaskGet(), &hashNode->listLock, NULL)) {
324 return;
325 }
326
327 return;
328}
STATIC VOID OsFutexDeleteKeyNodeFromHash(FutexNode *node, BOOL isDeleteHead, FutexNode **headNode, BOOL *queueFlags)
从哈希桶中删除快锁节点
Definition: los_futex.c:268
STATIC INLINE UINT32 OsFutexKeyToIndex(const UINTPTR futexKey, const UINT32 flags)
通过哈希key获取索引
Definition: los_futex.c:225
UINT32 OsMuxLockUnsafe(LosMux *mutex, UINT32 timeout)
Definition: los_mux.c:398
UINT32 OsMuxUnlockUnsafe(LosTaskCB *taskCB, LosMux *mutex, BOOL *needSched)
Definition: los_mux.c:527
STATIC INLINE LosTaskCB * OsCurrTaskGet(VOID)
单独哈希桶,上面挂了一个个 FutexNode
Definition: los_futex.c:116
LosMux listLock
内核操作lockList的互斥锁
Definition: los_futex.c:117
UINTPTR key
Definition: los_futex_pri.h:78
UINT32 index
Definition: los_futex_pri.h:79
UINT32 pid
Definition: los_futex_pri.h:80
函数调用图:
这是这个函数的调用关系图:

◆ OsFutexRequeue()

INT32 OsFutexRequeue ( const UINT32 userVaddr,
UINT32  flags,
INT32  wakeNumber,
INT32  count,
const UINT32 newUserVaddr 
)

调整指定锁在Futex表中的位置

在文件 los_futex.c1022 行定义.

1023{
1024 INT32 ret;
1025 UINTPTR oldFutexKey;
1026 UINTPTR newFutexKey;
1027 INT32 oldIndex;
1028 INT32 newIndex;
1029 FutexHash *oldHashNode = NULL;
1030 FutexHash *newHashNode = NULL;
1031 FutexNode *oldHeadNode = NULL;
1032 BOOL wakeAny = FALSE;
1033
1034 if (OsFutexRequeueParamCheck(userVaddr, flags, newUserVaddr)) {
1035 return LOS_EINVAL;
1036 }
1037
1038 oldFutexKey = OsFutexFlagsToKey(userVaddr, flags);//先拿key
1039 newFutexKey = OsFutexFlagsToKey(newUserVaddr, flags);
1040 oldIndex = OsFutexKeyToIndex(oldFutexKey, flags);//再拿所在哈希桶位置,共有80个哈希桶
1041 newIndex = OsFutexKeyToIndex(newFutexKey, flags);
1042
1043 oldHashNode = &g_futexHash[oldIndex];//拿到对应哈希桶实体
1044 if (OsFutexLock(&oldHashNode->listLock)) {
1045 return LOS_EINVAL;
1046 }
1047
1048 oldHeadNode = OsFutexRequeueRemoveOldKeyAndGetHead(oldFutexKey, flags, wakeNumber, newFutexKey, count, &wakeAny);
1049 if (oldHeadNode == NULL) {
1050 (VOID)OsFutexUnlock(&oldHashNode->listLock);
1051 if (wakeAny == TRUE) {
1052 ret = LOS_OK;
1053 goto EXIT;
1054 }
1055 return LOS_EBADF;
1056 }
1057
1058 newHashNode = &g_futexHash[newIndex];
1059 if (oldIndex != newIndex) {
1060 if (OsFutexUnlock(&oldHashNode->listLock)) {
1061 return LOS_EINVAL;
1062 }
1063
1064 if (OsFutexLock(&newHashNode->listLock)) {
1065 return LOS_EINVAL;
1066 }
1067 }
1068
1069 ret = OsFutexRequeueInsertNewKey(newFutexKey, newIndex, oldHeadNode);
1070
1071 if (OsFutexUnlock(&newHashNode->listLock)) {
1072 return LOS_EINVAL;
1073 }
1074
1075EXIT:
1076 if (wakeAny == TRUE) {
1077 LOS_MpSchedule(OS_MP_CPU_ALL);
1078 LOS_Schedule();
1079 }
1080
1081 return ret;
1082}
VOID LOS_Schedule(VOID)
Trigger active task scheduling.
Definition: los_sched.c:469
STATIC INT32 OsFutexLock(LosMux *lock)
对互斥锁封装
Definition: los_futex.c:124
STATIC INT32 OsFutexUnlock(LosMux *lock)
初始化Futex(Fast userspace mutex,用户态快速互斥锁)模块
Definition: los_futex.c:134
STATIC INLINE UINTPTR OsFutexFlagsToKey(const UINT32 *userVaddr, const UINT32 flags)
通过用户空间地址获取哈希key
Definition: los_futex.c:212
STATIC INT32 OsFutexRequeueInsertNewKey(UINTPTR newFutexKey, INT32 newIndex, FutexNode *oldHeadNode)
Definition: los_futex.c:866
STATIC FutexNode * OsFutexRequeueRemoveOldKeyAndGetHead(UINTPTR oldFutexKey, UINT32 flags, INT32 wakeNumber, UINTPTR newFutexKey, INT32 requeueCount, BOOL *wakeAny)
删除旧key并获取头节点
Definition: los_futex.c:959
STATIC INT32 OsFutexRequeueParamCheck(const UINT32 *oldUserVaddr, UINT32 flags, const UINT32 *newUserVaddr)
检查锁在Futex表中的状态
Definition: los_futex.c:995
VOID LOS_MpSchedule(UINT32 target)
Definition: los_mp.c:76
unsigned long UINTPTR
Definition: los_typedef.h:68
size_t BOOL
Definition: los_typedef.h:88
每个futex node对应一个被挂起的task ,key值唯一标识一把用户态锁,具有相同key值的node被queue_list串联起来表示被同一把锁阻塞的task队列。
Definition: los_futex_pri.h:77
函数调用图:
这是这个函数的调用关系图:

◆ OsFutexWait()

INT32 OsFutexWait ( const UINT32 userVaddr,
UINT32  flags,
UINT32  val,
UINT32  absTime 
)

设置线程等待 | 向Futex表中插入代表被阻塞的线程的node

在文件 los_futex.c693 行定义.

694{
695 INT32 ret;
696 UINT32 timeout = LOS_WAIT_FOREVER;
697
698 ret = OsFutexWaitParamCheck(userVaddr, flags, absTime);//参数检查
699 if (ret) {
700 return ret;
701 }
702 if (absTime != LOS_WAIT_FOREVER) {//转换时间 , 内核的时间单位是 tick
703 timeout = OsNS2Tick((UINT64)absTime * OS_SYS_NS_PER_US); //转成 tick
704 }
705
706 return OsFutexWaitTask(userVaddr, flags, val, timeout);//将任务挂起 timeOut 时长
707}
LITE_OS_SEC_TEXT_MINOR UINT32 OsNS2Tick(UINT64 nanoseconds)
纳秒转化成 tick
Definition: los_sys.c:125
STATIC INT32 OsFutexWaitParamCheck(const UINT32 *userVaddr, UINT32 flags, UINT32 absTime)
Definition: los_futex.c:557
STATIC INT32 OsFutexWaitTask(const UINT32 *userVaddr, const UINT32 flags, const UINT32 val, const UINT32 timeout)
将当前任务挂入等待链表中
Definition: los_futex.c:626
long unsigned int UINT64
Definition: los_typedef.h:66
函数调用图:
这是这个函数的调用关系图:

◆ OsFutexWake()

INT32 OsFutexWake ( const UINT32 userVaddr,
UINT32  flags,
INT32  wakeNumber 
)

唤醒一个被指定锁阻塞的线程

在文件 los_futex.c815 行定义.

816{
817 INT32 ret, futexRet;
818 UINTPTR futexKey;
819 UINT32 index;
820 FutexHash *hashNode = NULL;
821 FutexNode *headNode = NULL;
822 BOOL wakeAny = FALSE;
823 //1.检查参数
824 if (OsFutexWakeParamCheck(userVaddr, flags)) {
825 return LOS_EINVAL;
826 }
827 //2.找到指定用户空间地址对应的桶
828 futexKey = OsFutexFlagsToKey(userVaddr, flags);
829 index = OsFutexKeyToIndex(futexKey, flags);
830
831 hashNode = &g_futexHash[index];
832 if (OsFutexLock(&hashNode->listLock)) {
833 return LOS_EINVAL;
834 }
835 //3.换起等待该锁的进程
836 ret = OsFutexWakeTask(futexKey, flags, wakeNumber, &headNode, &wakeAny);
837 if (ret) {
838 goto EXIT_ERR;
839 }
840
841#ifdef LOS_FUTEX_DEBUG
843#endif
844
845 futexRet = OsFutexUnlock(&hashNode->listLock);
846 if (futexRet) {
847 goto EXIT_UNLOCK_ERR;
848 }
849 //4.根据指定参数决定是否发起调度
850 if (wakeAny == TRUE) {
851 LOS_MpSchedule(OS_MP_CPU_ALL);
852 LOS_Schedule();
853 }
854
855 return LOS_OK;
856
857EXIT_ERR:
858 futexRet = OsFutexUnlock(&hashNode->listLock);
859EXIT_UNLOCK_ERR:
860 if (futexRet) {
861 return futexRet;
862 }
863 return ret;
864}
STATIC INT32 OsFutexWakeTask(UINTPTR futexKey, UINT32 flags, INT32 wakeNumber, FutexNode **newHeadNode, BOOL *wakeAny)
OsFutexWakeTask 唤醒任务
Definition: los_futex.c:781
STATIC INT32 OsFutexWakeParamCheck(const UINT32 *userVaddr, UINT32 flags)
Definition: los_futex.c:709
VOID OsFutexHashShow(VOID)
Definition: los_futex.c:190
函数调用图:
这是这个函数的调用关系图: