Zephyr RTOS -- Thread -2

文章目录

System Threads - (系统线程)

系统线程是内核在系统初始化期间自动生成的线程。

内核产生以下系统线程:

  • Main thread:
    该线程执行内核初始化,然后调用应用程序的 main() 函数 (如果已定义) 。

    默认情况下,主线程使用配置的最高可抢占线程优先级 (即 0)。如果内核未配置为支持可抢占线程,则主线程将使用最低配置的协作线程优先级 (即 -1) 。

    主线程在执行内核初始化或执行应用程序的 main() 功能时是必不可少的线程 (essential thread) 。这意味着如果线程中止,则会引发致命的系统错误。如果 main() 未定义,或者执行后返回正常,则主线程将正常终止,并且不会引发任何错误。

  • Idle thread:
    当系统没有其他工作要做时,将执行此线程。如果可能的话,空闲线程会激活电路板的电源管理支持以节省电源;否则,空闲线程仅执行 “不执行任何操作(do nothing)” 循环。只要系统正在运行,空闲线程就一直存在,并且永远不会终止。

    空闲线程始终使用最低配置的线程优先级。如果这使它成为协作线程,则空闲线程会反复调用 CPU,以允许应用程序的其他线程在需要时运行。

    空闲线程是必不可少的线程 (essential thread) ,这意味着如果线程中止,则会引发致命的系统错误。

根据应用程序指定的内核和主板配置选项,还可以生成其他系统线程。例如,启用系统工作队列会产生一个系统线程,该线程为提交给它的工作项提供服务。(请参阅 Workqueue Threads)

Implementation - (实现)

Writing a main() function - (编写 main() 函数)

内核初始化完成后,应用程序提供的 main() 函数将开始执行。内核不将任何参数传递给该函数。

下面的代码概述了一个简单的 main() 函数。实际应用程序使用的函数可以根据需要任意复杂化。

void main(void)
{
    /* initialize a semaphore */
    ...

    /* register an ISR that gives the semaphore */
    ...

    /* monitor the semaphore forever */
    while (1) {
        /* wait for the semaphore to be given by the ISR */
        ...
        /* do whatever processing is now needed */
        ...
    }
}

Suggested Uses - (建议用途)

在只需要一个线程的应用程序中,使用主线程来执行基于线程的处理,而不是定义附加的特定于应用程序的线程。

Workqueue Threads - (工作队列线程)

工作队列是一个内核对象,它使用专用线程以先进先出的方式处理工作项。每个工作项都是通过调用工作项指定的函数来处理的。工作队列通常由ISR或高优先级线程使用,以将非紧急处理移交给低优先级线程,这样就不会影响对时间敏感的处理。

可以定义任意数量的工作队列 (仅受可用 RAM 的限制)。每个工作队列由其内存地址引用。

工作队列具有以下关键属性:

  • 队列中的工作项都是被添加进去、但是还没被处理的
  • 队列中的工作项是通过一个线程处理的,这个线程的优先级是可配置的,我们可以把它配置成协作式线程或者抢占式线程

无论工作队列线程优先级如何,工作队列线程将在每个提交的工作项之间产生,以防止协作工作队列耗尽其他线程。

必须先初始化工作队列,然后才能使用它。这将其队列设置为空并生成工作队列的线程。线程永远运行,但当没有可用的工作项时进入休眠状态。

Work Item Lifecycle - (工作项生命周期)

可以定义任何数量的工作项 (work items),每个工作项均由其内存地址引用。

为工作项分配一个处理程序函数 (handler function),该函数是在处理工作项时由工作队列的线程执行的函数。此函数接受一个参数,即工作项本身的地址。工作项还维护有关其状态的信息。

必须先初始化工作项,然后才能使用它。这将记录工作项的处理程序功能并将其标记为未挂起。

可以通过 ISR 或线程将工作项提交到工作队列中,从而使该工作项排队 ( K_WORK_QUEUED) 。提交工作项会将工作项追加到工作队列的队列中。工作队列的线程处理完队列中所有先前的工作项后,该线程将从队列中删除下一个工作项并调用工作项的处理函数。根据工作队列的线程的调度优先级以及队列中其他项目所需的工作,排队的工作项目可能会得到快速处理,或者可能会在队列中保留很长时间。

