First, public number [learn embedded together], focus on RTOS, Linux, C

The kernel is the most basic and important part of an operating system. From this article to enter the rT-Thread kernel related knowledge learning.

First understand the basic knowledge of the kernel, rT-Thread kernel design has a preliminary understanding.

Then take a look at the rT-Thread system startup process.

The kernel is introduced

The following figure shows the kernel architecture of RT-Thread:

The kernel consists of two parts: kernel library and real-time kernel implementation.

The kernel library

To ensure that the kernel can run independently, Rt-Thread designed a small subset of functions that are similar to C libraries, depending on the compiler’s C library.

The rt-Thread kernel service library only provides the implementation of C library functions used by the kernel. To avoid the same name as the standard C library, these functions are prefixed with “rt_”. The SRC /kservice.c functions are defined as follows:

void *rt_memset(void *s, int c, rt_ubase_t count) void *rt_memcpy(void *dst, const void *src, rt_ubase_t count) rt_int32_t rt_memcmp(const void *cs, const void *ct, rt_ubase_t count) char *rt_strstr(const char *s1, const char *s2) rt_size_t rt_strlen(const char *s) rt_int32_t rt_snprintf(char *buf, rt_size_t size, const char *fmt, ...). rt_int32_t rt_sprintf(char *buf, const char *format, ...)Copy the code

The kernel implementation

The implementation of real-time kernel includes: object management, thread management, scheduler, interthread communication, clock management, memory management, and so on. The minimum resource footprint of the kernel is 3KB ROM and 1.2KB RAM.

Thread 1.

Thread is the smallest scheduling unit in RT-Thread operating system. The Thread scheduling algorithm is a full preemptive multi-thread scheduling algorithm based on priority.

Rt-threads support 256 Thread priorities, which can also be changed to support a maximum of 32 or 8 Thread priorities through the configuration file. The 0 priority indicates the highest priority, and the lowest priority is reserved for idle threads.

Rt-thread supports the creation of multiple threads with the same priority. Scheduling between threads of the same priority, using a time slice rotation algorithm, so that each thread runs the set time.

The time for the scheduler to switch to the highest ready thread is constant, and there is no limit to the number of threads. The number of threads depends only on the specific memory of the hardware platform.

2. Manage the clock

Clock management for RT-Thread is based on the clock beat, which is the smallest clock unit in the RT-Thread operating system.

Rt-thread provides two types of timer mechanisms:

  • Single trigger timer. After the timer is started, it will trigger the timer event only once and then automatically stop.
  • Periodic trigger timer. These timers periodically trigger timer events until the user manually stops the timer.

3. Synchronize between threads

Rt-threads use semaphores, mutex, and event sets to synchronize threads.

Threads synchronize the acquisition and release of semaphores and mutex. Thread synchronization allows threads to acquire semaphores or mutex by priority waiting or on a first-in, first-out basis.

Threads synchronize the sending and receiving of events; Event sets support “or firing” and “and firing” of multiple events, suitable for threads waiting for multiple events.

4. Communication between threads

Rt-threads support communication mechanisms such as mailboxes and message queues.

The length of an email in a mailbox is fixed at 4 bytes. Message queues can receive messages of varying length and cache messages in their own memory space.

5. Memory management

Rt-thread supports static memory pool management and dynamic memory heap management.

Dynamic memory heap management module provides memory management algorithm for small memory system and SLAB memory management algorithm for large memory system under different system resources.

There is also a type of dynamic memory heap management called memheap, which applies to the system’s memory heap that has multiple addresses and is not contiguous. Using memheap, multiple memory heaps can be “pasted” together, allowing users to operate as if they were operating on a single memory heap.

Start-up process analysis

The startup process of RT-Thread is different from that of other RTOS. Many RTOS start the entry function as main, and rT-Thread is initialized before the main function runs. The main function acts as an entry point for the user program.

The system starts from the startup file, enters rtthread_startup(), and enters main().

Taking MDK-ARM as an example, rT-Thread starts the process, as shown in the following figure:

After the system starts, it starts from assembly code startup_xx.sStart running, then jump to C code, rT-Thread system startup, and finally enter the user program entrymain().

1. The extensionmain()

Rt-threads use MDK extensions $Sub$$and $Super$$to initialize system functionality before entering main(). For $Sub$$and $Super$$extensions, see Arm Compiler for Embedded Reference Guide Version 6.17.

https://developer.arm.com/documentation/101754/0617/armlink-Reference/Accessing-and-Managing-Symbols-with-armlink/Use-of --Super---and--Sub---to-patch-symbol-definitions?lang=en

The content of the guidance document is as follows:

$Sub$$main (); $Sub$$main (); $Sub$$main ();

Then after calling $Super$$main, the main() function executes. This will reduce the user’s workload without having to worry about system initialization before main(). Focus on completing the functional modules required by users.

The source code can be found in the SRC /components.c file:

extern int $Super$$main(void);
/* re-define main function */
int $Sub$$main(void)
{
  rtthread_startup();
  return 0;
}
Copy the code

$Sub$$main calls rtthread_startup(), and rtthread_startup() starts the rt-thread.

