Learning and understanding of VxWorks (1)

Thank you predecessors for sharing, and attach the link: http://www.prtos.org/vxworks-wind-kernel-overview/

Foreword: I used to communicate with my fellow embedded engineers during VxWorks training, and found that everyone did not know much about the Wind kernel of the embedded VxWorks system, and there was no systematic interpretation and analysis of the Wind kernel of VxWorks on the Internet. So I decided to publish a series of blog posts to systematically interpret the design ideas of the Wind kernel. I chose the Wind 2.6 kernel of the VxWorks5.5 system (the code of this version is shared on the Internet O(∩_∩)O). During the analysis process, if you have any questions, you can leave a message for me. I further improved the blog post to make the later readers more fluent in reading.

This article first introduces the real-time operating system from the definition of the real-time kernel, and explains the characteristics of the real-time operating system, and then introduces the integral kernel, hierarchical kernel, and microkernel from the perspective of the function and structure of the kernel. Finally, the VxWorks Wind core with micro-kernel characteristics is introduced.

  1.1 Overview of real-time kernel

"Real-time" means that the control system can handle the external events that require control in the system in a timely manner. The reaction time from the occurrence of the event to the response of the system is called Latency . For real-time systems, one of the most important conditions is that the delay has a definite upper bound (such systems are deterministic systems). After satisfying this condition, the performance of different real-time systems can be distinguished according to the size of this upper bound. The "system" here is a functionally complete design from the viewpoint of system theory, capable of independent interaction with the external world and achieving expected functions. This includes three parts: real-time hardware system design, real-time operating system design, and real-time multitasking design. The latter two can be summarized as real-time software system design. Realizing the real-time system is the result of the organic combination of these three parts.

From another perspective, that is, the degree of real-time, the system can be divided into hard real-time systems and soft real-time systems. A hard real-time system is a system whose time requires a definite deadline (Deadline). The response beyond the deadline, even if the calculation is correct, is an intolerable error result and usually causes serious consequences. Such a system Belongs to a hard real-time system. For a soft real-time system, "real-time" is only the concept of "degree". When submitting operating system services such as interruption, timing, and scheduling, the system defines a delay within a time range. Within this range, the earlier the response is, the more valuable it is. As long as it does not exceed the range, the value of the result given later will decrease, but it can be tolerated.

Therefore, an RTOS kernel must meet the basic requirements of many specific real-time environments, including:

  • Multitasking: Due to the asynchronous nature of real-world events, it is important to be able to run many concurrent processes or tasks. Multitasking provides a better match to the real world because it allows multithreaded execution corresponding to many external events. The system kernel allocates CPU to these tasks to obtain concurrency.
  • Preemptive scheduling: Real-world events have inherited priorities, and these priorities must be paid attention to when allocating CPUs. Priority-based preemptive scheduling, tasks are assigned priority, among the tasks that can be executed (not suspended or waiting for resources), the task with the highest priority is allocated CPU resources. In other words, when a high-priority task becomes an executable state (called Ready State in the Wind kernel), it will immediately preempt the currently running lower-priority task.
  • Fast and flexible communication and synchronization between tasks: In a real-time system, there may be many tasks performed as part of an application. The system must provide a fast and powerful communication mechanism between these tasks. The kernel should also provide the synchronization mechanism needed to effectively share non-preemptible resources or critical sections.
  • Convenient communication between tasks and interrupts: Although real-world events usually come as interrupts, in order to provide effective queuing, prioritization and reduce interrupt delays, we usually hope to handle the corresponding work at the task level (uC/OS -III kernel uses this strategy). So there needs to be communication between the task level and the interrupt level.
  • Performance boundary: A real-time kernel must provide worst-case performance optimization, not performance optimization for throughput. We prefer a system that can always execute a function at 50 microseconds (us), and we don't expect the system to execute the function at 10 microseconds (us) on average, but occasionally execute it at 75 microseconds (us).
  • Special considerations: Due to the increased requirements for real-time kernels, the requirements for the kernel to support the ever-increasing complex functions must be considered. This includes multi-process processing (such as VxWorks RTP), and support for newer, more powerful processor architectures (such as Multicore CPU).

