임베디드/[ RTOS ]

[ RTOS ] 02. ThreadX의 Thread Execution

kim.svadoz 2021. 3. 30. 10:08
728x90
반응형

이번 글은 RTOS의 중요한 기능중 하나인 Thread에 대해 다룰 것이므로 주의 깊게 보시기를 추천드립니다

Thread Execution

Scheduling and executing application threads is the most important activity of ThreadX. What exactly is a thread? A thread is typically defined as semi-independent program segment with a dedicated purpose. The combined processing of all threads makes an application.

How are threads created? Threads are created dynamically by calling tx_thread_create during initialization or during thread execution. Threads are created in either a ready or suspended state.

애플리케이션 스레드를 예약하고 실행하는 것은 ThreadX의 가장 중요한 활동입니다. 스레드는 정확히 무엇입니까? 스레드는 일반적으로 전용 목적을 가진 반독립 프로그램 세그먼트로 정의됩니다. 모든 스레드의 결합 된 처리는 응용 프로그램을 만듭니다.

스레드는 어떻게 생성됩니까? 스레드는 초기화 또는 스레드 실행 중에 tx_thread_create를 호출하여 동적으로 생성됩니다. 스레드는 준비 또는 일시 중지 상태로 생성됩니다.

  • Thread Execution States

Understanding the different processing states of threads is a key ingredient to understanding the entire multi-threaded environment. In ThreadX there are five distinct thread states, namely ready, suspended, executing, terminated, and completed. Figure 5 on page 53 shows the thread state transition diagram for ThreadX.

A thread is in a ready state when it is ready for execution. A ready thread is not executed until it is the highest priority thread ready. When this happens, ThreadX executes the thread, which changes its state to executing.

If a higher-priority thread becomes ready, the executing thread reverts back to a ready state. The newly ready high-priority thread is then executed, which changes its logical state to executing. This transition between ready and executing states occurs every time thread preemption occurs.

It is important to point out that at any given moment only one thread is in an executing state. This is because a thread in the executing state actually has control of the underlying processor.

Threads that are in a suspended state are not eligible for execution. Reasons for being in a suspended state include suspension for time, queue messages, semaphores, mutexes, event flags, memory, and basic thread suspension. Once the cause for suspension is removed, the thread is placed back in a ready state.

스레드의 다양한 처리 상태를 이해하는 것은 전체 다중 스레드 환경을 이해하기위한 핵심 요소입니다. ThreadX에는 준비, 일시 중지, 실행, 종료 및 완료의 다섯 가지 스레드 상태가 있습니다. 53 페이지의 그림 5는 ThreadX의 스레드 상태 전환 다이어그램을 보여줍니다.

스레드는 실행 준비가되었을 때 준비 상태입니다. 준비 스레드는 우선 순위가 가장 높은 스레드 준비가 될 때까지 실행되지 않습니다. 이 경우 ThreadX는 스레드를 실행하여 상태를 실행 중으로 변경합니다.

우선 순위가 더 높은 스레드가 준비되면 실행중인 스레드가 다시 준비 상태로 돌아갑니다. 새로 준비된 우선 순위가 높은 스레드가 실행되고 논리 상태가 실행 중으로 변경됩니다. 준비 상태와 실행 상태 사이의 이러한 전환은 스레드 선점이 발생할 때마다 발생합니다.

주어진 순간에 오직 하나의 스레드 만이 실행 상태에 있음을 지적하는 것이 중요합니다. 실행 상태의 스레드가 실제로 기본 프로세서를 제어하기 때문입니다.

일시 중단 된 상태의 스레드는 실행할 수 없습니다. 일시 중단 상태에있는 이유에는 시간 일시 중단, 큐 메시지, 세마포어, 뮤텍스, 이벤트 플래그, 메모리 및 기본 스레드 일시 중단이 포함됩니다. 일시 중단 원인이 제거되면 스레드는 다시 준비 상태로 돌아갑니다.

image-20210210132613248

A thread in a completed state indicates the thread completed its processing and returned from its entry function. Remember that the entry function is specified during thread creation. A thread in a completed state cannot execute again. A thread is in a terminated state because another thread or itself called the tx_thread_terminate service.

A thread in a terminated state cannot execute again. If re-starting a completed or terminated thread is desired, the application must first delete the thread. It can then be re-created and re-started.

완료된 상태의 스레드는 스레드가 처리를 완료하고 입력 함수에서 반환되었음을 나타냅니다. 입력 함수는 스레드 생성 중에 지정됩니다. 완료된 상태의 스레드는 다시 실행할 수 없습니다. 다른 스레드 또는 자체가 tx_thread_terminate 서비스를 호출했기 때문에 스레드가 종료 된 상태입니다.

