文章目录

  • 前言
  • Threads - (线程)
    • 1. Lifecycle - (生命周期)
      • 1.1 Thread Creation - (线程创建)
      • 1.2 Thread Termination - (线程终止)
      • 1.3 Thread Aborting - (线程中止)
      • 1.4 Thread Suspension - (线程挂起/暂停)
    • 2. Thread States - (线程状态)
    • 3. Thread Stack objects - (线程堆栈对象)
      • 3.1 Kernel-only Stacks - (仅内核堆栈)
      • 3.2 Thread stacks - (线程堆栈)
    • 4. Thread Priorities - (线程优先级)
    • 5. Thread Options - (线程选项)
    • 6. Thread Custom Data - (线程自定义数据)
    • 7. Implementation - (实现)
      • 7.1 Spawning a Thread - (创建线程)
      • 7.2 Dropping Permissions - (降低权限)
      • 7.3 Terminating a Thread - (终止线程)
    • 8. Runtime Statistics - (运行时统计信息)
    • 9. Suggested Uses - (建议用途)
    • 10. Configuration Options - (配置选项)
    • 11. API Reference - (API 参考)
  • 参考链接

本笔记基于 Zephyr 版本 2.6.0-rc2

前言

本人正在学习 Zephyr,一个可移植性较强,可以兼容多种开发板及物联网设备的操作系统,如果你感兴趣,可以点此查看我的 学习笔记总述 进行了解!

Threads - (线程)

线程是内核对象,用于太长或太复杂而无法由 ISR 执行的应用程序处理。

应用程序可以定义任意数量的线程 (仅受可用 RAM 限制),每个线程都由生成该线程时分配的线程 ID 引用。

线程具有以下的属性:

  • A stack area(堆栈区域),其是用于所述线程的堆栈的存储器区域,可以调整堆栈区域的大小以符合线程处理的实际需求。存在特殊的宏来创建和使用堆栈存储区。
  • A thread control block(线程控制块),用于线程的元数据的专用内核簿记。 这是类型 k_thread 的实例。
  • An entry point function(入口函数),在线程启动时调用。最多可以将 3 个参数值传递给此函数。
  • A scheduling priority(调度优先级),指示内核的调度如何分配CPU时间线程。参考 Scheduling
  • A set of thread options(线程选项),允许线程在特定情况下接受内核的特殊处理。参考 Thread Options
  • A start delay(启动延迟),它指定内核在启动线程之前应等待的时间。
  • An execution mode(执行模式),可以是超级用户模式(supervisor mode)或用户模式(user mode)。 默认情况下,线程在超级用户模式下运行,并允许访问特权 CPU 指令,整个内存地址空间和外围设备。 用户模式线程的特权减少了。 这取决于 CONFIG_USERSPACE 选项。 请参阅 User mode(用户模式)。

1. Lifecycle - (生命周期)

1.1 Thread Creation - (线程创建)

线程在被使用之前必须先被创建,内核初始化线程控制块以及堆栈部分的一端。 线程栈的其余部分通常未初始化。

可以指示内核通过指定超时值来延迟线程的执行,例如,允许线程使用的设备硬件变得可用。

指定启动延迟 K_NO_WAIT 会指示内核立即启动线程执行。

内核允许在线程开始执行之前取消延迟的启动。但是如果线程已经启动,则取消请求无效。延迟启动成功取消的线程,必须重新生成后才能使用。

1.2 Thread Termination - (线程终止)

线程一旦启动,通常将永远执行。但是,线程可以通过从其入口点函数返回来同步结束其执行。这称为终止。

终止线程负责在返回之前释放其可能拥有的任何共享资源 (例如互斥体和动态分配的内存),因为内核不会自动回收它们

在某些情况下,一个线程可能要休眠,直到另一个线程终止。可以使用 k_thread_join() API 来完成。这将阻塞调用线程,直到超时到期,目标线程自行退出或目标线程异常中止(由于 k_thread_abort() 调用或触发致命错误)。