1. Real-time hardware system design

Real-time hardware system design is the basis of the other two parts. Real-time hardware system design requires that sufficient processing capacity must be provided on the premise that the software system is fully efficient. For example, the interrupt processing logic provided by the hardware system can respond to the number of external events, hardware response time, memory size, processor computing power, bus capacity, etc., to ensure that all calculations are still completed in the worst case. The multi-processing hardware system also includes the internal communication rate design. When the hardware system cannot guarantee to meet the real-time requirements, you can be sure that the entire system is not real-time. At present, various hardware speeds continue to increase, and advanced technologies are emerging in large numbers. In most applications, hardware is no longer the bottleneck of real-time systems. Therefore, the key to real-time systems is the design of real-time software systems. This aspect has also become the main content of real-time research, and it is also the most complicated part. In many occasions, there is even no distinction between real-time hardware systems and real-time operating systems.

1. Real-time operating system design

Let's first look at the main indicators of performance evaluation of real-time operating systems:

  • Interrupt delay time: For a real-time operating system, the most important indicator is how long the interrupt is off. All real-time systems must turn off the interrupt before entering the critical code segment, and then turn on the interrupt after the critical code is executed. The longer the interrupt is off, the longer the interrupt delay. Therefore, the interrupt delay time can be expressed as the sum of the longest time to close the interrupt and the time to start executing the first instruction of the interrupt service subroutine. Sometimes it is also expressed as the time from when the system receives the interrupt signal to the operating system responds and enters the interrupt service routine. time.
  • Interrupt response time: The interrupt response time is defined as the time from the occurrence of an interrupt to the beginning of the execution of the user interrupt service subroutine code to handle the interrupt. The interrupt response time includes all the overhead before starting to process the interrupt. The interrupt response time includes the interrupt delay time, so when considering the processing time of a real-time system for external interrupts, it usually refers to the interrupt response time. Typically, the scene is protected before the user code is executed, and the time to push each register of the CPU onto the stack is recorded as the interrupt response time.
  • Task switching time: the time it takes to switch between multiple tasks, that is, the time period from the first instruction of the previous task that saves the context to the time that the next task resumes the context and starts to run the first instruction.

From the perspective of real-time performance, the operating system has experienced three stages: front-end and back-end systems, time-sharing operating systems, and real-time operating systems.

The Foreground/Background System does not actually have an operating system. There is only an infinite main loop running in the system, and there is no concept of multitasking, but it responds to external events through an interrupt service routine. In the front-end and back-end systems, the real-time response characteristics to external events are viewed from two aspects.

  • Interrupt delay time: The main loop generally keeps the interrupt open state, so the front and back system interrupt response is very fast, and nesting is usually allowed;
  • Interrupt response time: It takes a main loop to process the external request collected in the interrupt service program, so the system response time is determined by the main loop period.

The Time-sharing Operating System divides the computing power of the system into time slices and assigns them to various tasks according to certain strategies. Generally, a certain sense of fairness is pursued in the distribution process. The time-sharing operating system does not guarantee real-time performance.

The purpose of the real-time operating system (RTOS) is to achieve real-time response to external events, that is, according to the previous definition of real-time, the real-time operating system must respond within a certain time. The real-time operating system must meet the following 4 conditions:

  • Preemptible priority scheduling kernel. When a running task makes a task with a higher priority than it enters the ready state, the CPU usage rights of the current task are deprived, or suspended, the high priority The task of the first level immediately gets the control of the CPU, that is, the task with the highest priority is always guaranteed to have the right to use the CPU, as shown in Figure 1.1