종료 된 상태의 스레드는 다시 실행할 수 없습니다. 완료되거나 종료 된 스레드를 다시 시작하려면 애플리케이션이 먼저 스레드를 삭제해야합니다. 그런 다음 다시 생성하고 다시 시작할 수 있습니다.

  • Thread Priorities

As mentioned before, a thread is defined as a semiindependent program segment with a dedicated purpose. However, all threads are not created equal! The dedicated purpose of some threads is much more important than others. This heterogeneous type of thread importance is a hallmark of embedded realtime applications.

How does ThreadX determine a thread’s importance? When a thread is created, it is assigned a numerical value representing its importance or priority. Valid numerical priorities range between 0 and 31, where a value of 0 indicates the highest thread priority and a value of 31 represents the lowest thread priority.

Threads can have the same priority as others in the application. In addition, thread priorities can be changed during run-time.

앞서 언급했듯이 스레드는 전용 목적을 가진 반독립 프로그램 세그먼트로 정의됩니다. 그러나 모든 스레드가 동일하게 생성되는 것은 아닙니다! 일부 스레드의 전용 목적은 다른 스레드보다 훨씬 더 중요합니다. 이 이기종 유형의 스레드 중요도는 임베디드 실시간 애플리케이션의 특징입니다.

ThreadX는 스레드의 중요성을 어떻게 결정합니까? 스레드가 생성 될 때 중요도 또는 우선 순위를 나타내는 숫자 값이 할당됩니다. 유효한 숫자 우선 순위 범위는 0에서 31 사이입니다. 여기서 0 값은 가장 높은 스레드 우선 순위를 나타내고 31 값은 가장 낮은 스레드 우선 순위를 나타냅니다.

스레드는 애플리케이션의 다른 스레드와 동일한 우선 순위를 가질 수 있습니다. 또한 스레드 우선 순위는 런타임 중에 변경할 수 있습니다.

  • Thread Scheduling

ThreadX schedules threads based upon their priority. The ready thread with the highest priority is executed first. If multiple threads of the same priority are ready, they are executed in a first-in-first-out (FIFO) manner.

ThreadX는 우선 순위에 따라 스레드를 예약합니다. 우선 순위가 가장 높은 준비 스레드가 먼저 실행됩니다. 우선 순위가 동일한 여러 스레드가 준비된 경우 FIFO (선입 선출) 방식으로 실행됩니다.

  • Round-Robing Scheduling

Round-robin scheduling of multiple threads having the same priority is supported by ThreadX. This is accomplished through cooperative calls to tx_thread_relinquish. Calling this service gives all other ready threads at the same priority a chance to execute before the tx_thread_relinquish caller executes again.

ThreadX는 우선 순위가 동일한 여러 스레드의 라운드 로빈 스케줄링을 지원합니다. 이는 tx_thread_relinquish에 대한 협력 호출을 통해 수행됩니다. 이 서비스를 호출하면 tx_thread_relinquish 호출자가 다시 실행되기 전에 동일한 우선 순위의 다른 모든 준비 스레드가 실행될 수 있습니다.

  • Time-Slicing

Time-slicing provides another form of round-robin scheduling. In ThreadX, time-slicing is available on a per-thread basis. The thread’s time-slice is assigned during creation and can be modified during run-time.

What exactly is a time-slice? A time-slice specifies the maximum number of timer ticks (timer interrupts) that a thread can execute without giving up the processor. When a time-slice expires, all other ready threads of the same priority level are given a chance to execute before the time-sliced thread executes again.

A fresh thread time-slice is given to a thread after it suspends, relinquishes, makes a ThreadX service call that causes preemption, or is itself time-sliced.

When a time-sliced thread is preempted, it will resume before other ready threads of equal priority for the remainder of its time-slice.

Using time-slicing results in a slight amount of system overhead. Since time-slicing is only useful in cases where multiple threads share the same priority, threads having a unique priority should not be assigned a time-slice.

시간 분할은 다른 형태의 라운드 로빈 스케줄링을 제공합니다. ThreadX에서는 스레드 단위로 시간 분할이 가능합니다. 스레드의 시간 분할 영역은 생성 중에 할당되며 런타임 중에 수정할 수 있습니다.

타임 슬라이스 란 정확히 무엇입니까? 타임 슬라이스는 스레드가 프로세서를 포기하지 않고 실행할 수있는 최대 타이머 틱 (타이머 인터럽트) 수를 지정합니다. 타임 슬라이스가 만료되면 동일한 우선 순위 레벨의 다른 모든 준비 스레드가 타임 슬라이스 스레드가 다시 실행되기 전에 실행될 기회가 주어집니다.