一个可延迟的工作项可以被调度到一个工作队列 (K_WORK_DELAYED);参考 Delayable Work.

工作项在工作队列上运行时将 运行 (K_WORK_RUNNING),如果它在线程请求取消之前开始运行,也可能被取消 (K_WORK_CANCELING)。

一个工作项可以处于多个状态。例如,它可以是:

  • 在队列中运行
  • 标记为取消 (因为线程使用 k_work_cancel_sync() 来等待工作项完成)
  • 排队再次在同一队列上运行
  • 计划提交到一个 (可能不同的) 队列

同时所有 (all simultaneously)。处于上述任何一种状态的工作项是 pending(k_work_is_pending()) 或 busy (k_work_busy_get()) 状态。

工作项通常初始化一次,然后在需要执行工作时提交到特定的工作队列。如果 ISR 或线程试图提交已经排队的工作项,则工作项不受影响;工作项保持在工作队列队列中的当前位置,并且工作只执行一次。

允许处理程序函数将其工作项参数重新提交给工作队列,因为此时工作项不再处于排队状态。这允许处理程序分阶段执行工作,而不会过分延迟工作队列中其他工作项的处理。

Important:
在未被工作队列线程处理之前,不能更改暂挂的工作项。这意味着工作项忙碌时不能重新初始化。此外,在处理程序函数完成执行之前,工作项的处理程序函数执行其工作所需的任何附加信息都不能更改。

Delayable Work - (可延迟的工作)

一个 ISR 或一个线程可能需要安排一个只在指定的一段时间之后才处理的工作项,而不是立即处理。这可以通过 scheduling 一个 delayable work item 在将来提交到一个工作队列来完成。

一个delayable work包含一个标准的工作项,但是添加了一些字段,这些字段记录了应该提交该项的时间和地点。

delayable work 与标准工作项的初始化方式及调度到工作队列的方式类似,只是使用的 api 不同。当发出调度请求时,内核会启动一个超时机制,在指定的延时过后触发,之后和标准工作项的方式一样处理。

注意,用于delayable work 的工作处理程序仍然接收到一个指向底层 non-delayable work 结构的指针,该结构不能从 k_work_delayable 公开访问。要访问包含可延迟工作对象的对象,请习惯使用这个用法:

static void work_handler(struct k_work *work)
{
        struct k_work_delayable *dwork = k_work_delayable_from_work(work);
        struct work_context *ctx = CONTAINER_OF(dwork, struct work_context,
                                                timed_work);
        ...

Triggered Work - (触发的工作)

k_work_poll_submit() 接口调度一个被触发的工作项以响应 poll event (请参见Polling API),当受监视的资源可用或引发 poll signal 或发生超时时,该接口将调用用户定义的函数。与 k_poll() 相比,被触发的工作不需要专门的线程等待或主动轮询轮询事件(poll event)。

触发的工作项是具有以下添加属性的标准工作项:

  • 指针:指向一系列轮询事件的指针,这些轮询事件将触发工作项向工作队列的提交。
  • 数组大小:包含轮询事件 (poll events) 的数组的大小。

delayable work 一样,triggered work 也是用与标准工作项类似的初始化方式及调度到工作队列。发出提交请求后,内核开始观察由轮询事件指定的内核对象。一旦观察到的内核对象至少有一个更改状态,工作项就会提交到指定的工作队列中,在那里它将保持队列状态,直到以标准方式处理它。

Important:
被触发的工作项以及投票事件的引用数组必须是有效的,并且对于一个完整的被触发的工作项生命周期 (从提交到工作项执行或取消) 不能被修改。

还在等待轮询事件的 triggered work 可以被 ISR 或线程取消,在这种情况下,内核将将停止等待轮询事件,并且指定的工作项不被执行,否则无法取消。

System Workqueue - (系统工作队列)

内核定义了一个称为系统工作队列 (system workqueue) 的工作队列,该工作队列可用于需要工作队列支持的任何应用程序或内核代码。系统工作队列是可选的,并且只有在应用程序使用它时才存在。

Important:
只有当无法将新工作项提交给系统工作队列时,才应定义其他工作队列,因为每个新工作队列都会导致相当大的内存占用成本。 如果一个新的工作队列的工作项不可能与现有的系统工作队列工作项共存而不产生不可接受的影响,那么它可以被证明是合理的;例如,如果新工作项执行阻止操作,这将使其他系统工作队列处理延迟到无法接受的程度。

How to Use Workqueues - (如何使用工作队列)

Defining and Controlling a Workqueue - (定义和控制工作队列)

一个工作队列是使用 k_work_q 类型的变量来定义的。通过定义其线程使用的堆栈区域,然后调用 k_work_queue_start() 来初始化工作队列。必须使用 K_THREAD_STACK_DEFINE 定义堆栈区域,以确保在内存中正确地设置了它。

一下代码定义并初始化了一个工作队列:

#define MY_STACK_SIZE 512
#define MY_PRIORITY 5

K_THREAD_STACK_DEFINE(my_stack_area, MY_STACK_SIZE);

struct k_work_q my_work_q;

k_work_queue_start(&my_work_q, my_stack_area,
                   K_THREAD_STACK_SIZEOF(my_stack_area), MY_PRIORITY,
                   NULL);

此外,可以通过最后一个可选的参数控制与线程重新调度相关的队列标识和某些行为。

可以使用以下 API 与工作队列进行交互:

  • k_work_queue_drain() 可用于阻止调用者,直到工作队列中没有剩余的项目为止。
    当队列全部调用完毕时,将接受从工作队列线程重新提交的工作项,但拒绝来自任何其他线程或 ISR 的工作项。提交更多工作的限制可以扩展到耗尽操作完成之后,为了允许阻塞线程在队列被“插入”时执行额外的工作。请注意,清空队列对调度或处理可延迟项目没有影响,但如果队列被插入,且截止日期过期,则该项目将以静默方式无法提交。

  • k_work_queue_unplug() 删除由于先前的清空操作而导致提交到队列的所有先前的块。

Submitting a Work Item - (提交工作项)

工作项是使用 k_work 类型的变量来定义的。必须使用 k_work_init() 进行初始化,除非它是使用 K_WORK_DEFINE 定义的,在这种情况下,在编译时执行初始化。

一个初始化的工作项可以通过调用 k_work_submit() 被提交到队列中,或者通过调用 k_work_submit_to_queue() 提交到一个指定的队列中。

以下代码演示了 ISR 如何将错误消息的打印到系统工作队列中。请注意,如果 ISR 尝试重新提交处于排队状态的工作项,则该工作项将保持不变,并且不会打印相关的错误消息。

struct device_info {
    struct k_work work;
    char name[16]
} my_device;

void my_isr(void *arg)
{
    ...
    if (error detected) {
        k_work_submit(&my_device.work);
    }
    ...
}

void print_error(struct k_work *item)
{
    struct device_info *the_device =
        CONTAINER_OF(item, struct device_info, work);
    printk("Got error on device %s\n", the_device->name);
}

/* initialize name info for a device */
strcpy(my_device.name, "FOO_dev");

/* initialize work item for printing device's error messages */
k_work_init(&my_device.work, print_error);

/* install my_isr() as interrupt handler for the device (not shown) */
...

Scheduling a Delayable Work Item - (调度可延迟工作项)

可延迟工作项是使用 k_work_delayable 类型的变量来定义的。必须使用 k_work_init_delayable() 进行初始化。

通过调用 k_delayed_work_submit() 把延时工作项提交给系统工作队列,通过 k_delayed_work_submit_to_queue() 把延时工作项提交给用户自己定义的工作队列。

当想取消一个超时工作项,可以使用 k_delayed_work_cancel() 函数,但是需注意,取消只能在工作项指定的超时没发生之前,否则不能被取消。

Configuration Options - (配置选项)

上一篇:Linux内核模块之设备驱动设计样式


下一篇:QT应用编程: ubuntu18.04下QT5.13搭建Android开发环境(配件齐全)