线程终止后,内核保证不会使用线程结构(k_thread)。 然后可以将这种结构的内存重新用于任何目的,包括产生新线程。

请注意,线程必须完全终止,这会在竞争状态下出现竞争情况,在该竞争情况下,一个线程自己的逻辑会发出信号完成信号,该信号在内核处理完成之前会被另一个线程看到。 在正常情况下,应用程序代码应使用 k_thread_join()k_thread_abort() 来同步线程终止状态,而不应依赖于来自应用程序逻辑内部的信号。

原文:
Once a thread has terminated, the kernel guarantees that no use will be made of the thread struct. The memory of such a struct can then be re-used for any purpose, including spawning a new thread. Note that the thread must be fully terminated, which presents race conditions where a thread’s own logic signals completion which is seen by another thread before the kernel processing is complete. Under normal circumstances, application code should use k_thread_join() or k_thread_abort() to synchronize on thread termination state and not rely on signaling from within application logic.

1.3 Thread Aborting - (线程中止)

线程可以通过 中止 异步结束其执行。如果线程触发致命错误条件 (例如,取消引用空指针),内核会自动中止线程

线程也可以通过另一个线程 (或由其自身) 调用 k_thread_abort() 被中止。但是,通常最好是发信号通知线程适当地终止自身,而不是中止该线程。

线程终止 一样,内核不会回收异常中止线程所拥有的共享资源

1.4 Thread Suspension - (线程挂起/暂停)

如果线程被挂起,可以无限期地阻止其执行。该函数 k_thread_suspend() 可用于挂起任何线程,包括已经调用的线程。一旦挂起,就无法调度线程,直到另一个线程调用 k_thread_resume() 以取消挂起。

挂起已被挂起的线程不会产生任何其他影响

Note: 线程可以在指定的时间段内阻止自己执行,通过使用 k_sleep() 。 但是,这与挂起线程不同,因为达到时间限制时,休眠线程会自动执行。

2. Thread States - (线程状态)

没有任何妨碍其执行的因素的线程被视为准备就绪态 (ready),并且有资格被选择为当前线程。

具有一个或多个阻止其执行的因素的线程被视为未就绪态 (unready),并且不能被选择为当前线程。

以下的因素使一个线程处于 unready 状态:

  • 线程未被启动
  • 线程正在等待内核对象完成操作 (例如:该线程正在使用不可用的信号量)
  • 线程正在等待超时发生
  • 线程已被挂起
  • 线程被终止或中止

线程状态切换图:

3. Thread Stack objects - (线程堆栈对象)

每个线程都需要自己的堆栈缓冲区,CPU 才能推送内容。根据配置,必须满足几个约束条件:

  • 可能需要为内存管理结构保留额外的内存
  • 如果启用了基于保护的堆栈溢出检测,则必须在堆栈缓冲区之前立即设置一个小的写保护存储器管理区域,以捕获溢出。
  • 如果启用了用户空间,则必须保留一个单独的固定大小的特权提升堆栈,以用作处理系统调用的专用内核堆栈。
  • 如果启用了用户空间,则线程的堆栈缓冲区的大小必须适当调整并对齐,以便可以对内存保护区域进行编程以使其完全适合。

对齐约束可能非常严格,例如,某些 MPU 要求其区域的大小为 2 的幂,并且要与其自身的大小对齐。

因此,可移植代码无法简单地将任意字符缓冲区传递给 k_thread_create()。存在特殊的宏以实例化堆栈,并以K_KERNEL_STACK 和前缀 K_THREAD_STACK

3.1 Kernel-only Stacks - (仅内核堆栈)

如果已知线程永远不会在用户模式下运行,或者堆栈用于特殊内容 (例如处理中断),则最好使用 K_KERNEL_STACK 宏定义堆栈。