새로운 스레드 타임 슬라이스는 일시 중단, 포기, 선점을 유발하는 ThreadX 서비스 호출을 수행하거나 자체적으로 타임 슬라이스 된 후 스레드에 제공됩니다.

시간 분할 스레드가 선점되면 나머지 시간 분할에 대해 동일한 우선 순위의 다른 준비 스레드보다 먼저 재개됩니다.

시간 분할을 사용하면 약간의 시스템 오버 헤드가 발생합니다. 시간 분할은 여러 스레드가 동일한 우선 순위를 공유하는 경우에만 유용하므로 고유 한 우선 순위를 갖는 스레드에 시간 분할이 지정되지 않아야합니다.

  • Preemption

Preemption is the process of temporarily interrupting an executing thread in favor of a higher-priority thread. This process is invisible to the executing thread. When the higher-priority thread is finished, control is transferred back to the exact place where the preemption took place.

This is a very important feature in real-time systems because it facilitates fast response to important application events. Although a very important feature, preemption can also be a source of a variety of problems, including starvation, excessive overhead, and priority inversion.

선점은 우선 순위가 높은 스레드를 위해 실행중인 스레드를 일시적으로 중단하는 프로세스입니다. 이 프로세스는 실행중인 스레드에 표시되지 않습니다. 우선 순위가 높은 스레드가 완료되면 제어가 선점이 발생한 정확한 위치로 다시 전송됩니다.

이는 중요한 애플리케이션 이벤트에 대한 빠른 응답을 용이하게하기 때문에 실시간 시스템에서 매우 중요한 기능입니다. 매우 중요한 기능이지만 선점은 기아, 과도한 오버 헤드, 우선 순위 반전 등 다양한 문제의 원인이 될 수도 있습니다.

  • Preemption-Threshold

In order to ease some of the inherent problems of preemption, ThreadX provides a unique and advanced feature called preemption-threshold.

What is a preemption-threshold? A preemptionthreshold allows a thread to specify a priority ceiling for disabling preemption. Threads that have higher priorities than the ceiling are still allowed to preempt, while those less than the ceiling are not allowed to preempt.

For example, suppose a thread of priority 20 only interacts with a group of threads that have priorities between 15 and 20. During its critical sections, the thread of priority 20 can set its preemption-threshold to 15, thereby preventing preemption from all of the threads that it interacts with. This still permits really important threads (priorities between 0 and 14) to preempt this thread during its critical section processing, which results in much more responsive processing.

Of course, it is still possible for a thread to disable all preemption by setting its preemption-threshold to 0. In addition, preemption-thresholds can be changed during run-time.

Note that using preemption-threshold disables timeslicing for the specified thread.

선점의 내재 된 문제를 완화하기 위해 ThreadX는 선점 임계 값이라는 고유하고 고급 기능을 제공합니다.

선점 임계 값이란 무엇입니까? preemptionthreshold를 사용하면 스레드가 선점을 비활성화하기위한 우선 순위 상한을 지정할 수 있습니다. 한도보다 우선 순위가 높은 스레드는 여전히 선점 할 수 있지만, 한도보다 낮은 스레드는 선점 할 수 없습니다.

예를 들어 우선 순위가 20 인 스레드가 우선 순위가 15에서 20 사이 인 스레드 그룹과 만 상호 작용한다고 가정합니다. 중요 섹션 동안 우선 순위 20의 스레드는 선점 임계 값을 15로 설정하여 모든 스레드에서 선점을 방지 할 수 있습니다. 상호 작용하는 스레드. 이것은 여전히 매우 중요한 스레드 (0에서 14 사이의 우선 순위)가 중요한 섹션 처리 중에이 스레드를 선점하도록 허용하여 훨씬 더 응답 성이 뛰어난 처리를 가능하게합니다.

물론 preemption-threshold를 0으로 설정하여 스레드가 모든 선점을 비활성화 할 수 있습니다. 또한 preemption-thresholds는 런타임 중에 변경할 수 있습니다.

preemption-threshold를 사용하면 지정된 스레드에 대한 타임 라이 싱이 비활성화됩니다.

  • Priority Inheritance

ThreadX also supports optional priority inheritance within its mutex services described later in this chapter. Priority inheritance allows a lower priority thread to temporarily assume the priority of a high priority thread that is waiting for a mutex owned by the lower priority thread. This capability helps the application to avoid un-deterministic priority inversion by eliminating preemption of intermediate thread priorities. Of course, preemption-threshold may be used to achieve a similar result.

