[译]Chapter 6 - Demonstration System for Azure RTOS ThreadX演示

[译]:https://docs.microsoft.com/zh-cn/azure/rtos/threadx/chapter6

This chapter contains a description of the demonstration system that is delivered with all Azure RTOS ThreadX processor support packages.

本章包含所有Azure RTOS ThreadX处理器支持包附带的演示系统的描述。

Overview

Each ThreadX product distribution contains a demonstration system that runs on all supported microprocessors.

This example system is defined in the distribution file demo_threadx.c and is designed to illustrate how ThreadX is used in an embedded multithread environment. The demonstration consists of initialization, eight threads, one byte pool, one block pool, one queue, one semaphore, one mutex, and one event flags group.

 备注

Except for the thread's stack size, the demonstration application is identical on all ThreadX supported processors.

The complete listing of demo_threadx.c, including the line numbers referenced throughout the remainder of this chapter.

概述

每个ThreadX产品发行版都包含一个在所有支持的微处理器上运行的演示系统。

此示例系统在分发文件demo_threadx.c中定义,旨在说明如何在嵌入式多线程环境中使用threadx。演示包括初始化、八个线程、一个字节池、一个块池、一个队列、一个信号量、一个互斥量和一个事件标志组。

备注

除了线程的堆栈大小,演示应用程序在所有支持ThreadX的处理器上都是相同的。

demo_threadx.c的完整列表,包括本章其余部分中引用的行号。

Application Define

The tx_application_define function executes after the basic ThreadX initialization is complete. It is responsible for setting up all of the initial system resources, including threads, queues, semaphores, mutexes, event flags, and memory pools.

应用程序定义

tx_application_define 函数在基本ThreadX初始化完成后执行。它负责设置所有初始系统资源,包括线程、队列、信号量、互斥锁、事件标志和内存池。

The demonstration system's tx_application_define (line numbers 60-164) creates the demonstration objects in the following order:

演示系统的tx_application_define (行号60-164)按以下顺序创建演示对象:

  • byte_pool_0
  • thread_0
  • thread_1
  • thread_2
  • thread_3
  • thread_4
  • thread_5
  • thread_6
  • thread_7
  • queue_0
  • semaphore_0
  • event_flags_0
  • mutex_0
  • block_pool_0

The demonstration system does not create any other additional ThreadX objects. However, an actual application may create system objects during runtime inside of executing threads.

演示系统不会创建任何其他ThreadX对象。但是,实际应用程序可能会在运行时在执行线程内部创建系统对象。

Initial Execution

All threads are created with the TX_AUTO_START option. This makes them initially ready for execution. After tx_application_define completes, control is transferred to the thread scheduler and from there to each individual thread.

The order in which the threads execute is determined by their priority and the order that they were created. In the demonstration system, thread_0 executes first because it has the highest priority (it was created with a priority of 1). After thread_0 suspends, thread_5 is executed, followed by the execution of thread_3thread_4thread_6thread_7thread_1, and finally thread_2.

 备注

Even though thread_3 and thread_4 have the same priority (both created with a priority of 8), thread_3 executes first. This is because thread_3 was created and became ready before thread_4. Threads of equal priority execute in a FIFO fashion.

初始执行

所有线程都是使用TX_AUTO_START 选项创建的。这使它们最初准备好执行。在tx_application_define 完成后,控制权被转移到线程调度程序,并从那里转移到每个独立的线程。

线程的执行顺序由其优先级和创建顺序决定。在演示系统中,thread_0首先执行,因为它具有最高的优先级(创建时优先级为1)。thread_0挂起后,执行线程thread_5,然后执行thread_3thread_4thread_6thread_7thread_1,最后执行thread_2

备注

即使thread_3thread_4具有相同的优先级(都是以优先级8创建的),thread_3还是首先执行。这是因为thread_3是在thread_4之前创建并准备好的。优先级相等的线程以FIFO方式执行。

Thread 0

The function thread_0_entry marks the entry point of the thread (lines 167-190). Thread_0 is the first thread in the demonstration system to execute. Its processing is simple: it increments its counter, sleeps for 10 timer ticks, sets an event flag to wake up thread_5, then repeats the sequence.