VxWorks kernel interpretation-1

  • Interrupts have priority, and interrupts can be nested, that is, high-priority interrupts can interrupt the low-priority interrupt handler that is being executed, give priority to execution, and wait until the processing is completed before returning to the low-priority task to continue execution.
  • To prevent priority inversion, the priority of the system service is determined by the priority of the task requesting the service, and has a priority protection mechanism to prevent priority inversion; priority inversion refers to when a high-priority task is forced to wait for one Priority inversion will occur when low-priority tasks have an indefinite completion time. Consider the following scenario: Tasks T1, T2, and T3 are three tasks with decreasing priority. T3 has acquired these resources by acquiring semaphores related to the protection of resources. When T1 preempts T3 to compete for these resources by acquiring the same semaphore, T3 will be blocked. If we can ensure that the blocking time of T1 will not be longer than the time that these resources are released after T3 is executing, the situation will not be particularly serious. After all, the resource occupied by T1 is not preemptible. But low-priority tasks are fragile and are usually preempted by medium-priority tasks. For example, at this time T2, this will prevent T3 from releasing the resources required by T1. This situation will always exist and will make the blocking time of T1 at the highest priority uncertain. The schematic diagram is as shown in 1.2.

VxWorks kernel interpretation-1

1.2 Schematic diagram of priority inversion

One solution to the above problem is to use the Priority Inheritance Protocol. When a high-priority task requires a low-priority task to occupy resources, increase the priority of the low-priority task to the same level as the high-priority task, which is equivalent to the low-priority task inheriting the priority level of the high-priority task; prevent Another protocol for priority flipping is Priority Ceiling. Its design strategy is to take "prevention" rather than "remedy" for priority flipping. That is to say: No matter whether high-priority tasks are blocked or not, the tasks that hold the protocol are given the priority occupied by the priority ceiling during execution, so that the task can complete the operation as soon as possible, so the task priority ceiling can be regarded as It is a more "active" way to protect priority. We will analyze the design and implementation of the two solutions in detail in the following blog posts.

  •  The performance evaluation index (interrupt response time and task switching time) of the real-time operating system (RTOS) has a fixed upper bound.

After the above four necessary conditions are met, the specific implementation mechanism of the RTOS kernel determines its real-time performance. The Wind kernel of VxWorks is a real real-time microkernel that meets the above conditions. At the same time, the wind kernel adopts a single real-time address space, and the task switching overhead is very low, which is equivalent to switching to another thread in the same process on a host such as UNIX, and there is no system call overhead. Efficient real-time design enables Wind core to show excellent real-time performance in many fields from industrial field control to national defense and aviation (strictly speaking, the VxWorks 653 version used in the aviation field).

1.2 Design Concept of Microkernel Operating System

Traditionally, an operating system is divided into core mode and user mode. The kernel runs in the core mode and provides services for user-mode applications. The kernel is the soul and center of the operating system, which determines the efficiency and application areas of the operating system. When designing an operating system, what functions the kernel contains and what organizational structure the kernel functions adopt are all determined by the designer. From the perspective of kernel function and structural characteristics, the operating system has three different forms: a single kernel, a hierarchical kernel, and a micro kernel.

The monolithic kernel is written as a collection of processes and linked into a large executable binary program. Using this technology, each process in the system can freely mobilize other processes, and only the latter provides some useful calculation work required by the former. These thousands of procedures that can call each other without restriction often lead to a clumsy and incomprehensible system. The operating system of the monolithic kernel structure does not carry out any data encapsulation and hiding. While it has higher efficiency, it has the disadvantage of being difficult to expand and upgrade.

The operating system of the hierarchical kernel structure divides the module functions into different levels, the lower module encapsulates the internal details, and the upper module calls the interface provided by the lower module. Unix, Linux, Multics, etc. belong to the hierarchical structure operating system. Hierarchy makes the operating system structure simple, easy to debug and expand. The monolithic kernel and hierarchical kernel structure are shown in Figure 1.3.

VxWorks kernel interpretation-1

 

1.3 Overall kernel and hierarchical kernel structure diagram

Regardless of the monolithic structure or the hierarchical structure, their operating systems include many functions that are needed when they are used in various possible fields, so they are called the monolithic kernel operating system, and the entire core program is It runs as the Kernel Space and Supervisor Mode, so that it can be considered that the kernel itself is a complete operating system. Take UNIX as an example. Its kernel includes functions such as process management, file system, device management, and network communication. The user layer only provides an operating system shell and some utility programs.