ThreadX는 또한이 장의 뒷부분에서 설명하는 뮤텍스 서비스 내에서 선택적 우선 순위 상속을 지원합니다. 우선 순위 상속을 통해 우선 순위가 낮은 스레드가 우선 순위가 낮은 스레드가 소유 한 뮤텍스를 기다리는 높은 우선 순위 스레드의 우선 순위를 일시적으로 가정 할 수 있습니다. 이 기능은 애플리케이션이 중간 스레드 우선 순위의 선점을 제거하여 비 결정적 우선 순위 반전을 방지하는 데 도움이됩니다. 물론 선점 임계 값을 사용하여 유사한 결과를 얻을 수 있습니다.

  • Thread Creation

Application threads are created during initialization or during the execution of other application threads. There are no limits on the number of threads that can be created by an application.

응용 프로그램 스레드는 초기화 또는 다른 응용 프로그램 스레드 실행 중에 생성됩니다. 응용 프로그램에서 만들 수있는 스레드 수에는 제한이 없습니다.

  • Thread Control Block TX_THREAD

The characteristics of each thread are contained in its control block. This structure is defined in the tx_api.h file.

A thread’s control block can be located anywhere in memory, but it is most common to make the control block a global structure by defining it outside the scope of any function.

Locating the control block in other areas requires a bit more care, just like all dynamically allocated memory. If a control block is allocated within a C function, the memory associated with it is part of the calling thread’s stack. In general, using local storage for control blocks should be avoided because once the function returns, then all of its local variable stack space is released—regardless of whether another thread is using it for a control block!

In most cases, the application is oblivious to the contents of the thread’s control block. However, there are some situations, especially in debug, where looking at certain members is quite useful. The following are a few of the more useful control block members:

각 스레드의 특성은 제어 블록에 포함되어 있습니다. 이 구조는 tx_api.h 파일에 정의되어 있습니다.

스레드의 제어 블록은 메모리의 어느 위치 에나있을 수 있지만 제어 블록을 함수 범위 외부에서 정의하여 전역 구조로 만드는 것이 가장 일반적입니다.

다른 영역에서 제어 블록을 찾으려면 모든 동적 할당 메모리와 마찬가지로 좀 더주의가 필요합니다. 제어 블록이 C 함수 내에 할당 된 경우 이와 관련된 메모리는 호출 스레드 스택의 일부입니다. 일반적으로 제어 블록에 로컬 스토리지를 사용하는 것은 피해야합니다. 함수가 반환되면 다른 스레드가 제어 블록에이를 사용하는지 여부에 관계없이 모든 로컬 변수 스택 공간이 해제되기 때문입니다!

대부분의 경우 응용 프로그램은 스레드 제어 블록의 내용을 인식하지 못합니다. 그러나 특히 디버그에서 특정 멤버를 보는 것이 매우 유용한 일부 상황이 있습니다. 다음은 몇 가지 유용한 제어 블록 멤버입니다.

tx_run_count

This member contains a counter of how many times the thread has been scheduled. An increasing counter indicates the thread is being scheduled and executed.

이 멤버는 스레드가 예약 된 횟수에 대한 카운터를 포함합니다. 카운터가 증가하면 스레드가 예약되고 실행되고 있음을 나타냅니다.

tx_state

This member contains the state of the associated thread. The following list represents the possible thread states:

이 멤버는 연관된 스레드의 상태를 포함합니다. 다음 목록은 가능한 스레드 상태를 나타냅니다.

image-20210210135743791

Of course there are many other interesting fields in the thread control block, including the stack pointer, time-slice value, priorities, etc. The user is welcome to review any and all of the control block members, but modification is strictly prohibited!

물론 스택 포인터, 시간 분할 값, 우선 순위 등을 포함하여 스레드 제어 블록에는 다른 많은 흥미로운 필드가 있습니다. 사용자는 모든 제어 블록 구성원을 검토 할 수 있지만 수정은 엄격히 금지됩니다!

There is no equate for the “executing” state mentioned earlier in this section. It is not necessary since there is only one executing thread at a given time. The state of an executing thread is also TX_READY

이 섹션의 앞부분에서 언급 한 "실행 중"상태에 해당하는 것은 없습니다. 주어진 시간에 실행 스레드가 하나만 있기 때문에 필요하지 않습니다. 실행중인 스레드의 상태도 TX_READY입니다.

  • Currently Executing Thread

As mentioned before, there is only one thread executing at any given time. There are several ways to identify the executing thread, depending on who is making the request.

A program segment can get the control block address of the executing thread by calling tx_thread_identify. This is useful in shared portions of application code that are executed from multiple threads.