这些堆栈可节省内存,因为无需对MPU区域进行编程即可覆盖堆栈缓冲区本身,并且内核无需为特权提升堆栈或仅与用户模式线程相关的内存管理数据结构保留额外的空间

如果 CONFIG_USERSPACE 未启用,则 K_THREAD_STACK 宏的作用与 K_KERNEL_STACK 宏相同。

3.2 Thread stacks - (线程堆栈)

如果已知某个堆栈需要托管用户线程,或者无法确定该线程,请使用 K_THREAD_STACK 宏定义该堆栈。这可能会使用更多的内存,但是堆栈对象适合于承载用户线程。

如果 CONFIG_USERSPACE 未启用,则 K_THREAD_STACK 宏的作用与 K_KERNEL_STACK 宏相同。

4. Thread Priorities - (线程优先级)

线程的优先级是整数值,可以为负或非负。数字上较低的优先级优先于数字上较高的值。

例如,调度程序为优先级为 4 的线程 A 高于优先级为 7 的线程 B 的优先级。
同样,优先级为 -2 的线程 C 的优先级高于线程 A 和线程 B。

调度程序根据每个线程的优先级来区分两类线程:

  • 一个协作线程的优先级值为负。 一旦成为当前线程,协作线程将保留为当前线程,直到它执行操作使它变为未就绪态 (unready)。
  • 一个可抢占线程具有非负的优先级值,一旦成为当前线程,如果协作线程或具有更高或同等优先级的可抢占线程准备就绪,则可随时替换可抢占线程。

启动线程后,可以向上或向下更改线程的初始优先级值。因此,通过更改优先级,可抢占线程有可能变为协作线程,反之亦然。

内核几乎支持无限数量的线程优先级。配置选项,CONFIG_NUM_COOP_PRIORITIESCONFIG_NUM_PREEMPT_PRIORITIES 指定每种线程类的优先级级别数,从而产生以下可用的优先级范围:

  • 协作线程:(-CONFIG_NUM_COOP_PRIORITIES) to -1
  • 抢占线程:0 to (CONFIG_NUM_PREEMPT_PRIORITIES - 1)

5. Thread Options - (线程选项)

内核支持一小组线程选项,这些选项允许线程在特定情况下获得特殊待遇。生成线程时,将指定与线程关联的选项集。

不需要任何线程选项的线程的选项值为零。需要线程选项的线程通过名称指定,如果需要多个选项,则使用 | 字符作为分隔符。(即使用按位或运算符组合选项)

支持的线程选项:

  • K_ESSENTIAL:此选项将线程标记为必要线程(essential thread),这指示内核将线程的终止或中止视为致命的系统错误。默认情况下,该线程不被视为必不可少的线程。
  • K_SSE_REGS:此特定于x86的选项表示线程使用CPU的SSE寄存器。 另请参阅 K_FP_REGS。默认情况下,内核在调度线程时不尝试保存和恢复该寄存器的内容。
  • K_FP_REGS:此选项表明线程使用 CPU 的浮点寄存器(CPU’s floating point registers)。这指示内核在调度线程时采取其他步骤来保存和恢复这些寄存器的内容。(有关更多信息,请参见浮点服务(Floating Point Services)) 默认情况下,内核在调度线程时不尝试保存和恢复该寄存器的内容。
  • K_USER:如果CONFIG_USERSPACE 启用,则该线程将在用户模式下创建,并具有减少的特权。请参阅用户模式(User Mode)。否则,此标志不执行任何操作。
  • K_INHERIT_PERMS:如果CONFIG_USERSPACE 启用,则该线程将继承父线程具有的所有内核对象许可权,但父线程对象除外。请参阅用户模式(User Mode)

6. Thread Custom Data - (线程自定义数据)

每个线程都有一个 32 位的自定义数据区,只能由线程本身访问,并且可以由应用程序出于其选择的任何目的使用。线程的默认自定义数据值为零。

Note: 自定义数据支持不适用于 ISR,因为它们在单个共享的内核中断处理上下文中运行。