Thread_0 is the highest priority thread in the system. When its requested sleep expires, it will preempt any other executing thread in the demonstration.

线程0

函数thread_0_entry 标记线程的入口点(第167-190行)。Thread_0 是演示系统中要执行的第一个线程。它的处理很简单:它递增计数器,休眠10个计时器刻度,设置一个事件标志来唤醒线程5,然后重复序列。

Thread_0 是系统中优先级最高的线程。当它请求的睡眠过期时,它将抢占演示中任何其他正在执行的线程。

Thread 1

The function thread_1_entry marks the entry point of the thread (lines 193-216). Thread_1 is the second-to-last thread in the demonstration system to execute. Its processing consists of incrementing its counter, sending a message to thread_2 (through queue_0), and repeating the sequence. Notice that thread_1 suspends whenever queue_0 becomes full (line 207).

线程1

函数thread_1_entry 标记线程的入口点(第193-216行)。Thread_1 是演示系统中要执行的倒数第二个线程。它的处理包括递增计数器、向Thread_2(通过queue_0 )发送消息并重复序列。请注意,只要queue_0 已满,Thread_1 就会挂起(第207行)。

Thread 2

The function thread_2_entry marks the entry point of the thread (lines 219-243). Thread_2 is the last thread in the demonstration system to execute. Its processing consists of incrementing its counter, getting a message from thread_1 (through queue_0), and repeating the sequence. Notice that thread_2 suspends whenever queue_0 becomes empty (line 233).

Although thread_1 and thread_2 share the lowest priority in the demonstration system (priority 16), they Threads 3 and 4 are also the only threads that are ready for execution most of the time. They are also the only threads created with time-slicing (lines 87 and 93). Each thread is allowed to execute for a maximum of 4 timer ticks before the other thread is executed.

线程2

函数thread_2_entry标记线程的入口点(第219-243行)。Thread_2 是演示系统中要执行的最后一个线程。它的处理包括递增计数器、从Thread_1(通过queue_0)获取消息以及重复执行序列。请注意,每当queue_0变为空时,线程2就会挂起(第233行)。

虽然Thread_1 和Thread_2 共享演示系统中的最低优先级(优先级16),但它们是线程3和线程4也是大多数时候都可以执行的唯一线程。它们也是使用时间切片创建的唯一线程(第87行和第93行)。在执行另一个线程之前,允许每个线程最多执行4次计时器计时。

Threads 3 and 4

The function thread_3_and_4_entry marks the entry point of both thread_3 and thread_4 (lines 246-280). Both threads have a priority of 8, which makes them the third and fourth threads in the demonstration system to execute. The processing for each thread is the same: incrementing its counter, getting semaphore_0, sleeping for 2 timer ticks, releasing semaphore_0, and repeating the sequence. Notice that each thread suspends whenever semaphore_0 is unavailable (line 264).

Also both threads use the same function for their main processing. This presents no problems because they both have their own unique stack, and C is naturally reentrant. Each thread determines which one it is by examination of the thread input parameter (line 258), which is setup when they are created (lines 102 and 109).

 备注

It is also reasonable to obtain the current thread point during thread execution and compare it with the control block's address to determine thread identity.

线程3和4

函数thread_3_and_4_entry 标记thread_3 and thread_4的入口点(第246-280行)。两个线程的优先级都是8,这使它们成为演示系统中要执行的第三个和第四个线程。每个线程的处理都是相同的:递增其计数器,获取semaphore_0,休眠2个计时器节拍,释放semaphore_0,并重复执行序列。请注意,每当semaphore_0不可用时,每个线程都会挂起(第264行)。

两个线程的主处理也使用相同的函数。这不会带来任何问题,因为它们都有自己独特的堆栈,而且C自然是可重入的。每个线程通过检查线程输入参数(第258行)来确定它是哪个线程,该参数在创建线程时设置(第102行和第109行)。

备注

在线程执行期间获取当前线程点并将其与控制块的地址进行比较以确定线程标识也是合理的。

Thread 5