In debug sessions, users can examine the internal ThreadX pointer _tx_thread_current_ptr. It contains the control block address of the currently executing thread. If this pointer is NULL, no application thread is executing; i.e., ThreadX is waiting in its scheduling loop for a thread to become ready

앞서 언급했듯이 주어진 시간에 실행되는 스레드는 하나뿐입니다. 요청하는 사람에 따라 실행중인 스레드를 식별하는 방법에는 여러 가지가 있습니다.

프로그램 세그먼트는 tx_thread_identify를 호출하여 실행중인 스레드의 제어 블록 주소를 가져올 수 있습니다. 이것은 여러 스레드에서 실행되는 응용 프로그램 코드의 공유 부분에서 유용합니다.

디버그 세션에서 사용자는 내부 ThreadX 포인터 _tx_thread_current_ptr을 검사 할 수 있습니다. 현재 실행중인 스레드의 제어 블록 주소를 포함합니다. 이 포인터가 NULL이면 응용 프로그램 스레드가 실행되지 않습니다. 즉, ThreadX는 스레드가 준비 될 때까지 스케줄링 루프에서 기다리고 있습니다.

  • Thread Stack Area

Each thread must have its own stack for saving the context of its last execution and compiler use. Most C compilers use the stack for making function calls and for temporarily allocating local variables. Figure 6 shows a typical thread’s stack.

Where is a thread stack located? This is really up to the application. The stack area is specified during thread creation and can be located anywhere in the target’s address space. This is a very important feature because it allows applications to improve performance of important threads by placing their stack in high-speed RAM.

How big should a stack be? This is one of the most frequently asked questions about threads. A thread’s stack area must be large enough to accommodate worst-case function call nesting, local variable allocation, and saving its last execution context.

The minimum stack size, TX_MINIMUM_STACK, is defined by ThreadX. A stack of this size supports saving a thread’s context and minimum amount of function calls and local variable allocation.

For most threads, the minimum stack size is simply too small. The user must come up with the worstcase size requirement by examining function-call nesting and local variable allocation. Of course, it is always better to error towards a larger stack area.

After the application is debugged, it is possible to go back and tune the thread stacks sizes if memory is scarce. A favorite trick is to preset all stack areas with an easily identifiable data pattern like (0xEFEF) prior to creating the threads. After the application has been thoroughly put through its paces, the stack areas can be examined to see how much was actually used by finding the area of the stack where the preset pattern is still intact. Figure 7 on page 61 shows a stack preset to 0xEFEF after thorough thread execution.

각 스레드에는 마지막 실행 및 컴파일러 사용의 컨텍스트를 저장하기위한 자체 스택이 있어야합니다. 대부분의 C 컴파일러는 스택을 사용하여 함수를 호출하고 일시적으로 지역 변수를 할당합니다. 그림 6은 일반적인 스레드 스택을 보여줍니다.

스레드 스택은 어디에 있습니까? 이것은 실제로 응용 프로그램에 달려 있습니다. 스택 영역은 스레드 생성 중에 지정되며 대상 주소 공간의 어느 곳에 나 위치 할 수 있습니다. 이것은 응용 프로그램이 고속 RAM에 스택을 배치하여 중요한 스레드의 성능을 향상시킬 수 있도록하기 때문에 매우 중요한 기능입니다.

스택은 얼마나 커야합니까? 이것은 스레드에 대해 가장 자주 묻는 질문 중 하나입니다. 스레드의 스택 영역은 최악의 경우 함수 호출 중첩, 로컬 변수 할당 및 마지막 실행 컨텍스트 저장을 수용 할 수있을만큼 충분히 커야합니다.

최소 스택 크기 TX_MINIMUM_STACK은 ThreadX에 의해 정의됩니다. 이 크기의 스택은 스레드의 컨텍스트 저장과 최소 함수 호출 및 로컬 변수 할당을 지원합니다.

대부분의 스레드에서 최소 스택 크기는 너무 작습니다. 사용자는 함수 호출 중첩 및 지역 변수 할당을 검사하여 최악의 크기 요구 사항을 제시해야합니다. 물론 더 큰 스택 영역으로 오류를 일으키는 것이 항상 좋습니다.

응용 프로그램이 디버깅 된 후 메모리가 부족한 경우 돌아가서 스레드 스택 크기를 조정할 수 있습니다. 가장 좋아하는 트릭은 스레드를 만들기 전에 (0xEFEF)와 같이 쉽게 식별 할 수있는 데이터 패턴으로 모든 스택 영역을 미리 설정하는 것입니다. 응용 프로그램이 완전히 진행된 후 스택 영역을 검사하여 사전 설정된 패턴이 여전히 손상되지 않은 스택 영역을 찾아 실제로 얼마나 많이 사용되었는지 확인할 수 있습니다. 61 페이지의 그림 7은 스레드 실행 후 0xEFEF로 사전 설정된 스택을 보여줍니다.