To design an operating system with a hierarchical approach, the designer needs to determine where to divide the kernel-user boundary. Traditionally, all layers are in the kernel, but this is not necessary. In fact, it is better to reduce kernel mode functions as much as possible, because errors in the kernel will quickly drag down the system; on the contrary, user-level tasks can be set to smaller permissions, so that the consequences of a certain error will not be fatal. The design idea of ​​the microkernel is to divide the operating system into small, well-defined modules in order to achieve its high reliability. Only one of the modules (microkernel module) runs in the kernel mode, and the remaining modules are relatively weak due to their relatively weak functions. , It runs as a normal user task. In particular, since each device driver and file system are treated as common tasks, although errors in these modules will cause these modules to crash, they will not crash the entire system. For example, an error in the audio driver will make the sound intermittent or stop, but it will not crash the computer system. On the contrary, in the macro-kernel operating system, since all the device drivers are in the kernel, a faulty audio driver can easily lead to invalid address references and cause annoying system downtime.

There are many microkernels that have been implemented and put into use. Microkernels are particularly popular in real-time, industrial, aerospace, and military applications because these fields are mission-critical and require a high degree of reliability. Therefore, embedded operating systems mostly adopt microkernel structure. The microkernel operating system is a newly developed technology in the past two decades. The kernel is very small but highly efficient, ranging from tens of KB to hundreds of KB bytes, and is suitable for embedded applications with relatively limited resources. The microkernel separates many common operations functions from the kernel (such as file systems, device drivers, network protocol stacks, etc.), and only retains the most basic content. Well-known kernels include the INTEGRITY real-time operating system developed by Green Hills Software, vxWorks developed by windriver systems, the QNX real-time system used by BlackBerry manufacturer RIM (Research In Motion Ltd., RIM), and L4, PikeOS, Minix3 etc.

Remarks: From the perspective of the micro-kernel module running in the CPU core state, and other modules running in the non-core state, from this point of view, the Wind kernel of VxWorks cannot be regarded as a micro-kernel system in the strict sense. The Wind kernel simulates the privileged state of the Wind kernel through the global variable kernelState. The routines beginning with wind* in the Wind kernel constitute the core service routines of the Wind kernel. When the kernelState is FALSE, it means that the Wind kernel has no program access at this time, and the peripheral modules of VxWorks can debug the core service routines of the Wind kernel; when the kernelState is TRUE, it means that other programs are using kernel state routines and need to be called currently The program of the core service routine must be placed in the delay queue until the program in the core state exits the kernel state (that is, kernelState is FALSE), the delayed kernel state routine will be executed. From here we can see that the kernelState simulates The core state is non-preemptive. The wind kernel of VxWorks uses global variables to simulate core state service routines. In addition to its highly configurable type, it also has the characteristics of a general microkernel operating system.

It is generally believed that the microkernel operating system has the following advantages:

  •  Uniform interface, no need to identify between user mode and core mode;
  • Good scalability, easy to expand, and can adapt to hardware updates and application changes;
  • Portability is good, the operating system needs to be transplanted to different hardware platforms only by modifying very few codes in the microkernel;
  • The real-time performance is good, the kernel response speed is fast, and it can conveniently support real-time processing;
  • The safety and reliability are high. The microkernel takes safety as an internal feature of the system to design, and only a few application programming interfaces are used externally.

Because the core of the operating system is resident in memory, and the microkernel structure simplifies the core functions of the operating system, the kernel scale is relatively small, and some functions have been moved to the external memory, so the microkernel structure is very suitable for embedded dedicated systems, as shown in Figure 1- The VxWorks system structure shown in 4, everyone can intuitively feel the position of the Wind kernel in the VxWorks system O(∩_∩)O.

 

VxWorks kernel interpretation-1

Figure 1-4 VxWorks system structure

1.3 Wind microkernel design

In order to improve the efficiency of the microkernel, there are two implementation modes: a protected virtual address space mode and an unprotected single real address space mode. The former is used in macro-kernel operating systems (such as Unix) and some micro-kernel operating systems (such as QNX, Minix3). The advantages of this model are obvious: tasks run independently, are not affected by other task errors, and the system is highly reliable.