The function thread_5_entry marks the entry point of the thread (lines 283-305). Thread_5 is the second thread in the demonstration system to execute. Its processing consists of incrementing its counter, getting an event flag from thread_0 (through event_flags_0), and repeating the sequence. Notice that thread_5 suspends whenever the event flag in event_flags_0 is not available (line 298).

线程5

函数 thread_5_entry标记线程的入口点(第283-305行)。Thread_5是演示系统中要执行的第二个线程。它的处理包括递增计数器、从Thread_0获取事件标志(通过event_flags_0)以及重复执行序列。请注意,只要event_flags_0中的事件标志不可用,Thread_5就会挂起(第298行)。

Threads 6 and 7

The function thread_6_and_7_entry marks the entry point of both thread_6 and thread_7 (lines 307-358). Both threads have a priority of 8, which makes them the fifth and sixth threads in the demonstration system to execute. The processing for each thread is the same: incrementing its counter, getting mutex_0 twice, sleeping for 2 timer ticks, releasing mutex_0 twice, and repeating the sequence. Notice that each thread suspends whenever mutex_0 is unavailable (line 325).

Also both threads use the same function for their main processing. This presents no problems because they both have their own unique stack, and C is naturally reentrant. Each thread determines which one it is by examination of the thread input parameter (line 319), which is setup when they are created (lines 126 and 133).

线程6和7

函数thread_6_and_7_entry标记thread_6和thread_7的入口点(第307-358行)。两个线程的优先级都是8,这使它们成为演示系统中要执行的第五个和第六个线程。每个线程的处理过程都是相同的:递增其计数器,获取mutex_0 两次,休眠两个计时器刻度,释放mutex_0 两次,然后重复序列。请注意,每当mutex_0 不可用时,每个线程都会挂起(第325行)。

两个线程的主处理也使用相同的函数。这不会带来任何问题,因为它们都有自己独特的堆栈,而且C自然是可重入的。每个线程通过检查线程输入参数(第319行)来确定它是哪个线程,该参数在创建线程时设置(第126行和第133行)。

Observing the Demonstration

Each of the demonstration threads increments its own unique counter. The following counters may be examined to check on the demo's operation:

观察演示

每个演示线程都增加自己的唯一计数器。可以检查以下计数器以检查演示的操作:

  • thread_0_counter
  • thread_1_counter
  • thread_2_counter
  • thread_3_counter
  • thread_4_counter
  • thread_5_counter
  • thread_6_counter
  • thread_7_counter

Each of these counters should continue to increase as the demonstration executes, with thread_1_counter and thread_2_counter increasing at the fastest rate.

随着演示的执行,这些计数器中的每一个都应该继续增加,其中thread_1_counter 和thread_2_counter 以最快的速度增加。

Distribution file: demo_threadx.c

This section displays the complete listing of demo_threadx.c, including the line numbers referenced throughout this chapter.

发布文件:demo_threadx.c

本节显示demo_threadx.c的完整列表,包括本章中引用的行号。

/* This is a small demo of the high-performance ThreadX kernel. It includes examples of eight
threads of different priorities, using a message queue, semaphore, mutex, event flags group,
byte pool, and block pool. */

#include "tx_api.h"

#define DEMO_STACK_SIZE 1024
#define DEMO_BYTE_POOL_SIZE 9120
#define DEMO_BLOCK_POOL_SIZE 100
#define DEMO_QUEUE_SIZE 100

/* Define the ThreadX object control blocks... */

TX_THREAD thread_0;
TX_THREAD thread_1;
TX_THREAD thread_2;
TX_THREAD thread_3;
TX_THREAD thread_4;
TX_THREAD thread_5;
TX_THREAD thread_6;
TX_THREAD thread_7;
TX_QUEUE queue_0;
TX_SEMAPHORE semaphore_0;
TX_MUTEX mutex_0;
TX_EVENT_FLAGS_GROUP event_flags_0;
TX_BYTE_POOL byte_pool_0;
TX_BLOCK_POOL block_pool_0;

/* Define the counters used in the demo application... */

