SylixOS线程私有数据浅析

目录

1. 线程私有数据概述    1

2. 线程私有数据的相关API函数流程浅析    1

2.1    加入线程私有变量    1

2.2    删除线程私有变量    3

2.3    设置私有线程变量    6

2.4    获得线程私有变量值    8

3. 总结    10

4. 参考文献    10

  1. 线程私有数据概述

    在SylixOS中为了满足多线程安全的要求,使得一种资源可以安全的被多个线程使用,采用了包括代码临界区保护和可重入性等方法。本文描述实现可重入的一种方法:线程私有数据。值得注意的是这种保护方式牺牲了系统的实时性并且只针对单CPU系统有效,若非必要,SylixOS不推荐使用此方式。

  2. 线程私有数据的相关API函数流程浅析

  3. 加入线程私有变量

    加入线程私有变量的流程如图 21所示。

    图 21 加入线程私有变量

  4. 输入

    在调用API_ThreadVarAdd函数时,我们需要输入线程ID以及私有变量地址。具体实现如程序清单 21所示。

    程序清单 21 加入线程私有变量输入


    ULONGAPI_ThreadVarAdd (LW_OBJECT_HANDLEulId, ULONG *pulAddr)

    {

    REGISTERUINT16usIndex;

    REGISTERPLW_CLASS_TCBptcb;

    REGISTERPLW_CLASS_THREADVARpthreadvar;

    usIndex = _ObjectGetIndex(ulId);

    ...

    }

  5. 判断是否在中断

    获取CPU当前的中断嵌套值,判断是否在中断,该函数不能在中断中调用。具体实现如程序清单 22所示。

    程序清单 22 判断是否在中断


    if (LW_CPU_GET_CUR_NESTING()) { /* 不能在中断中调用 */

    _DebugHandle(__ERRORMESSAGE_LEVEL, "called from ISR.\r\n");

    _ErrorHandle(ERROR_KERNEL_IN_ISR);

    return (ERROR_KERNEL_IN_ISR);

    }

  6. 检查线程的有效性

    在进入内核态之前,检查当前线程ID是否符合线程规范,以判断该线程是否有效。具体实现如程序清单 23所示。

    程序清单 23 检查线程有效性


    #if LW_CFG_ARG_CHK_EN > 0

    if (!_ObjectClassOK(ulId, _OBJECT_THREAD)) {

    _DebugHandle(__ERRORMESSAGE_LEVEL, "thread handle invalidate.\r\n");

    _ErrorHandle(ERROR_KERNEL_HANDLE_NULL);

    return (ERROR_KERNEL_HANDLE_NULL);

    }

    if (_Thread_Index_Invalid(usIndex)) { /* 检查线程有效性 */

    _DebugHandle(__ERRORMESSAGE_LEVEL, "thread handle invalidate.\r\n");

    _ErrorHandle(ERROR_THREAD_NULL);

    return (ERROR_THREAD_NULL);

    }

    #endif

  7. 进入内核态

    进入内核态进行操作。

  8. 检查线程的有效性

    进入内核态以后,通过检查对应的线程TCB地址表来判断线程是否有效。具体实现如程序清单 24所示。

    程序清单 24 检查线程有效性


    if (_Thread_Invalid(usIndex)) {

    __KERNEL_EXIT(); /* 退出内核 */

    _DebugHandle(__ERRORMESSAGE_LEVEL, "thread handle invalidate.\r\n");

    _ErrorHandle(ERROR_THREAD_NULL);

    return (ERROR_THREAD_NULL);

    }

  9. 分配控制块

    为当前的线程分配一个私有数据控制块,通过Allocate_ThreadVar_Object函数从空闲ThreadVar 控件池中取出一个空闲 ThreadVar。具体实现如程序清单 25所示。

    程序清单 25 分配控制块


    pthreadvar = _Allocate_ThreadVar_Object(); /* 分配一个控制块 */

  10. 检查控制块的有效性

    判断获得的控制器是否有效。具体实现如程序清单 26所示。

    程序清单 26 检查控制块的有效性


    if (!pthreadvar) {

    __KERNEL_EXIT(); /* 退出内核 */

    _ErrorHandle(ERROR_THREAD_VAR_FULL);

    return (ERROR_THREAD_VAR_FULL);

    }

  11. 链接控制块

    将获得的私有数据控制块加入TCB的上下文。具体实现如程序清单 27所示。

    程序清单 27 链接控制块


    pthreadvar->PRIVATEVAR_pulAddress = pulAddr;

    pthreadvar->PRIVATEVAR_ulValueSave = *pulAddr;

    ptcb = _K_ptcbTCBIdTable[usIndex];

    /* 加入TCB上下文 */

    _List_Line_Add_Ahead(&pthreadvar->PRIVATEVAR_lineVarList, &ptcb->TCB_plinePrivateVars);

  12. 退出内核

    退出内核态。

  13. 删除线程私有变量

    删除线程私有变量的流程如图 22所示。

    图 22 删除线程私有变量

  14. 输入

    在调用API_ThreadVarDelete函数时,我们需要输入线程ID以及私有变量地址。具体实现如程序清单 28所示。

    程序清单 28 删除线程私有数据输入


    ULONGAPI_ThreadVarDelete (LW_OBJECT_HANDLEulId, ULONG *pulAddr)

    {

    REGISTERUINT16usIndex;

    REGISTERPLW_CLASS_TCBptcbCur;

    REGISTERPLW_CLASS_TCBptcb;

    REGISTERPLW_LIST_LINEplineVar;

    REGISTERPLW_CLASS_THREADVARpthreadvar;

    usIndex = _ObjectGetIndex(ulId);

    }

  15. 判断是否在中断

    获取CPU当前的中断嵌套值,判断是否在中断,该函数不能在中断中调用。

  16. 检查线程的有效性

    在进入内核态之前,检查当前线程ID是否符合线程规范,以判断该线程是否有效。

  17. 进入内核态

    进入内核态进行操作。

  18. 检查线程的有效性

    进入内核态以后,通过检查对应的线程TCB地址表来判断线程是否有效。

  19. 查找私有数据控制块并解链

    通过全局变量私有化线表查找控制块,将私有数据控制块从TCB中解链并释放控制块。具体实现如程序清单 29所示。

    程序清单 29 查找控制块并解链


    for (plineVar = ptcb->TCB_plinePrivateVars; /* 查找 */

    plineVar != LW_NULL;

    plineVar = _list_line_get_next(plineVar)) {

    pthreadvar = _LIST_ENTRY(plineVar, LW_CLASS_THREADVAR, PRIVATEVAR_lineVarList);

    if (pthreadvar->PRIVATEVAR_pulAddress == pulAddr) {

    if (ptcb == ptcbCur) {

    *pulAddr = pthreadvar->PRIVATEVAR_ulValueSave;

    }

    _List_Line_Del(plineVar, &ptcb->TCB_plinePrivateVars); /* 从 TCB 中解链 */

    _Free_ThreadVar_Object(pthreadvar); /* 释放控制块 */

    __KERNEL_EXIT(); /* 退出内核 */

    return (ERROR_NONE);

    }

    }

     
  20. 退出内核

    退出内核态。

  21. 设置私有线程变量

    设置线程私有变量的流程如图 23所示。

    图 23 设置线程私有变量

  22. 输入

    在调用API_ThreadVarSet函数时,我们需要输入线程ID以及私有变量地址。具体实现如程序清单 210所示。

    程序清单 210 设置线程私有变量的值输入


    ULONG API_ThreadVarSet (LW_OBJECT_HANDLEulId, ULONG *pulAddr, ULONGulValue)

    {

    REGISTERUINT16usIndex;

    REGISTERPLW_CLASS_TCBptcbCur;

    REGISTERPLW_CLASS_TCBptcb;

    REGISTERPLW_LIST_LINEplineVar;

    REGISTERPLW_CLASS_THREADVARpthreadvar;

    usIndex = _ObjectGetIndex(ulId);

    }

  23. 判断是否在中断

    获取CPU当前的中断嵌套值,判断是否在中断,该函数不能在中断中调用。

  24. 检查线程的有效性

    在进入内核态之前,检查当前线程ID是否符合线程规范,以判断该线程是否有效。

  25. 进入内核态

    进入内核态进行操作。

  26. 检查线程的有效性

    进入内核态以后,通过检查对应的线程TCB地址表来判断线程是否有效。

  27. 查找私有数据控制块同时判断是否为当前线程

    通过全局变量私有化线表查找控制块,然后检查线程控制块的相关信息判断是否为当前线程,是则赋值,不是则将值保存。具体实现如程序清单 211所示。

    程序清单 211 查找控制块并判断是否为当前线程


    ptcb = _K_ptcbTCBIdTable[usIndex];

    for (plineVar = ptcb->TCB_plinePrivateVars; /* 查找 */

    plineVar != LW_NULL;

    plineVar = _list_line_get_next(plineVar)) {

    pthreadvar = _LIST_ENTRY(plineVar, LW_CLASS_THREADVAR, PRIVATEVAR_lineVarList);

    if (pthreadvar->PRIVATEVAR_pulAddress == pulAddr) {

    if (ptcb == ptcbCur) { /* 是不是当前线程 */

    *pulAddr = ulValue;

    } else {

    pthreadvar->PRIVATEVAR_ulValueSave = ulValue;

    }

  28. 退出内核

    退出内核态。

  29. 获得线程私有变量值

    获得线程私有变量值的流程如图 24所示。

    图 24 获得线程私有变量值

  30. 输入

    我们需要输入线程ID以及私有变量地址。具体实现如程序清单 212所示。

    程序清单 212 私有线程变量值获取


    ULONG API_ThreadVarGet (LW_OBJECT_HANDLE ulId, ULONG *pulAddr)

    {

    REGISTERUINT16usIndex;

    REGISTERPLW_CLASS_TCBptcbCur;

    REGISTERPLW_CLASS_TCBptcb;

    REGISTERPLW_LIST_LINEplineVar;

    REGISTERULONGulValue;

    REGISTERPLW_CLASS_THREADVARpthreadvar;

    usIndex = _ObjectGetIndex(ulId);

    }

  31. 判断是否在中断

    获取CPU当前的中断嵌套值,判断是否在中断,该函数不能在中断中调用。

  32. 检查线程的有效性

    在进入内核态之前,检查当前线程ID是否符合线程规范,以判断该线程是否有效。

  33. 进入内核态

    进入内核态进行操作。

  34. 检查线程的有效性

    进入内核态以后,通过检查对应的线程TCB地址表来判断线程是否有效。

  35. 查找私有数据控制块同时判断是否为当前线程

    通过全局变量私有化线表查找控制块,然后检查线程控制块的相关信息判断是否为当前线程,是获取线程私有变量的值,不是则获得保存的变量值。实现如程序清单 213所示。

    程序清单 213 查找控制块并判断是否为当前线程


    ptcb = _K_ptcbTCBIdTable[usIndex];

    for (plineVar = ptcb->TCB_plinePrivateVars; /* 查找 */

    plineVar != LW_NULL;

    plineVar = _list_line_get_next(plineVar)) {

    pthreadvar = _LIST_ENTRY(plineVar, LW_CLASS_THREADVAR, PRIVATEVAR_lineVarList);

    if (pthreadvar->PRIVATEVAR_pulAddress == pulAddr) {

    if (ptcb == ptcbCur) { /* 是不是当前线程 */

    ulValue = *pulAddr;

    } else {

    ulValue = pthreadvar->PRIVATEVAR_ulValueSave;

    }

  36. 退出内核

    退出内核态。

  37. 总结

    线程私有数据是线程上下文记录的一个unsigned long型数值(可以将其看成一个指向一个全局变量的指针)和保存全局变量值的临时变量(用来恢复线程上下文中全局变量的值)。每次线程被调入处理器时,系统根据该指针自动从线程上下文装入全局变量的值。相应地,任务被调出处理器时,系统根据该指针自动将全局变量的值保存到线程的上下文。

  38. 参考文献

    1.《SylixOS应用开发手册》

    2. SylixOS源码

时间: 2024-07-31 14:27:06

SylixOS线程私有数据浅析的相关文章

12.6 线程私有数据

线程私有数据是一种用于存储和获取与特定线程相关联数据的机制,称为线程特定的或者是线程私有的,是因为我们希望每个线程都可以独立访问其独有的数据,而不用担心与其他线程的同步访问问题. 许多人费力实现了促进进程数据以及属性贡献的线程模型,那么为什么还有人想要实现一个接口,在这样一个模型中防止共享呢?有如下两点原因: 首先,有些时候我们需要以线程为基础维护一些数据,因为没有任何机制可以保证线程ID总是比较小的,且是连续的整数,因此我们不能简单地将每一个线程的私有数据分配为一个数组,然后使用线程ID作为索

【C/C++多线程编程之十】pthread线程私有数据

多线程编程之线程私有数据 Pthread是 POSIX threads 的简称,是POSIX的线程标准.  线程同步从互斥量[C/C++多线程编程之六]pthread互斥量,信号量[C/C++多线程编程之七]pthread信号量,条件变量[C/C++多线程编程之八]pthread条件变量,读写锁[C/C++多线程编程之九]pthread读写锁,多线程的同步机制已经有了清晰深入的探究,多线程编程的精髓所在,需要深入理解.        线程私有数据TSD(Thread-specific Data)

线程私有数据

在多线程程序中,经常要用全局变量来实现多个函数间的数据共享.由于数据空间是共享的,因此全局变量也为所有线程共有. 测试代码如下: [cpp] view plaincopy #include <stdio.h> #include <pthread.h> #include <unistd.h> #include <stdlib.h> int key = 100; //全局变量 void *helloworld_one(void *arg) { printf(&q

pthread_getspecific()--读线程私有数据|pthread_setspecific()--写线程私有数据

原型: #include <pthread.h> void *pthread_getspecific(pthread_key_t key); int pthread_setspecific(pthread_key_t key, const void *value); 说明: TSD 的读写都通过上面两个专门的 Posix Thread 函数进行. 函数 pthread_setspecific() 将 pointer 的值 (不是锁指的内容) 与key 相关联. 函数 pthread_getsp

posix多线程--线程私有数据

1.当多个线程共享一个变量时,将该变量定义为静态或外部变量,使用互斥量确保共享变量的安全访问.如果每个线程都需要一个私有变量值,则该值成为线程的私有数据.程序创建一个键,每个线程独立地设定或得到自己的键值,各线程间私有数据互不影响. 2.建立线程私有数据int pthread_key_create(pthread_key_t *key,void (*destructor)(void *));int pthread_key_delete(pthread_key_t key);int pthread

Linux系统编程——线程私有数据

在多线程程序中,经常要用全局变量来实现多个函数间的数据共享.由于数据空间是共享的,因此全局变量也为所有线程共有. 测试代码如下: #include <stdio.h> #include <pthread.h> #include <unistd.h> #include <stdlib.h> int key = 100; //全局变量 void *helloworld_one(void *arg) { printf("the message is %s

线程私有数据和pthread_once

#include <stdio.h> #include <pthread.h> pthread_key_t key; pthread_once_t ponce = PTHREAD_ONCE_INIT; void ronce(){ printf("%s\n", "ronce"); } void *thread1(){ pthread_setspecific(key, "thread1"); printf("%s\n

操作线程私有数据的函数主要有4个:

在线程内部,线程私有数据可以被各个函数访问到,但它对其他线程是屏蔽的. 使用线程数据时,首先要为每个线程数据创建一个相关联的键.在各个线程内部,都使用这个公用的键来指代线程数据,但是在不同的线程中,这个键代表的数据是不同的.也就是说,key一旦被创建,所有线程都可以访问它,但各线程可根据自己的需要往key中填入不同的值.这相当于提供了一个同名而不同值的全局变量,一键多值. 操作线程私有数据的函数主要有4个:pthread_key_create(创建一个键)pthread_setspecific(

SylixOS线程堆栈大小浅析

目录 1. SylixOS线程.线程栈介绍    1 1.1    线程的介绍    1 1.2    线程栈的介绍    1 2. SylixOS线程栈大小的分配    1 2.1    线程.线程栈相关属性的设置    2 2.2    线程栈大小    2 2.3    线程堆栈警戒区    3 3. 总结    5 4. 参考资料    5 SylixOS线程.线程栈介绍 SylixOS是多线程操作系统,系统能够同时创建多个线程,具体最大线程数量取决于系统内存的大小以及编译SylixOS