The Wind kernel of VxWorks adopts a single real address space mode, all tasks run in the same address space, without distinguishing between core state and user state. Its advantages are:

  • There is no need to switch the virtual address space during task switching;
  • Variables can be shared directly between tasks, without the need to copy data in different address spaces through the kernel;
  • There is no need to switch between the core mode and the user mode when the system is called, which is equivalent to a direct function call.

Remarks: The system call needs to switch from the user mode to the core mode to perform operations that cannot be performed in the user mode. On many processors, this is implemented by a TRAP instruction equivalent to the system call, which must be passed before executing the instruction Strict parameter inspection. There is no such switch in VxWorks, so there is no difference between system calls and general function calls. But the general term is still used in this series of blog posts.

Proponents of the two models have had a lot of debate. Comparing things that have their own strengths is often very difficult. This article tends to think that for embedded real-time applications, a single real address mode is more appropriate. Many practices have also proved that there are always limitations to relying on virtual address protection to improve reliability. After all, errors have occurred in program operation. To a certain extent, virtual address protection is just a process of delay, accumulation and amplification of errors that have occurred. I have this feeling after using Windows. The VxWorks operating system, which has been tested by a large number of key applications, has been fully proven to be highly reliable (of course, a reliable system must consist of a reliable operating system and a reliable application system).

The hierarchical Wind kernel only provides service routines for multitasking environment, inter-process communication and synchronization functions. These service routines are sufficient to support the rich performance requirements provided by VxWorks at a higher level. The operation of the Wind core of VxWorks is invisible to the user. In order to implement task management and synchronization that require kernel participation, the application uses some system calls, but the processing of these calls is invisible to the calling task. The application program only links the appropriate VxWorks routines (usually using the dynamic link function of VxWorks), and issues system calls just like calling subroutines. This kind of interface is not like the Linux kernel which needs a jump table interface, the user needs to specify a kernel function call through an integer.

1.4 Wind core design ideas

VxWorks adopts the idea of ​​class and object to divide the Wind kernel into 5 components: task management module, memory management module, message queue management module, semaphore management module, and watchdog management module.

Remarks: In addition to the above five modules, there are also the virtual memory management interface (VxVMI) module and the TTY ring buffer management module, which are also two modules managed by the Wind kernel with class and object thinking. The virtual memory interface module (VxVMI) is a functional module of VxWorks. It uses the memory management unit (MMU) on the user chip or on the board to provide users with advanced memory management functions; the TTY ring buffer management module is the ttyDrv device At the core, the ttyDrv device is called a virtual device, which forms a conversion layer between the I/O system and the real driver, and provides a standard I/O interface for VxWorks. A virtual ttyDrv device can manage multiple serial devices. driver. These two modules are not a theoretical part of the RTOS kernel in the strict sense, so they are not listed as part of the Wind kernel.

In the wind kernel, all objects are part of the class. The class defines the method of operating the object (Method) and maintains the operation record of all the objects at the same time. The Wind core uses the semantics of C++, but it is implemented in the C language. The entire Wind kernel is realized through explicit coding, and its compilation process does not depend on a specific compiler. This means that the Wind kernel can not only be compiled on the diab compiler that comes with vxWorks, but also can use the open source gnu/gcc compiler. VxWorks designed a meta-class for the Wind core, and all object classes (Obj-class) are based on this meta-class. Each object class is only responsible for maintaining the operation methods of its respective object (Object) (such as creating an object, initializing an object, canceling an object, etc.), and managing statistical records (such as the data of a created object, the number of destroyed objects, etc.). Figure 1.5 realizes the logical relationship between metaclasses, object classes, and objects.

VxWorks kernel interpretation-1

Figure 1-5 Diagram of the relationship between metaclasses, object classes, and objects