ULONG thread_0_counter;
ULONG thread_1_counter;
ULONG thread_1_messages_sent;
ULONG thread_2_counter;
ULONG thread_2_messages_received;
ULONG thread_3_counter;
ULONG thread_4_counter;
ULONG thread_5_counter;
ULONG thread_6_counter;
ULONG thread_7_counter;

/* Define thread prototypes. */

void thread_0_entry(ULONG thread_input);
void thread_1_entry(ULONG thread_input);
void thread_2_entry(ULONG thread_input);
void thread_3_and_4_entry(ULONG thread_input);
void thread_5_entry(ULONG thread_input);
void thread_6_and_7_entry(ULONG thread_input);


/* Define main entry point. */

int main()
{
    /* Enter the ThreadX kernel. */
    tx_kernel_enter();
}

/* Define what the initial system looks like. */
void tx_application_define(void *first_unused_memory)
{

    CHAR *pointer;

    /* Create a byte memory pool from which to allocate the thread stacks. */
    tx_byte_pool_create(&byte_pool_0, "byte pool 0", first_unused_memory,
        DEMO_BYTE_POOL_SIZE);

    /* Put system definition stuff in here, e.g., thread creates and other assorted
        create information. */

    /* Allocate the stack for thread 0. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    /* Create the main thread. */
    tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0,
        pointer, DEMO_STACK_SIZE,
        1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);

    /* Allocate the stack for thread 1. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    /* Create threads 1 and 2. These threads pass information through a ThreadX
        message queue. It is also interesting to note that these threads have a time
        slice. */
    tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1,
        pointer, DEMO_STACK_SIZE,
        16, 16, 4, TX_AUTO_START);

    /* Allocate the stack for thread 2. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
        tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2,
        pointer, DEMO_STACK_SIZE,
        16, 16, 4, TX_AUTO_START);

    /* Allocate the stack for thread 3. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    /* Create threads 3 and 4. These threads compete for a ThreadX counting semaphore.
        An interesting thing here is that both threads share the same instruction area. */
    tx_thread_create(&thread_3, "thread 3", thread_3_and_4_entry, 3,
        pointer, DEMO_STACK_SIZE,
        8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);

    /* Allocate the stack for thread 4. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    tx_thread_create(&thread_4, "thread 4", thread_3_and_4_entry, 4,
        pointer, DEMO_STACK_SIZE,
        8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);

    /* Allocate the stack for thread 5. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    /* Create thread 5. This thread simply pends on an event flag, which will be set
        by thread_0. */
    tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5,
        pointer, DEMO_STACK_SIZE,
        4, 4, TX_NO_TIME_SLICE, TX_AUTO_START);

    /* Allocate the stack for thread 6. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    /* Create threads 6 and 7. These threads compete for a ThreadX mutex. */
    tx_thread_create(&thread_6, "thread 6", thread_6_and_7_entry, 6,
        pointer, DEMO_STACK_SIZE,
        8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);

    /* Allocate the stack for thread 7. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);

    tx_thread_create(&thread_7, "thread 7", thread_6_and_7_entry, 7,
        pointer, DEMO_STACK_SIZE,
        8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);

    /* Allocate the message queue. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT);

    /* Create the message queue shared by threads 1 and 2. */
    tx_queue_create(&queue_0, "queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG));

    /* Create the semaphore used by threads 3 and 4. */
    tx_semaphore_create(&semaphore_0, "semaphore 0", 1);

    /* Create the event flags group used by threads 1 and 5. */
    tx_event_flags_create(&event_flags_0, "event flags 0");

    /* Create the mutex used by thread 6 and 7 without priority inheritance. */
    tx_mutex_create(&mutex_0, "mutex 0", TX_NO_INHERIT);

    /* Allocate the memory for a small block pool. */
    tx_byte_allocate(&byte_pool_0, &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT);

    /* Create a block memory pool to allocate a message buffer from. */
    tx_block_pool_create(&block_pool_0, "block pool 0", sizeof(ULONG), pointer,
        DEMO_BLOCK_POOL_SIZE);

    /* Allocate a block and release the block memory. */
    tx_block_allocate(&block_pool_0, &pointer, TX_NO_WAIT);

    /* Release the block back to the pool. */
    tx_block_release(pointer);
}