image-20210210140459733

image-20210210140641509

  • Memory Pitfalls

The stack requirements for threads can be quite large. Therefore, it is important to design the application to have a reasonable number of threads. Furthermore, some care must be taken to avoid excessive stack usage within threads. Recursive algorithms and large local data structures should generally be avoided.

What happens when a stack area is too small? In most cases, the run-time environment simply assumes there is enough stack space. This causes thread execution to corrupt memory adjacent (usually before) its stack area. The results are very unpredictable, but most often result in an un-natural change in the program counter. This is often called “jumping into the weeds.” Of course, the only way to prevent this is to ensure that all thread stacks are large enough.

스레드에 대한 스택 요구 사항은 상당히 클 수 있습니다. 따라서 적절한 수의 스레드를 갖도록 응용 프로그램을 설계하는 것이 중요합니다. 또한 스레드 내에서 과도한 스택 사용을 방지하려면 약간의주의를 기울여야합니다. 재귀 알고리즘과 대규모 로컬 데이터 구조는 일반적으로 피해야합니다.

스택 영역이 너무 작 으면 어떻게됩니까? 대부분의 경우 런타임 환경은 스택 공간이 충분하다고 가정합니다. 이로 인해 스레드 실행으로 인해 스택 영역에 인접한 (일반적으로 이전) 메모리가 손상됩니다. 결과는 매우 예측할 수 없지만 대개 프로그램 카운터가 부 자연스럽게 변경됩니다. 이것은 종종 "잡초 속으로 점프"라고 불립니다. 물론이를 방지하는 유일한 방법은 모든 스레드 스택이 충분히 큰지 확인하는 것입니다.

  • Reentrancy

One of the real beauties of multi-threading is that the same C function can be called from multiple threads. This provides great power and also helps reduce code space. However, it does require that C functions called from multiple threads are reentrant.

What does reentrant mean? Basically, a reentrant function stores the caller’s return address on the current stack and does not rely on global or static C variables that it previously setup. Most compilers place the return address on the stack. Hence, application developers must only worry about the use of globals and statics.

An example of a non-reentrant function is the string token function “strtok” found in the standard C library. This function remembers the previous string pointer on subsequent calls. It does this with a static string pointer. If this function is called from multiple threads, it would most likely return an invalid pointer.

멀티 스레딩의 진정한 장점 중 하나는 동일한 C 함수를 여러 스레드에서 호출 할 수 있다는 것입니다. 이는 강력한 성능을 제공하고 코드 공간을 줄이는 데 도움이됩니다. 그러나 여러 스레드에서 호출 된 C 함수가 재진입 할 수 있어야합니다.

재진입이란 무엇을 의미합니까? 기본적으로 재진입 함수는 호출자의 반환 주소를 현재 스택에 저장하고 이전에 설정 한 전역 또는 정적 C 변수에 의존하지 않습니다. 대부분의 컴파일러는 스택에 반환 주소를 배치합니다. 따라서 응용 프로그램 개발자는 전역 및 정적 사용에 대해서만 걱정해야합니다.

재진입이 아닌 함수의 예는 표준 C 라이브러리에있는 문자열 토큰 함수 "strtok"입니다. 이 함수는 후속 호출에서 이전 문자열 포인터를 기억합니다. 정적 문자열 포인터로이를 수행합니다. 이 함수가 여러 스레드에서 호출되면 유효하지 않은 포인터를 반환 할 가능성이 높습니다.

  • Thread Priority Pitfalls

Selecting thread priorities is one of the most important aspects of multi-threading. It is sometimes very tempting to assign priorities based on a perceived notion of thread importance rather than determining what is exactly required during run-time. Misuse of thread priorities can starve other threads, create priority inversion, reduce processing bandwidth, and make the application’s run-time behavior difficult to understand.

스레드 우선 순위를 선택하는 것은 멀티 스레딩의 가장 중요한 측면 중 하나입니다.

런타임에 정확히 필요한 것이 무엇인지 결정하기보다는 스레드 중요도에 대한 인식 된 개념을 기반으로 우선 순위를 할당하는 것이 때로는 매우 유혹적입니다.

스레드 우선 순위를 잘못 사용하면 다른 스레드가 고갈되고 우선 순위 반전이 생성되며 처리 대역폭이 줄어들고 응용 프로그램의 런타임 동작을 이해하기 어려울 수 있습니다.