Int rtthread_startup(void) {/* Disable global interrupts */ rt_hw_interrupt_disable(); /* Hardware configuration initialization */ rt_hw_board_init(); */ rt_show_version(); /* Timer system initialization */ rt_system_timer_init(); /* Thread scheduler initialization */ rt_system_scheduler_init(); #ifdef RT_USING_SIGNALS /* rt_system_signal_init(); #endif */ rt_application_init(); /* Timer thread initialization */ rt_system_timer_thread_init(); /* free thread initialization */ rt_thread_idle_init(); #ifdef RT_USING_SMP rt_hw_spin_lock(&_cpus_lock); #endif /*RT_USING_SMP*/ * rt_system_scheduler_start(); /* never reach here */ return 0; }Copy the code

This part of the startup code can be roughly divided into four parts:

  • Initialize system-related hardware;
  • Initialize system kernel objects, such as timers, schedulers, signals;
  • Create a main thread, in the main thread for each module initialization;
  • Initialize the timer thread, idle thread, and start the scheduler.

2. Enter the main ()

The rt_application_init() function is called in the rtthread_startup() function, which creates an initialization thread, the user thread.

The thread’s entry function is main_thread_entry(), which calls $Super$$main() to enter main().

/* system main_thread_entry(void *parameter) {extern int main(void); extern int $Super$$main(void); RT_USING_COMPONENTS_INIT (); RT_USING_COMPONENTS_INIT (); #endif #ifdef RT_USING_SMP rt_hw_secondary_cpu_up(); # endif / * system main function called main () * / # if defined (__CC_ARM) | | defined (__CLANG_ARM) $Super $$main (); /* for ARMCC. */ #elif defined(__ICCARM__) || defined(__GNUC__) main(); #endif }Copy the code

Automatic initialization

Rt-thread has an automatic initialization mechanism: The initialization function does not need to be called. It only needs to be declared in the function definition by means of macro definition and can be executed during system startup.

The automatic initialization mechanism for RT-Thread uses a custom RTI symbol segment into which the macro definition puts Pointers to functions that need to be initialized at startup to form a table of initialized functions. During system startup, the system traverses the table and calls functions in the table to achieve automatic initialization.

Sample code:

Int rt_hw_usart_init(void) /* string initialization function */ {... . / * * registered serial port 1 equipment/rt_hw_serial_register (& serial1, "uart1 RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, uart); return 0; } INIT_BOARD_EXPORT(rt_hw_usart_init); /* Make the component automatic start machine */Copy the code

Rt_hw_usart_init () is called automatically.

Rt_components_board_init (); rt_Components_init ();

Initialization sequence number function Function declaration macros describe
1 board init functions INIT_BOARD_EXPORT(fn) Very early initialization, before the scheduler is started
2 pre-initialization functions INIT_PREV_EXPORT(fn) Functions that are primarily used for pure software initialization without many dependencies
3 device init functions INIT_DEVICE_EXPORT(fn) Peripheral driver initialization is relevant, such as network card devices
4 components init functions INIT_COMPONENT_EXPORT(fn) Component initialization, such as file system or LWIP
5 enviroment init functions INIT_ENV_EXPORT(fn) Initialize the system environment, such as mounting a file system
6 application init functions INIT_APP_EXPORT(fn) Initialization of applications, such as GUI applications

The rT_componentS_board_init () function iterates through the list of initialization functions declared by INIT_BOARD_EXPORT(fn) and calls each function to initialize the hardware environment. The function code is as follows:

void rt_components_board_init(void) { const init_fn_t *fn_ptr; for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++) { (*fn_ptr)(); }}Copy the code

Rt_components_init () is called in the main thread after the system is up and running. The hardware environment and operating system are initialized and the application code can be executed. Rt_components_init () iterates through the list of initialization functions declared by the remaining macros.

void rt_components_init(void) { const init_fn_t *fn_ptr; for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++) { (*fn_ptr)(); }}Copy the code

__rt_init_rti_board_start, __rt_init_rti_board_end, and __rt_init_rti_end are implemented by rT-thread internal macro definitions, which are summarized as follows:

#define INIT_EXPORT(fn, level) \ RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn." level) = fn /* board init routines will be called in board_init() function */ #define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1") /* components pre-initialization (pure software initilization) */ #define INIT_PREV_EXPORT(fn) INIT_EXPORT(fn, "2") /* device initialization */ #define INIT_DEVICE_EXPORT(fn) INIT_EXPORT(fn, "3") /* components initialization (dfs, lwip, ...) */ #define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "4") /* environment initialization (mount disk, ...) */ #define INIT_ENV_EXPORT(fn) INIT_EXPORT(fn, "5") /* appliation initialization (rtgui application etc ...) */ #define INIT_APP_EXPORT(fn) INIT_EXPORT(fn, "6") static int rti_start(void) { return 0; } INIT_EXPORT(rti_start, "0"); /* Implement __rt_init_rti_board_start */ static int rti_board_start(void) {return 0; } INIT_EXPORT(rti_board_start, "0.end"); /* Implement __rt_init_rti_board_end */ static int rti_board_end(void) {return 0; } INIT_EXPORT(rti_board_end, "1.end"); /* Implement __rt_init_rti_end */ static int rti_end(void) {return 0; } INIT_EXPORT(rti_end, "6.end");Copy the code

The core of the macro definition in the automatic initialization mechanism is INIT_EXPORT. This macro definition involves the following macro definitions:

#define SECTION(x) __attribute__((SECTION(x))) *Copy the code

In the macro definition, the two symbols ## are used to concatenate two strings. The macro defines INIT_EXPORT to concatenate the string __rt_init_ with the function name string fn.

The section keyword defines variables into the specified input segment. The macro definition puts __rt_init_fn in the specified segment.

OK, I’ll stop there for today and continue next time. Come on ~