默认情况下,线程自定义数据支持是禁用的。配置选项 CONFIG_THREAD_CUSTOM_DATA 可用于启用支持。

k_thread_custom_data_set()k_thread_custom_data_get() 函数分别用于写入和读出的线程的自定义数据。一个线程只能访问自己的自定义数据,而不能访问另一个线程的自定义数据。

以下代码使用自定义数据功能来记录每个线程调用特定例程的次数。
Note: 显然,只有一个例程可以使用此技术,因为它垄断了自定义数据功能的使用。

int call_tracking_routine(void)
{uint32_t call_count;if (k_is_in_isr()) {/* ignore any call made by an ISR */} else {call_count = (uint32_t)k_thread_custom_data_get();call_count++;k_thread_custom_data_set((void *)call_count);}/* do rest of routine's processing */...
}

使用线程自定义数据,通过将自定义数据用作指向线程拥有的数据结构的指针,可以使例程访问线程特定的信息。

7. Implementation - (实现)

7.1 Spawning a Thread - (创建线程)

通过定义线程的堆栈区域和线程控制块,然后调用 k_thread_create() 来生成线程。

必须使用 K_THREAD_STACK_DEFINEK_KERNEL_STACK_DEFINE 定义堆栈区域,以确保在内存中正确设置了堆栈区域。

堆栈的大小参数必须是以下三个值之一:

  • 原始请求的堆栈大小传递给 K_THREAD_STACKK_KERNEL_STACK 堆栈实例化宏系列。
  • 对于使用 K_THREAD_STACK 宏系列定义的堆栈对象,该对象的返回值 K_THREAD_STACK_SIZEOF()
  • 对于使用 K_KERNEL_STACK 宏系列定义的堆栈对象,该对象的返回值 K_KERNEL_STACK_SIZEOF()

线程产生函数返回其线程 ID,该 ID 可用于引用线程。

示例: 以下代码产生一个立即启动的线程

#define MY_STACK_SIZE 500
#define MY_PRIORITY 5extern void my_entry_point(void *, void *, void *);K_THREAD_STACK_DEFINE(my_stack_area, MY_STACK_SIZE);
struct k_thread my_thread_data;k_tid_t my_tid = k_thread_create(&my_thread_data, my_stack_area,K_THREAD_STACK_SIZEOF(my_stack_area),my_entry_point,NULL, NULL, NULL,MY_PRIORITY, 0, K_NO_WAIT);

或者,可以在编译时通过调用 K_THREAD_DEFINE 来声明线程。该宏会自动定义堆栈区域,控制块和线程 ID 变量。上面的代码可改为下面这种方式:

#define MY_STACK_SIZE 500
#define MY_PRIORITY 5extern void my_entry_point(void *, void *, void *);K_THREAD_DEFINE(my_tid, MY_STACK_SIZE,my_entry_point, NULL, NULL, NULL,MY_PRIORITY, 0, 0);

Note: k_thread_create() 的延时参数是一个 k_timeout_t 值,因此 K_NO_WAIT 意味着立即启动线程。K_THREAD_DEFINE 的对应参数是持续时间(以整数毫秒为单位),因此等效参数为 0。

User Mode Constraints - (用户模式限制)

本部分仅在 CONFIG_USERSPACE 启用后才适用,并且一个用户线程尝试创建一个新线程。该 k_thread_create() API 仍在使用,但必须满足其他约束条件,否则调用线程将被终止:

  • 调用线程必须在子线程和堆栈参数上均具有授予的权限;两者都由内核作为内核对象进行跟踪。
  • 子线程和堆栈对象必须处于未初始化状态,即当前未运行,并且未使用堆栈内存。
  • 传入的堆栈大小参数必须等于或小于声明时的堆栈对象范围。
  • K_USER 必须使用该选项,因为用户线程只能创建其他用户线程。
  • K_ESSENTIAL 该选项不得被使用,否则用户线程可能不会被视为必要线程(essential threads)。
  • 子线程的优先级必须是有效的优先级值,并且等于或小于父线程的优先级。