/* Define the test threads. */
void thread_0_entry(ULONG thread_input)
{
    UINT status;


    /* This thread simply sits in while-forever-sleep loop. */
    while(1)
    {

        /* Increment the thread counter. */
        thread_0_counter++;

        /* Sleep for 10 ticks. */
        tx_thread_sleep(10);

        /* Set event flag 0 to wakeup thread 5. */
        status = tx_event_flags_set(&event_flags_0, 0x1, TX_OR);

        /* Check status. */
        if (status != TX_SUCCESS)
            break;
    }
}


void thread_1_entry(ULONG thread_input)
{
    UINT status;


    /* This thread simply sends messages to a queue shared by thread 2. */
    while(1)
    {
        /* Increment the thread counter. */
        thread_1_counter++;

        /* Send message to queue 0. */
        status = tx_queue_send(&queue_0, &thread_1_messages_sent, TX_WAIT_FOREVER);

        /* Check completion status. */
        if (status != TX_SUCCESS)
            break;

        /* Increment the message sent. */
        thread_1_messages_sent++;
    }
}


void thread_2_entry(ULONG thread_input)
{
    ULONG received_message;
    UINT status;

    /* This thread retrieves messages placed on the queue by thread 1. */
    while(1)
    {
        /* Increment the thread counter. */
        thread_2_counter++;

        /* Retrieve a message from the queue. */
        status = tx_queue_receive(&queue_0, &received_message, TX_WAIT_FOREVER);

        /* Check completion status and make sure the message is what we
        expected. */
        if ((status != TX_SUCCESS) || (received_message != thread_2_messages_received))
            break;

        /* Otherwise, all is okay. Increment the received message count. */
        thread_2_messages_received++;
    }
}


void thread_3_and_4_entry(ULONG thread_input)
{
    UINT status;


    /* This function is executed from thread 3 and thread 4. As the loop
    below shows, these function compete for ownership of semaphore_0. */
    while(1)
    {
        /* Increment the thread counter. */
        if (thread_input == 3)
            thread_3_counter++;
        else
            thread_4_counter++;

        /* Get the semaphore with suspension. */
        status = tx_semaphore_get(&semaphore_0, TX_WAIT_FOREVER);

        /* Check status. */
        if (status != TX_SUCCESS)
            break;

        /* Sleep for 2 ticks to hold the semaphore. */
        tx_thread_sleep(2);

        /* Release the semaphore. */
        status = tx_semaphore_put(&semaphore_0);

        /* Check status. */
        if (status != TX_SUCCESS)
            break;
    }
}


void thread_5_entry(ULONG thread_input)
{
    UINT status;
    ULONG actual_flags;


    /* This thread simply waits for an event in a forever loop. */
    while(1)
    {
        /* Increment the thread counter. */
        thread_5_counter++;

        /* Wait for event flag 0. */
        status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR,
            &actual_flags, TX_WAIT_FOREVER);

        /* Check status. */
        if ((status != TX_SUCCESS) || (actual_flags != 0x1))
            break;
    }
}

void thread_6_and_7_entry(ULONG thread_input)
{
    UINT status;

    /* This function is executed from thread 6 and thread 7. As the loop
        below shows, these function compete for ownership of mutex_0. */
    while(1)
    {
        /* Increment the thread counter. */
        if (thread_input == 6)
            thread_6_counter++;
        else
            thread_7_counter++;

        /* Get the mutex with suspension. */
        status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER);

        /* Check status. */
        if (status != TX_SUCCESS)
            break;

        /* Get the mutex again with suspension. This shows
            that an owning thread may retrieve the mutex it
            owns multiple times. */
        status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER);

        /* Check status. */
        if (status != TX_SUCCESS)
            break;

        /* Sleep for 2 ticks to hold the mutex. */
        tx_thread_sleep(2);

        /* Release the mutex. */
        status = tx_mutex_put(&mutex_0);

        /* Check status. */
        if (status != TX_SUCCESS)
            break;

        /* Release the mutex again. This will actually
            release ownership since it was obtained twice. */
        status = tx_mutex_put(&mutex_0);

        /* Check status. */
        if (status != TX_SUCCESS)
            break;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值