As mentioned before, ThreadX provides a prioritybased, preemptive scheduling algorithm. Lower priority threads do not execute until there are no Thread Execution 63 Express Logic, Inc. higher-priority threads ready for execution. If a higher-priority thread is always ready, the lowerpriority threads never execute. This condition is called thread starvation.

앞서 언급했듯이 ThreadX는 우선 순위 기반의 선점 스케줄링 알고리즘을 제공합니다.

우선 순위가 낮은 스레드는 스레드 실행이 없을 때까지 실행되지 않습니다. 63 Express Logic, Inc.
우선 순위가 더 높은 스레드를 실행할 준비가되었습니다.
우선 순위가 높은 스레드가 항상 준비되어 있으면 우선 순위가 낮은 스레드는 실행되지 않습니다.
이 상태를 스레드 부족이라고합니다.

Most starvation problems are detected early in debug and can be solved by ensuring that higher priority threads don’t execute ontinuously.

Alternatively, logic can be added to the application that gradually raises the priority of starved threads until they get a chance to execute.

대부분의 기아 문제는 디버그 초기에 감지되며 우선 순위가 더 높은 스레드가 지속적으로 실행되지 않도록하여 해결할 수 있습니다.

또는 응용 프로그램에 논리를 추가하여 실행 기회를 얻을 때까지 부족한 스레드의 우선 순위를 점차적으로 높일 수 있습니다.

Another unpleasant pitfall associated with thread priorities is priority inversion. Priority inversion takes place when a higher-priority thread is suspended because a lower-priority thread has a needed resource. Of course, in some instances it is necessary for two threads of different priority to share a common resource. If these threads are the only ones active, the priority inversion time is bounded by the time the lower-priority thread holds the resource. This condition is both deterministic and quite normal. However, if threads of intermediate priority become active during this priority inversion condition, the priority inversion time is no longer deterministic and could cause an application failure.

스레드 우선 순위와 관련된 또 다른 불쾌한 함정은 우선 순위 반전입니다.
우선 순위가 낮은 스레드에 필요한 리소스가 있기 때문에 우선 순위가 높은 스레드가 일시 중단 될 때 우선 순위 반전이 발생합니다.

물론 어떤 경우에는 우선 순위가 다른 두 스레드가 공통 리소스를 공유해야합니다.

이 스레드가 유일한 활성 스레드 인 경우 우선 순위 반전 시간은 우선 순위가 낮은 스레드가 자원을 보유하는 시간에 의해 제한됩니다. 이 조건은 결정적이며 매우 정상적입니다.

그러나 중간 우선 순위의 스레드가 이 우선 순위 반전 조건 동안 활성화되면 우선 순위 반전 시간이 더 이상 결정적이지 않으며 응용 프로그램 오류가 발생할 수 있습니다.

There are principally three distinct methods of preventing un-deterministic priority inversion in ThreadX. First, the application priority selections and run-time behavior can be designed in a manner that prevents the priority inversion problem. Second, lower-priority threads can utilize preemptionthreshold to block preemption from intermediate threads while they share resources with higherpriority threads. Finally, threads using ThreadX mutex objects to protect system resources may utilize the optional mutex priority inheritance to eliminate un-deterministic priority inversion.

ThreadX에서 비 결정적 우선 순위 반전을 방지하는 세 가지 다른 방법이 있습니다.

첫째, 우선 순위 반전 문제를 방지하는 방식으로 응용 프로그램 우선 순위 선택 및 런타임 동작을 설계 할 수 있습니다.

둘째, 우선 순위가 낮은 스레드는 preemptionthreshold를 활용하여 우선 순위가 높은 스레드와 리소스를 공유하는 동안 중간 스레드의 선점을 차단할 수 있습니다.

마지막으로 ThreadX 뮤텍스 객체를 사용하여 시스템 리소스를 보호하는 스레드는 선택적 뮤텍스 우선 순위 상속을 활용하여 비 결정적 우선 순위 반전을 제거 할 수 있습니다.

  • Priority Overhead

One of the most overlooked ways to reduce overhead in multi-threading is to reduce the number of context switches. As previously mentioned, a context switch occurs when execution of a higherpriority thread is favored over that of the executing thread. It is worthwhile to mention that higher-priority threads can become ready as a result of both external events (like interrupts) and from service calls made by the executing thread.

멀티 스레딩에서 오버 헤드를 줄이는 가장 간과되는 방법 중 하나는 컨텍스트 전환 수를 줄이는 것입니다.

앞서 언급했듯이 컨텍스트 전환은 실행중인 스레드보다 우선 순위가 높은 스레드의 실행이 선호 될 때 발생합니다.