7.2 Dropping Permissions - (降低权限)

如果启用了 CONFIG_USERSPACE,则在超级用户模式下运行的线程可以使用 k_thread_user_mode_enter() API 向用户模式进行单向转换。它将重置线程的堆栈内存并将其清零。该线程将被标记为非必需(non-essential)。

7.3 Terminating a Thread - (终止线程)

线程通过从其入口点函数返回而终止自身。

以下代码说明了线程可以终止的方式:

void my_entry_point(int unused1, int unused2, int unused3)
{while (1) {...if (<some condition>) {return; /* thread terminates from mid-entry point function */}...}/* thread terminates at end of entry point function */
}

如果启用了 CONFIG_USERSPACE,则中止线程将另外将该线程和堆栈对象标记为未初始化,以便可以重新使用它们。

8. Runtime Statistics - (运行时统计信息)

如果 CONFIG_THREAD_RUNTIME_STATS 启用,则可以收集和检索线程运行时统计信息 ,例如,线程的执行周期总数。

默认情况下,使用默认内核计时器来收集运行时统计信息。对于某些体系结构,SoC 或开发板,有些计时器可以通过计时功能提供更高的分辨率。这些计时器的使用可以通过启用 CONFIG_THREAD_RUNTIME_STATS_USE_TIMING_FUNCTIONS

例如:

k_thread_runtime_stats_t rt_stats_thread;k_thread_runtime_stats_get(k_current_get(), &rt_stats_thread);printk("Cycles: %llu\n", rt_stats_thread.execution_cycles);

9. Suggested Uses - (建议用途)

使用线程来处理 ISR 无法处理的进程。

使用单独的线程来处理可以并行执行的逻辑上不同的处理操作。

10. Configuration Options - (配置选项)

相关配置选项:

  • CONFIG_MAIN_THREAD_PRIORITY
  • CONFIG_MAIN_STACK_SIZE
  • CONFIG_IDLE_STACK_SIZE
  • CONFIG_THREAD_CUSTOM_DATA
  • CONFIG_NUM_COOP_PRIORITIES
  • CONFIG_NUM_PREEMPT_PRIORITIES
  • CONFIG_TIMESLICING
  • CONFIG_TIMESLICE_SIZE
  • CONFIG_TIMESLICE_PRIORITY
  • CONFIG_USERSPACE

11. API Reference - (API 参考)

关于线程的一些 API 函数及宏

参考链接

https://docs.zephyrproject.org/latest/reference/kernel/threads/index.html