Remarks: Every time I draw this kind of class and object relationship diagram, I get very entangled. Because in the design of the wind kernel, the metaclass classClass has an ID number classClassId, and each object class X-objClass also has an ID number X-objClassId. In the implementation of C language, classClassId and X-objClassId are pointer variables, which store the address of the corresponding class. When the object class and object are initialized, the value of the pointer variable stored in the objCore field of the object class and object (that is, the address of the corresponding class) has nothing to do with classClassId. Figure 1.5 truly reflects the real relationship between the class addresses stored by objCore.

From a logical point of view, Figure 1.6 looks more comfortable, although from the C language implementation, the role of classClassId and X-objClassId is wrong, but from a logical point of view Figure 1.6 can describe the problem more clearly (and the figure also More beautiful O(∩_∩)O), although the pointer to the class address stored in objCore in Figure 1.6. Because of this, when drawing class and object relationship diagrams, I use the method shown in Figure 1-6.

VxWorks kernel interpretation-1

Figure 1-6 Diagram of the relationship between metaclasses, object classes, and objects

As shown in Figure 1-5 and Figure 1-6, each object class points to the metaclass classClass, and each object class is only responsible for managing its own objects. The complete metaclass, object class, and logic key between objects in the Wind core are shown in Figure 1-7.

VxWorks kernel interpretation-1

Figure 1-7 The relationship between object classes, objects, and metaclasses among the components of the wind kernel

Remarks: The class management mode is not a feature of the Wind kernel. From a functional point of view, it is just a means for the Wind kernel to organize various modules. The object classes and class objects of all kernel modules depend on it. VxWorks uses classes and objects to organize the structure of the operating system. One of the most important advantages is to increase the security of the code, that is, when creating new object classes and objects, and deleting object classes and objects, the object classes and objects can be verified. .

1.5 Features of the Wind core

The Wind kernel of VxWorks naturally has four characteristics common to all RTOS described in section 1.2, all of which can be summarized as follows:

  • Tailorable micro-kernel design;
  • Concurrent execution of multiple tasks;
  • Preemptible priority scheduling algorithm;
  • Optional time slice rotation algorithm;
  • Inter-task communication and synchronization mechanism;
  • Fast context switching time;
  • Low interrupt latency;
  • Fast interrupt response time;
  • Can be nested interrupt support;
  • 256 task priorities;
  • Task deletion protection;
  • Priority inheritance
  • Based on function call, no trap instruction and jump table are used;
  • The VxWorks kernel runs in processor privilege mode;
  • The VxWorks kernel is implemented in layers. The core library Wind kernel of VxWorks is in the core state and is protected by the global management kernelState.

Note: The next part of this series of blog posts will analyze in detail how the wind kernel is designed to have these characteristics.

A few more nonsense O(∩_∩)O: The purpose I uphold when analyzing the Wind kernel is the principle of separating the policy (Mechanism) and the mechanism (Policy). The separation of the policy (Mechanism) and the mechanism (Policy) is micro The guiding ideology of kernel design, in other words, the guiding principle of microkernel operating system design is to provide mechanisms rather than strategies. To illustrate this point more clearly, let's take task scheduling as an example. A simple scheduling algorithm is to assign a priority to each task and let the kernel execute the ready task with the highest priority. In this example, the mechanism (Mechanism) is to find the highest priority ready task in the kernel and run it; and the policy (Policy) is to give the task the corresponding priority. In other words, the mechanism is responsible for providing what kind of functions, and the strategy is responsible for how to use these functions. The guiding ideology of separating strategy and mechanism can make the operating system kernel smaller and more stable. As a saying goes, "A beautiful kernel is not about what other functions can be added, but what other functions can be reduced" (buddy forgot who said ⊙﹏⊙b Khan).

This series of blog posts strives to consider the source of RTOS kernel design ideas while analyzing and researching the Wind kernel. The Wind kernel of VxWorks has experienced nearly 20 years of development and perfection, and has reached the current stable state. With the current design, there must be internal considerations. I hope to analyze the design philosophy, working mechanism, and characteristics of the Wind kernel from the perspective of a system designer as much as possible, and design an excellent RTOS for us. The kernel provides reference!

To be continued...O(∩_∩)O haha~

Guess you like

Origin blog.csdn.net/qq543716996/article/details/105246104