우선 순위가 더 높은 스레드는 외부 이벤트 (인터럽트와 같은)와 실행 스레드가 수행 한 서비스 호출의 결과로 준비 될 수 있다는 점을 언급 할 가치가 있습니다.

To illustrate the effects thread priorities have on context switch overhead, assume a three thread environment with threads named thread_1, thread_2, and thread_3. Assume further that all of the threads are in a state of suspension waiting for a message. When thread_1 receives a message, it immediately forwards it to thread_2. Thread_2 then forwards the message to thread_3. Thread_3 just discards the message. After each thread processes its message, they go back and wait for another.

스레드 우선 순위가 컨텍스트 전환 오버 헤드에 미치는 영향을 설명하기 위해 thread_1, thread_2 및 thread_3이라는 스레드가있는 3 개의 스레드 환경을 가정합니다.
모든 스레드가 메시지를 기다리는 일시 중단 상태에 있다고 가정하십시오.

thread_1이 메시지를 받으면 즉시 thread_2로 전달합니다. 그런 다음 Thread_2는 메시지를 thread_3로 전달합니다. Thread_3은 메시지를 버립니다.

각 스레드가 메시지를 처리 한 후 다시 돌아가서 다른 스레드를 기다립니다.

The processing required to execute these three threads varies greatly depending on their priorities. If all of the threads have the same priority, a single context switch occurs between their execution. The context switch occurs when each thread suspends on an empty message queue.

이 세 개의 스레드를 실행하는 데 필요한 처리는 우선 순위에 따라 크게 다릅니다.
모든 스레드의 우선 순위가 동일한 경우 실행 사이에 단일 컨텍스트 전환이 발생합니다.
컨텍스트 전환은 각 스레드가 빈 메시지 큐에서 일시 중단 될 때 발생합니다.

However, if thread_2 is higher-priority than thread_1 and thread_3 is higher-priority than thread_2, the number of context switches doubles. This is because another context switch occurs inside of the tx_queue_send service when it detects that a higherpriority thread is now ready.

그러나 thread_2가 thread_1보다 우선 순위가 높고 thread_3이 thread_2보다 우선 순위가 높으면 컨텍스트 전환 수가 두 배가됩니다.
이는 우선 순위가 더 높은 스레드가 이제 준비되었음을 감지 할 때 tx_queue_send 서비스 내부에서 다른 컨텍스트 전환이 발생하기 때문입니다.

The ThreadX preemption-threshold mechanism can avoid these extra context switches and still allow the previously mentioned priority selections. This is a really important feature because it allows several thread priorities during scheduling, while at the same time eliminating some of the unwanted context switching between them during thread execution.

ThreadX 선점 임계 값 메커니즘은 이러한 추가 컨텍스트 전환을 피하고 이전에 언급 한 우선 순위 선택을 계속 허용 할 수 있습니다.
이것은 스케줄링 중에 여러 스레드 우선 순위를 허용하는 동시에 스레드 실행 중에 원치 않는 컨텍스트 전환을 제거하기 때문에 정말 중요한 기능입니다.

  • Debugging Pitfalls

Debugging multi-threaded applications is a little more difficult because the same program code can be executed from multiple threads. In such cases, a break-point alone may not be enough. The debugger must also view the current thread pointer _tx_thread_current_ptr to see if the calling thread is the one to debug.

다중 스레드 응용 프로그램을 디버깅하는 것은 동일한 프로그램 코드가 여러 스레드에서 실행될 수 있기 때문에 조금 더 어렵습니다.

이러한 경우 중단 점만으로는 충분하지 않을 수 있습니다.

디버거는 현재 스레드 포인터도 확인해야합니다.
tx_thread_current_ptr을 사용하여 호출 스레드가 디버깅 할 스레드인지 확인합니다.

Much of this is being handled in multi-threading support packages offered through various development tool vendors. Because of its simple design, integrating ThreadX with different development tools is relatively easy.

이 중 대부분은 다양한 개발 도구 공급 업체를 통해 제공되는 멀티 스레딩 지원 패키지에서 처리되고 있습니다.

단순한 디자인으로 인해 ThreadX를 다른 개발 도구와 통합하는 것은 비교적 쉽습니다.

Stack size is always an important debug topic in multi-threading. Whenever totally strange behavior is seen, it is usually a good first guess to increase stack sizes for all threads—especially the stack size of the last executing thread!

스택 크기는 항상 멀티 스레딩에서 중요한 디버그 주제입니다.
완전히 이상한 동작이 보일 때마다 일반적으로 모든 스레드의 스택 크기를 늘리는 것이 좋습니다. 특히 마지막 실행 스레드의 스택 크기를 늘리는 것이 좋습니다.

728x90
반응형