Zephyr RTOS -- Threads相关推荐

  1. 在Zephyr RTOS上实现一个轮询系统

    近日在设计一个服务系统,使用的是RTOS,因为是实时操作系统,所以有时候需要进行切片,需要跑不同的任务,所以需要让它多线程起来,这里是我实现的一些思路,分享给大家,在实时RTOS上如何设计一款轮询系统 ...

  2. Zephyr Power Management Subsystem详细介绍

    目录 简介 前景补充 SOC Interface IDLE Thread * Tickless Idle Power Gating Power State Device Runtime Power M ...

  3. 桌面版应用_【Nordic博文分享系列】开发你的第一个NCS(Zephyr)应用程序

    Nordic有2套并存的SDK 1.老的nRF5 SDK 2.新的NCS SDK 两套SDK相互独立, 大家选择其中一套进行开发即可. 一般而言,如果你选择的芯片是nRF51或者nRF52系列,那么推 ...

  4. 基于Zephyr在微型MCU上使用Tensor Flow Lite Micro做图像分类

    首先需要保证你已经拥有了一个图像分类的模型. 其次我们需要Zephyr RTOS. 这些可以参考如下文章: 基于Stm32F746g_disg平台下移植zephry使用TinyML预测模型_17岁bo ...

  5. 使用VScode开发ESP32,PlatformIO开发ESP32

    arduino IDE安装开发板库 安装arduino IDE 首先下载arduino IDE并安装.在官网下载即可.如果官网下载慢,可以找第三方资源下载. 下载开发板库 安装后进入"文件& ...

  6. 使用VScode开发ESP8266,PlatformIO开发ESP8266

    arduino IDE安装开发板库 安装arduino IDE 首先下载arduino IDE并安装.在官网下载即可.如果官网下载慢,可以找第三方资源下载. 下载开发板库 安装后进入"文件& ...

  7. 微软披露25个漏洞 BadAlloc,至少影响数十亿智能设备

     聚焦源代码安全,网罗国内外最新资讯! 编译:奇安信代码卫士 微软研究团队发现了25个漏洞,影响大量智能物联网设备和工业设备.这些漏洞被统称为 "BadAlloc". 这些漏洞是由 ...

  8. 物联网的几大开源操作系统

    在过去的十年间,大多数新型开源操作系统已从移动市场转向物联网市场.本文介绍了面向物联网的许多新型开源操作系统.我们之前的文章介绍了开源物联网框架,以及面向物联网和消费者智能家居设备的Linux和开源开 ...

  9. AI模型训练部署:在CSK6芯片上运行AI模型

    前言 在<LNN工具链详解:在CSK6上运行你自己的AI算法>中通过LNN工具链获得了一个算法模型,并在PC上使用test_thinker进行了推理运行,最后如何在CSK6芯片上运行输出的 ...

最新文章

  1. php去掉两个数组重复数据,php两个数组怎么去除重复
  2. java 反射私有变量赋值_通过反射,操作私有成员变量(取/赋值),调用私有方法...
  3. C语言const 关键字
  4. eclipse mat 打开dump文件,明明大小1G,打开后却只有不到100M.其他的去哪了
  5. 大型网站的 HTTPS 实践(二)——HTTPS 对性能的影响
  6. Java中字符串的几个实例
  7. 《.Ne框架程序设计》随记(3)
  8. 3D点云模型下载网站
  9. 支付宝小程序复制到剪贴板
  10. 开关电源中输出电容的ESR会影响负载的动态响应
  11. 从零开始的ZYNQ学习(基于矿卡EBAZ4205)(二)
  12. 为什么感知机不能表示异或?
  13. redis分布式锁的安全性及与zookeeper的对比
  14. C51 - DS18B20
  15. 竟然有人把VSCode玩成了IDEA的效果,有点东西
  16. 十年前他在马王堆送快递,如今当上航空主管,拯救客户亿元大单
  17. 【开心一刻】小学生“0”分作文走红,网友感叹“太有才”:是我就给100分
  18. 计算机技术对艺术设计的影响,计算机技术对现代艺术设计的影响
  19. 陆教授浅谈:5G毫米波手机天线技术的发展现状和未来的应用场景
  20. 最新显卡功耗表和官方功耗表

热门文章

  1. SpringBoot2.X优雅停机
  2. matlab实验二答案 福州大学,2021知到网课答案_分析化学福州大学_章节测试答案...
  3. python pip如何upgrade
  4. 关于ROG魔霸3不识别西部数据SN750问题
  5. 油烟净化器的过滤网有什么用呢?不清洗可以吗
  6. 变频器MATLAB仿真模型(前端采用二极管整流,含有进线电抗器,预充电模块,母校电容与均压电阻
  7. 为什么不用python做大数据_光环大数据告诉你为什么说:人生苦短,我用Python
  8. linux通过修改时间排序,linux按修改时间排序
  9. 抖音多年以后用计算机,那是一个阳光明媚的早上,我做出了在多年以后都会后悔的决定。我要去拔智齿。我去了医院 - 糗事百科...
  10. 5.概念(maven,ssm,springMvc,spring,自定义注解,二级缓存,范式,事务,mysql,线程池,map,hashmap,redis,饿汉,懒汉)