Method for guaranteeing hard real-time in linux user mode based on multi-core cpu
By configuring a fully preemptible kernel in Linux user space, binding user threads to multi-core CPUs, setting high-priority scheduling and memory locking, and combining various optimization techniques, the problem of response uncertainty of hard real-time tasks in Linux user space is solved, and efficient and predictable real-time task execution is achieved.
Patent Information
- Authority / Receiving Office
- CN · China
- Patent Type
- Patents(China)
- Current Assignee / Owner
- Filing Date
- 2025-09-28
- Publication Date
- 2026-03-24
AI Technical Summary
Existing Linux operating systems lack the reliability to perform hard real-time tasks in user space, exhibiting uncertain response times and latency issues. In particular, the latency and interference caused by load balancing and memory management mechanisms are difficult to resolve in multi-core environments.
By configuring the Linux kernel to fully preemptive mode, creating user threads equal to the number of CPU cores and binding them to different cores, setting the SCHED_FIFO scheduling policy and high priority, locking the stack space of user threads, isolating the real-time scheduling core through kernel parameters, and combining timerfd, eventfd mechanisms and memory mapping technology, the execution environment of user-mode tasks is optimized.
It significantly improves the responsiveness and determinism of user-mode real-time tasks, avoids delays in task migration and memory page swapping, and enhances system integration and resource utilization. It is suitable for industrial control and communication fields with extremely high real-time requirements.
Smart Images

Figure CN121187791B_ABST
Abstract
Description
Technical Field
[0001] This invention relates to the field of embedded systems technology, and in particular to a method for ensuring hard real-time operation in Linux user space based on a multi-core CPU. Background Technology
[0002] The Linux operating system has been widely used in embedded devices, industrial control, and communication equipment due to its advantages such as being open-source, feature-rich, supporting a wide range of hardware, and conforming to the POSIX standard. However, Linux is essentially a time-sharing operating system, designed to prioritize overall system throughput and fairness rather than real-time performance. This results in inherent limitations when handling applications with strict response time requirements.
[0003] In many industrial automation, robotics, audio / video processing, and real-time communication scenarios, systems need to reliably respond to external events or timed cycles within milliseconds or even microseconds—that is, possess "hard real-time" capabilities. While some proprietary real-time operating systems (RTOS) exist, their ecosystems, ease of development, and hardware support are often inferior to Linux. Therefore, improving the real-time performance of Linux's user-space applications while retaining its powerful functionality and ease of use is of significant importance and value.
[0004] In existing technologies, efforts to improve Linux real-time performance have primarily focused on the kernel level, such as threading interrupts and replacing spinlocks with sleepable mutexes to increase kernel preemptibility. This provides a certain foundation for real-time performance. However, even with real-time patches applied to the kernel, achieving reliable hard real-time task execution in user space still faces numerous challenges.
[0005] Standard Linux scheduling policies (such as CFS) prioritize fairness, but high-priority user tasks can still be disrupted by kernel activities (such as soft interrupts and kernel threads). Load balancing mechanisms in multi-core environments cause tasks to migrate between CPU cores, leading to cache misses and unpredictable latency. Linux's virtual memory management mechanism can cause page faults, especially page faults, during real-time task execution, with latency reaching milliseconds, which is fatal for tasks requiring microsecond-level responses. System timer interrupts periodically interrupt task execution, introducing unnecessary overhead and timing jitter. Finally, the switching between user mode and kernel mode, as well as system calls themselves, also incur significant overhead.
[0006] Therefore, even after adopting real-time kernel patches, existing technologies still suffer from insufficient reliability and uncertain response times when implementing hard real-time tasks in user space. There is an urgent need for a systematic approach that can comprehensively utilize the various mechanisms provided by the kernel to optimize from multiple dimensions, creating a clean and predictable execution environment for user-space hard real-time tasks. Summary of the Invention
[0007] This invention overcomes the problem of poor real-time performance of Linux user-space tasks caused by kernel non-preemption, scheduling delay, memory paging, and system interference. Through multi-level collaborative optimization, it ensures extremely low latency and high determinism in user-space task response.
[0008] To achieve the above objectives, the present invention adopts the following solution:
[0009] The method for ensuring hard real-time performance in Linux user space based on multi-core CPUs includes the following steps:
[0010] S1: Configure the Linux kernel to be fully preemptive;
[0011] S2: Create user applications. In Linux user space, multiple user threads are created through user applications. The number of user threads created is equal to the number of processor cores of a multi-core CPU. Each user thread is bound to a different CPU processor core by setting affinity.
[0012] S3: Select at least one CPU processor core as the real-time scheduling core. For each real-time scheduling core, set the scheduling policy of the user threads bound to it to SCHED_FIFO, and set the priority value of the user threads to be the same as the priority value of the lowest priority soft interrupt that can be generated on this real-time scheduling core.
[0013] S4: Lock the stack space of the user threads bound to each real-time scheduling core;
[0014] S5: The real-time scheduling core ID is specified by the Linux kernel boot parameter isolcpus to isolate the real-time scheduling core from other CPU processor cores, and the real-time scheduling core ID is specified by the Linux kernel boot parameter nohz_full to avoid the systick overhead of a single task.
[0015] S6: Run user applications to enable user threads on the real-time scheduling core to execute real-time tasks.
[0016] Preferably, in step S2, the method of setting affinity to bind the user thread to the CPU processor core includes: calling the function pthread_setaffinity_np and passing in the user thread's ID and CPU mask.
[0017] Preferably, in step S3, the method for locking the user thread's stack space includes: calling the function pthread_attr_getstack to obtain the starting address and size of the user thread's stack space, and calling the function mlock with the starting address and size of the stack space passed in.
[0018] Preferably, in step S2, the cancellation type of the user thread is also set to immediate cancellation, which includes calling the function pthread_setcanceltype and passing in the parameters PTHREAD_CANCEL_ASYNCHRONOUS and NULL.
[0019] Preferably, in step S6, the real-time tasks executed by the user thread include the execution of periodic timed tasks and the response to external events, and the specific execution methods include:
[0020] The functions `timerfd_create` are called to create a timer file descriptor, `timerfd_settime` is called to set the timer's period, `eventfd` is called to create an event file descriptor, and `select` or `epoll` is called to monitor the readability of the timer and event file descriptors.
[0021] When the event file descriptor is readable, the user thread executes the corresponding external event handling task and reads the contents of the event file descriptor; when the timer file descriptor is readable, the user thread executes the corresponding periodic task and clears the readable state of the timer file descriptor.
[0022] Preferably, in step S6, when the user thread executes a real-time task, multiple I / O multiplexing is performed on multiple hardware peripheral communication interfaces. Specific multiplexing methods include:
[0023] The `open` function is called to open the driver file of each communication hardware peripheral to obtain the driver file descriptor, and the `select` or `epoll` function is called to monitor the readable status of each driver file descriptor; when the driver file descriptor is readable, the user thread calls the `read` function to read the data received by the hardware from the driver file descriptor.
[0024] Preferably, in step S6, the methods for user threads to read and write physical memory include:
[0025] The mmap function is called to map physical memory space to a virtual space that can be directly accessed by user threads. User threads can read and write physical memory directly without switching to kernel mode by reading and writing virtual space. When real-time tasks need to frequently access contiguous memory space whose size exceeds a preset threshold, the page size is increased through Linux's big page settings.
[0026] Preferably, in step S3, the priority value of the lowest priority soft interrupt that can be generated on a real-time scheduling core is determined by the following method:
[0027] The system parses the contents of the interrupts and softirqs files in the Linux system's proc filesystem to identify all soft interrupt types registered or potentially triggered on this real-time scheduling kernel. It then queries the priority values corresponding to these soft interrupt types defined in the Linux kernel header files and selects the lowest priority value as the priority value for the lowest priority soft interrupt. When explicit soft interrupt priority information cannot be obtained by parsing the filesystem, the system calls the sched_get_priority_min function provided by Linux, passing the SCHED_FIFO parameter to obtain the minimum priority value of the SCHED_FIFO policy allowed by the system, and uses this value as the priority value for the lowest priority soft interrupt.
[0028] Preferably, before executing step S6, a real-time scheduling kernel isolation status verification is performed, including the following steps:
[0029] Verify the effectiveness of the isolcpus parameter by reading the cpuisol file in the proc file system and check if the real-time scheduling kernel is in the isolation list; verify that the affinity settings of all interrupts do not include the real-time scheduling kernel by reading the smp_affinity file corresponding to each interrupt number in the irq directory of the proc file system; and verify that there is no kernel thread scheduling activity on the real-time scheduling kernel by reading the schedstat file in the proc file system.
[0030] If any isolation failure is detected, the isolcpus parameters will be reconfigured and the interrupt routing settings will be adjusted until all verifications pass before proceeding to step S6 to run the user application. Isolation failure scenarios include:
[0031] The real-time scheduling core did not appear in the cpuisol file isolation list;
[0032] Any interrupted smp_affinity file shows that the interrupt can be routed to the real-time scheduling core;
[0033] The schedstat file shows that the real-time scheduling kernel has kernel thread scheduling records.
[0034] Preferably, in step S6, if multiple user threads on real-time scheduling cores need to access the same physical memory space, the following scheduling method is used:
[0035] Create an independent eventfd event file descriptor for each shared physical memory space and initialize the count to 1. Before a user thread accesses this shared physical memory space, call eventfd_read to attempt to decrement the count by 1. If the count is 0, the user thread is blocked at the call to eventfd_read. After accessing the shared physical memory space, call eventfd_write to increment the count by 1. Use the futex system call to execute the FUTEX_WAIT operation when blocked at eventfd_read and the FUTEX_WAKE operation after eventfd_write is completed. Insert mfence memory barrier instructions before and after the instruction sequence for reading and writing shared variables. Map the shared memory region to WC write combination mode. Use futex(FUTEX_LOCK_PI, ...) and futex(FUTEX_UNLOCK_PI, ...) operations to lock and unlock futex variables with a priority inheritance protocol, and set the attribute of futex variables to PTHREAD_PRIO_INHERIT during initialization.
[0036] The present invention includes at least the following beneficial effects: (1) By configuring the Linux kernel to fully preemptible mode, setting the SCHED_FIFO scheduling policy and high priority for real-time task threads, and binding the real-time task threads to isolated dedicated CPU cores for execution, it effectively ensures that user-mode urgent real-time tasks can preempt non-real-time tasks and obtain CPU resources in a timely manner. Their execution process is not easily interrupted by system kernel tasks or other non-real-time user tasks, which significantly improves the timeliness and determinism of user-mode real-time task response; (2) By creating user threads equal to the number of CPU cores and setting CPU affinity to strictly bind them to different cores, and using memory locks to lock the stack space of real-time threads, it effectively avoids the cache invalidation overhead caused by task threads migrating between multiple CPU cores and the page fault interrupt delay caused by memory page swapping, making the execution time of real-time tasks more stable and predictable; (3) By comprehensively using timerfd and eventfd mechanisms, and using select / epoll for multi-way I / O multiplexing monitoring, it makes the execution time of real-time tasks more stable and predictable. A single thread bound to the real-time kernel can efficiently handle periodically triggered tasks, external event triggered tasks, and communication tasks with multiple hardware peripherals simultaneously, taking into account the processing needs of multiple real-time event sources and improving the system integration and resource utilization; (4) By calling the mmap function to map the physical memory space to the user space for direct access, and optionally using the large page memory technology, the user-mode real-time task can access physical memory or hardware buffers as efficiently as accessing ordinary memory, completely avoiding the system call overhead of frequently entering the kernel mode when accessing these data, greatly reducing data access latency, and is particularly suitable for data-intensive real-time processing scenarios; (5) By providing a method for dynamically determining the priority of safe threads, an effective mechanism for automatically verifying whether CPU isolation is thorough before task execution, and a shared memory synchronous access scheme that supports priority inheritance, a complete and robust real-time guarantee system is formed, which significantly enhances the adaptability, reliability, and ability to handle complex resource sharing scenarios of the optimization method under different hardware configurations and environments. Attached Figure Description
[0037] Figure 1 This is a flowchart of the method steps of the present invention. Detailed Implementation
[0038] The present invention will now be described in further detail with reference to the accompanying drawings, so that those skilled in the art can implement it based on the description.
[0039] like Figure 1 As shown, the method for ensuring hard real-time operation in Linux user space based on a multi-core CPU, provided by this invention, includes the following steps:
[0040] S1: Configure the Linux kernel to be fully preemptive;
[0041] S2: Create user applications. In Linux user space, multiple user threads are created through user applications. The number of user threads created is equal to the number of processor cores of a multi-core CPU. Each user thread is bound to a different CPU processor core by setting affinity.
[0042] S3: Select at least one CPU processor core as the real-time scheduling core. For each real-time scheduling core, set the scheduling policy of the user threads bound to it to SCHED_FIFO, and set the priority value of the user threads to be the same as the priority value of the lowest priority soft interrupt that can be generated on this real-time scheduling core.
[0043] S4: Lock the stack space of the user threads bound to each real-time scheduling core;
[0044] S5: The real-time scheduling core ID is specified by the Linux kernel boot parameter isolcpus to isolate the real-time scheduling core from other CPU processor cores, and the real-time scheduling core ID is specified by the Linux kernel boot parameter nohz_full to avoid the systick overhead of a single task.
[0045] S6: Run user applications to enable user threads on the real-time scheduling core to execute real-time tasks.
[0046] Configuring the Linux kernel to fully preemptible mode improves its real-time responsiveness and is a necessary prerequisite for this method. Fully preemptible kernel mode means that in most code paths running in the kernel, higher-priority tasks can immediately preempt the currently executing kernel task, significantly reducing task response latency. This is achieved by modifying kernel configuration options (such as CONFIG_PREEMPT) to make some previously non-preemptible kernel critical sections available for interruption by higher-priority tasks. This configuration provides kernel-level support for prioritizing the scheduling of subsequent user-space real-time tasks. Linux kernel versions after 6.12 natively support configuring the kernel to fully preemptible mode, which can be achieved by selecting the FullyPreemptible Kernel (RT) option in the pre-compilation configuration interface (such as make menuconfig). Kernel versions prior to 6.12 do not directly support configuring to fully preemptible mode and require applying the real-time patch PREEMPT_RT. Both of these methods are standard implementations provided in the Linux version distribution information. By configuring a fully preemptible mode, a system environment that supports real-time scheduling of high-priority user tasks is built together with subsequent steps.
[0047] User applications create multiple user threads, the number of which matches the number of CPU cores, ensuring that only one user thread runs on each physical core, thus achieving static load distribution across cores. By setting CPU affinity for each thread, binding each thread to a different core avoids performance fluctuations and cache invalidation caused by thread migration or scheduler load balancing. In practice, the `pthread_setaffinity_np()` function can be called, passing in the thread ID and a bitmask (cpumask) representing the target CPU to bind the thread to the core. This approach ensures that each core is dedicated to processing one user thread, providing a stable execution environment for real-time scheduling.
[0048] Step S3 sets up the real-time scheduling cores and scheduling policy. A subset of CPU cores are selected as real-time scheduling cores, and the thread scheduling policy running on them is set to SCHED_FIFO. This is a real-time scheduling policy that means once a thread starts executing, it will continuously occupy the CPU until it voluntarily relinquishes its position or is preempted by a higher-priority task. The priority is set to the lowest priority soft interrupt that may occur on this core. This avoids user thread priorities being too high, which could cause soft interrupts to fail to respond in a timely manner, thus affecting the overall system functionality (such as network, timers, etc.). In practice, the soft interrupt priority can be determined by parsing / proc / interrupts or / proc / softirqs, or by using sched_get_priority_min(SCHED_FIFO) to obtain the system's minimum real-time priority value. This setting ensures that user threads have high real-time performance without completely blocking the processing of system soft interrupts.
[0049] To prevent page faults caused by the stack memory being swapped out to the swap area during real-time task execution, which could lead to fluctuations in response time, memory locking is used to fix the thread stack space in physical memory. Specifically, the stack address and size can be obtained through `pthread_attr_getstack`, and then `mlock()` can be called to lock it. This mechanism ensures that real-time tasks do not incur additional latency due to memory management operations during execution, and is particularly suitable for scenarios with extremely high response time requirements.
[0050] The Linux kernel boot parameters `isolcpus` and `nohz_full` are used to isolate and power-save real-time kernels. `isolcpus` isolates a specified CPU core from the global scheduler, preventing the kernel and other user threads from scheduling it. `nohz_full` stops timer interrupts on the core when only one task is running, reducing unnecessary interrupt overhead. In practice, to isolate cores 0 and 3 in a 4-core CPU, the boot parameters can be written as "isolcpus=0,3 nohz_full=0,3". This further purifies the execution environment of the real-time kernel, ensuring it only processes the bound real-time thread.
[0051] After the aforementioned environment configuration is completed, the user application begins running, and threads on the real-time core execute real-time tasks according to a preset scheduling policy. These tasks may include periodic scheduled tasks, responses to external events, or multiplexed I / O processing. In implementation, mechanisms such as timerfd and eventfd can be used to implement timing and event notifications, and multiplexing can be performed using select / epoll, responding immediately once an event occurs. During the execution phase, real-time tasks run stably and with low latency in the optimized environment.
[0052] This method significantly improves the responsiveness and time determinism of real-time tasks in Linux user space. Specifically, it enhances the preemptibility of the system kernel, enabling high-priority tasks to acquire CPU resources promptly; static binding of CPU cores and threads, along with real-time scheduling strategies, avoids uncertainty in task switching and scheduling; memory locking mechanisms prevent latency fluctuations caused by memory paging; and kernel parameter isolation and power-saving configurations further purify the real-time execution environment and reduce interrupt interference. Ultimately, this method enables user-space real-time tasks to achieve near-hardware-level responsiveness, making it suitable for fields with extremely high real-time requirements, such as industrial control, communication, and embedded systems. It has strong practicality and broad application prospects.
[0053] In another technical solution, step S2, the method of setting affinity to bind the user thread to the CPU processor core includes: calling the function pthread_setaffinity_np and passing in the user thread's ID and CPU mask.
[0054] When setting thread affinity with CPU cores, the `pthread_setaffinity_np` function provided in the POSIX thread library is used to bind a thread to a specific CPU processor core. This function is a non-portable but widely supported Linux system interface that directly modifies the thread's CPU affinity mask. This mask is a bitmap data structure, where each bit represents a logical CPU core. Setting the corresponding bits indicates which cores the thread is allowed to be scheduled on. In practice, first, the target thread's identifier (ID) needs to be obtained, typically a `pthread_t` type handle saved after thread creation. Then, a `cpu_set_t` type variable needs to be constructed as the CPU mask. The mask is initialized using the macro `CPU_ZERO`, and then the macro `CPU_SET` sets the numbers of one or more specified CPU cores into the mask. CPU core numbers are usually consecutive integers starting from 0; for example, on an 8-core processor, the numbers could be 0 to 7. Finally, the `pthread_setaffinity_np` function is called, passing in the target thread ID, the size of the constructed CPU mask, and the address of the mask itself.
[0055] This operation ensures that in the constructed multi-threaded real-time environment, each user thread is strictly confined to running on its allocated physical core. This works closely with the "one core, one thread" strategy in step S2 and the core isolation measures implemented through the isolcpus parameter in step S5, together eliminating the performance overhead and time uncertainty caused by cache invalidation, context switching, etc., resulting from thread migration between multiple CPU cores. This provides a stable and predictable underlying execution foundation for subsequent real-time task scheduling. It effectively guarantees the locality and stability of user thread execution, significantly reduces the random latency introduced by the operating system scheduler's automatic load balancing, and thus provides a more fundamental support for the implementation of user-space hard real-time tasks.
[0056] In another technical solution, step S3 involves locking the user thread's stack space by calling the function pthread_attr_getstack to obtain the starting address and size of the user thread's stack space, and then calling the function mlock with the starting address and size of the stack space passed in.
[0057] Locking the user thread stack space is achieved by combining the `pthread_attr_getstack` and `mlock` functions to lock the thread stack memory and prevent it from being swapped out to external storage. Under the virtual memory management mechanisms of modern operating systems, any memory page (including the page containing the thread stack) can be swapped out to the swap space unless explicitly locked. Once a page is swapped out, a major page fault is triggered when the thread accesses that memory again. The operating system needs to read the corresponding data page back from the disk, a process that introduces millisecond-level, unpredictable latency, which is fatal for hard real-time tasks. The `mlock` system call is designed to solve this problem; it allows a process to "lock" a specified memory region in physical memory, thus preventing it from being swapped out. In actual configuration, the target thread's attributes (`pthread_attr_t`) must first be obtained. This may be set through an attribute object when the thread is created, or obtained later through the `pthread_getattr_np` function. Then, the `pthread_attr_getstack` function is called to parse the starting address (`stackaddr`) and total size (`stacksize`) of the memory region where the thread's stack resides from the thread attributes. Finally, the `mlock` function is called, passing the obtained starting address and size as parameters to lock the memory region. The stack size can be set according to actual application requirements. For example, a common thread stack size can be several megabytes (such as 2MB or 8MB), but this is just a common reference value, and the actual size depends on the call depth of thread functions and the size of local variables.
[0058] This step ensures that even under high system memory pressure, the execution of real-time threads will never encounter page faults due to stack memory swapping, thus eliminating a significant source of timing uncertainty and making thread response times more deterministic and compact. It adds a crucial layer of deterministic memory access guarantee to hard real-time optimization schemes. It effectively avoids unpredictable page swapping latency caused by virtual memory management mechanisms, making the execution behavior of real-time task threads more stable and reliable, and further consolidating and improving the overall system's real-time performance.
[0059] In another technical solution, step S2 also sets the cancellation type of the user thread to immediate cancellation, which includes calling the function pthread_setcanceltype and passing in the parameters PTHREAD_CANCEL_ASYNCHRONOUS and NULL.
[0060] When setting the cancellation type of a user thread, the `pthread_setcanceltype` function is called with the cancellation type parameter set to `PTHREAD_CANCEL_ASYNCHRONOUS`, thus setting the thread's cancellation mode to "immediate cancellation". POSIX threads have two main cancellation types: "Deferred Cancellation" and "Asynchronous Cancellation". "Deferred Cancellation" is the default setting; the thread only responds to cancellation requests when it reaches a specific "cancellation point" (such as certain system calls), which may cause the thread to be blocked for a long time before the cancellation point, preventing timely termination. In contrast, in "Asynchronous Cancellation" mode, the cancellation request takes effect immediately at any point during thread execution, forcibly terminating the thread at its currently executing instruction, thus achieving extremely high response speed.
[0061] In actual configuration, `pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)` is typically called during thread initialization, before the execution of critical real-time task loops. The second parameter being NULL indicates that previous cancellation type settings are irrelevant. This setting works in conjunction with measures such as binding threads to dedicated CPU cores and setting high-priority real-time scheduling policies. As a safety and management mechanism, it ensures that even in the most extreme cases (e.g., a real-time task thread gets stuck in an infinite loop or unexpectedly blocks for a long time due to a programming error or external exception), other management threads or watchdog threads can still immediately and forcibly terminate the execution of the abnormal thread by calling the `pthread_cancel` function. This quickly reclaims computing resources, prevents it from continuously monopolizing valuable real-time CPU cores, and ensures the determinism and manageability of the entire real-time system. This is a powerful fault recovery mechanism for dealing with abnormal situations. By introducing the immediate cancellation mechanism, an important layer of safety protection and resource assurance is added. It greatly enhances the system's ability to respond quickly and handle abnormal tasks, ensuring that the real-time performance and resource availability of the entire system can be effectively maintained even when individual threads exhibit unexpected behavior, thus avoiding the risk of the overall determinism of the system being compromised due to the failure of a single thread.
[0062] In another technical solution, in step S6, the real-time tasks executed by the user thread include the execution of periodic timed tasks and the response to external events, and the specific execution methods include:
[0063] The functions `timerfd_create` are called to create a timer file descriptor, `timerfd_settime` is called to set the timer's period, `eventfd` is called to create an event file descriptor, and `select` or `epoll` is called to monitor the readability of the timer and event file descriptors.
[0064] When the event file descriptor is readable, the user thread executes the corresponding external event handling task and reads the contents of the event file descriptor; when the timer file descriptor is readable, the user thread executes the corresponding periodic task and clears the readable state of the timer file descriptor.
[0065] By comprehensively utilizing various mechanisms provided by the Linux kernel, such as `timerfd_create`, `timerfd_settime`, `eventfd`, and `select` or `epoll`, a high-efficiency, unified event handling loop can be constructed to simultaneously meet the hard real-time requirements of periodic task triggering and asynchronous event response. `timerfd_create` abstracts a timer as a file descriptor (fd), and `timerfd_settime` can set the timer period (e.g., triggering every few hundred microseconds or milliseconds). When the timer expires, the fd becomes readable. `eventfd` creates an fd for event notification; other threads or interrupt handlers notify of the event by writing data (such as an 8-byte integer) to it, which also makes the fd readable. `select` or `epoll` are I / O multiplexing mechanisms that can simultaneously monitor the state changes of multiple fds (including multiple timer fds, event fds, and potentially added device fds) and block until any monitored fd is ready (e.g., readable).
[0066] In actual operation, the real-time thread first calls `timerfd_create` and `eventfd` to create the necessary file descriptors, and then uses `timerfd_settime` to start a periodic timer. Subsequently, the thread enters a loop, calling `select` or `epoll` while monitoring the timer fd and event fd. When `select` / `epoll` returns, the thread checks which fd is ready. If the event fd is ready, it indicates an external asynchronous event needs to be processed; the thread then calls `read` to empty the count in `eventfd` (this could be a simple semaphore semantic), and then executes the corresponding external event processing task. If the timer fd is ready, it indicates a timeout period has elapsed; the thread then calls `read` to clear the fd's readable state (read timeout count), and then executes the corresponding periodic task. The timing period can be set according to the actual control law or sampling requirements; for example, in motion control, it can be from 100 microseconds to a few milliseconds—this is just a common reference range.
[0067] This method unifies two different types of task triggering mechanisms (time-driven and event-driven) into a highly efficient "wait-and-handle" model. It works closely with other techniques such as high-priority scheduling, CPU isolation, and memory locking, enabling real-time threads to respond to time and external events in a clean, low-latency environment with high determinism. This perfectly balances the execution of periodic control tasks and the handling of sudden external events. By concretizing the real-time task processing model, it provides an efficient, reliable, and flexible implementation framework, allowing a single real-time thread to reliably handle both periodic and asynchronous tasks simultaneously. This meets the response requirements for multiple real-time event sources in complex industrial control and communication scenarios, significantly improving the system's real-time processing capabilities and response determinism overall.
[0068] In another technical solution, in step S6, when the user thread executes a real-time task, multiple I / O multiplexing is performed on multiple hardware peripheral communication interfaces. The specific multiplexing methods include:
[0069] The `open` function is called to open the driver file of each communication hardware peripheral to obtain the driver file descriptor, and the `select` or `epoll` function is called to monitor the readable status of each driver file descriptor; when the driver file descriptor is readable, the user thread calls the `read` function to read the data received by the hardware from the driver file descriptor.
[0070] By utilizing I / O multiplexing techniques (such as select or epoll) in a real-time thread to simultaneously monitor multiple hardware peripheral communication interfaces, efficient and deterministic multiplexing data reception is achieved, avoiding the scheduling overhead and complexity of creating independent threads for each peripheral. In Linux systems, hardware peripherals are typically managed by kernel drivers, which provide file abstractions to user space. A file descriptor (fd) is obtained by calling the `open` function to open a device file representing specific hardware (such as UART, SPI, CAN controllers, network interfaces, etc.) (e.g., ` / dev / ttyS0`, ` / dev / spidev0.0`, ` / dev / can0`). When the hardware receives data, the driver makes the corresponding fd readable. `select` and `epoll` are system calls that allow a thread to monitor the state changes of multiple fds simultaneously. The thread can block on this call until any monitored fd becomes ready (i.e., data is readable), at which point it is awakened and processed. This is an event-driven programming model, which is highly efficient. During the initialization phase, the real-time thread calls a series of `open` functions to open the device files corresponding to all the hardware peripherals that need to be monitored and obtain a batch of file descriptors (fds). These fds are added to an `fd_set` (used for `select`) or an `epoll` instance (used for `epoll`). Then, in the thread's main loop, while waiting for timer or event fds, this set of hardware fds is passed as parameters to `select` or `epoll_wait` calls. When these calls return, the thread iterates through all the monitored fds, checking which fds are in a readable state due to received data. For each readable hardware fd, the thread immediately calls the `read` function to read all the data received by the hardware from that fd and performs subsequent parsing, processing, or forwarding. Using `epoll` is generally more efficient than `select`, especially when monitoring a large number of fds, because it avoids the overhead of copying the entire set of fds between user space and kernel space for each call.
[0071] This approach works seamlessly with techniques such as high-priority scheduling, CPU core isolation, and memory locking. It ensures that the real-time thread responsible for communication can wait for and process data from multiple hardware channels on its dedicated CPU core with the highest scheduling priority, without interruption or switching. All data reception and processing are completed within a single thread context, eliminating the overhead of synchronization and communication between multiple threads. This makes the latency from data arriving at the hardware to being read and processed by the user-mode thread extremely short and highly deterministic. A single high-priority real-time thread can efficiently and reliably process data input from multiple peripherals simultaneously, significantly improving the system's data throughput and real-time response, while avoiding the inherent complexity and uncertainty of multi-threaded designs.
[0072] In another technical solution, step S6 involves the following methods for user threads to read and write physical memory:
[0073] The mmap function is called to map physical memory space to a virtual space that can be directly accessed by user threads. User threads can read and write physical memory directly without switching to kernel mode by reading and writing virtual space. When real-time tasks need to frequently access contiguous memory space whose size exceeds a preset threshold, the page size is increased through Linux's big page settings.
[0074] Memory mapping technology allows user-mode real-time threads to directly read and write physical memory addresses as if they were accessing ordinary memory, completely avoiding the overhead of switching between user mode and kernel mode caused by system calls (such as read / write / ioctl) when accessing these memory regions. The mmap system call can map the contents of a file or a device (or even anonymous memory) into the virtual address space of the calling process. Once the mapping is successful, the application can directly read and write that segment of virtual memory through a pointer, while the operating system handles the behind-the-scenes translation of these accesses into operations on actual physical pages. The mapped "file" is usually a specific device file representing physical memory (e.g., / dev / mem), and the specific physical address can be located by specifying an offset. In this way, the real-time thread obtains a pointer to a specific physical memory region, and all subsequent read and write operations become simple memory access instructions, which are extremely efficient. The real-time thread first needs to open the / dev / mem device file in read-write mode. Then, the `mmap` function is called, passing in the file descriptor (fd), the size of the physical memory region to be mapped, the desired access protection flags (such as `PROT_READ` | `PROT_WRITE`), the mapping flags (usually `MAP_SHARED` to ensure modifications are synchronized to physical memory), and a crucial parameter: the offset. This offset directly corresponds to the starting address of the target physical memory. After successful mapping, `mmap` returns a pointer to the mapped region, which the thread can then use for direct memory access (DMA). For large contiguous memory blocks that require frequent access (whose size exceeds a preset threshold, for example, from tens of KB to several MB or even larger; this is only a reference choice based on the specific application and system page size), it is recommended to use Linux's Huge Pages feature. The standard memory page size is typically 4KB, and frequent access to large memory blocks can result in numerous Translation Lookaside Buffer (TLB) misses. By configuring and using Huge Pages (such as 2MB or 1GB pages), the number of TLB misses and page table traversals can be significantly reduced, thereby further reducing memory access latency.
[0075] The mapped memory may be a shared memory area that interacts with external hardware via Direct Memory Access (DMA), or a buffer used to store frequently accessed data. Real-time threads, operating on isolated CPU cores with the highest scheduling priority, directly manipulate physical memory via pointers. This results in the shortest access path, the lowest latency, and no fluctuations from system call context switching. This provides crucial underlying support for real-time applications requiring extreme performance and data exchange speeds, such as high-frequency data acquisition, real-time signal processing, and communication with dedicated hardware accelerators. By introducing direct user-mode mapping of physical memory and large page memory optimization, the most efficient way to access low-level data is provided for hard real-time solutions. It completely eliminates kernel-mode switching and system call overhead, enabling real-time threads to achieve near-bare-metal performance and extremely high time determinism in operations on critical memory regions, significantly improving the execution efficiency of data-intensive real-time tasks and the overall system responsiveness.
[0076] In another technical solution, in step S3, the priority value of the lowest priority soft interrupt that can be generated on a real-time scheduling core is determined by the following method:
[0077] The system parses the contents of the interrupts and softirqs files in the Linux system's proc filesystem to identify all soft interrupt types registered or potentially triggered on this real-time scheduling kernel. It then queries the priority values corresponding to these soft interrupt types defined in the Linux kernel header files and selects the lowest priority value as the priority value for the lowest priority soft interrupt. When explicit soft interrupt priority information cannot be obtained by parsing the filesystem, the system calls the sched_get_priority_min function provided by Linux, passing the SCHED_FIFO parameter to obtain the minimum priority value of the SCHED_FIFO policy allowed by the system, and uses this value as the priority value for the lowest priority soft interrupt.
[0078] This method dynamically determines the priority value of the lowest priority soft interrupt that can be generated on a real-time scheduling core, primarily relying on parsing system state information exposed by the Linux kernel through the proc virtual filesystem. The proc filesystem is a mechanism by which the kernel provides an interface to user space with kernel data structures. The ` / proc / interrupts` file records the number of occurrences of various hardware interrupts on each CPU core, while the ` / proc / softirqs` file records the number of times each type of soft interrupt is triggered on each CPU core. By analyzing the contents of these files, it is possible to infer which soft interrupt types are likely to be triggered on the target real-time scheduling core. User-space applications open and parse ` / proc / interrupts` and ` / proc / softirqs` as if they were ordinary files. By analyzing these two files, all soft interrupt types recorded (i.e., those that have occurred or been registered) on the real-time scheduling core can be identified, such as network receive soft interrupts (NET_RX), timer soft interrupts (TIMER), and block device soft interrupts (BLOCK). Each soft interrupt type typically has a fixed priority value defined in the kernel header file (such as `linux / interrupt.h`). After obtaining the priority values of all possible soft interrupt types, a simple comparison algorithm is used to select the smallest value, which is then determined as the priority value of the lowest-priority soft interrupt. As a supplement and safeguard, a backup method is used when soft interrupt priority information cannot be reliably obtained by parsing the proc filesystem. Linux's system call interface provides a standard method for obtaining the priority range of the scheduling policy, ensuring the method's portability and reliability. In practice, if, due to kernel version differences, changes in the proc file format, or security settings (such as some embedded systems potentially simplifying or hiding proc information), the correspondence between soft interrupts and priorities cannot be explicitly parsed from files such as / proc / softirqs, the application will automatically enable this backup scheme. In this case, the program calls the sched_get_priority_min function, passing in the scheduling policy parameter SCHED_FIFO. This system call returns the minimum priority value allowed by the operating system under the SCHED_FIFO policy. This minimum priority value returned by the system is then used as the "priority value of the lowest-priority soft interrupt." This approach provides a safe and standardized fallback mechanism, ensuring the robustness and availability of the entire real-time optimization method across different Linux systems and configuration environments.
[0079] The primary and backup schemes together constitute a complete and reliable priority determination strategy. The primary scheme strives for accuracy, aiming to obtain the true kernel soft interrupt priority information, thereby setting the optimal and safest priority value for user threads. The backup scheme ensures that the entire system build process will not be interrupted if the primary scheme fails, and can still continue to run based on a conservative but effective safety value (the minimum real-time priority allowed by the system). During runtime, the primary scheme is usually attempted first, and the backup scheme is seamlessly switched only if the primary scheme's parsing process fails or the result is obviously unreasonable. This provides a combined primary and backup, progressive priority determination method, which significantly improves the adaptability and reliability of the entire hard real-time optimization scheme under different Linux kernel versions and system configurations, ensuring the rationality and safety of real-time thread priority settings, and thus providing crucial underlying parameter guarantees for the stable real-time performance of the overall system.
[0080] In another technical solution, before executing step S6, a real-time scheduling kernel isolation status verification is performed, including the following steps:
[0081] Verify the effectiveness of the isolcpus parameter by reading the cpuisol file in the proc file system and check if the real-time scheduling kernel is in the isolation list; verify that the affinity settings of all interrupts do not include the real-time scheduling kernel by reading the smp_affinity file corresponding to each interrupt number in the irq directory of the proc file system; and verify that there is no kernel thread scheduling activity on the real-time scheduling kernel by reading the schedstat file in the proc file system.
[0082] If any isolation failure is detected, the isolcpus parameters will be reconfigured and the interrupt routing settings will be adjusted until all verifications pass before proceeding to step S6 to run the user application. Isolation failure scenarios include:
[0083] The real-time scheduling core did not appear in the cpuisol file isolation list;
[0084] Any interrupted smp_affinity file shows that the interrupt can be routed to the real-time scheduling core;
[0085] The schedstat file shows that the real-time scheduling kernel has kernel thread scheduling records.
[0086] Before running real-time tasks, the isolation status of the real-time scheduling kernel is systematically verified to ensure that the isolation configuration previously implemented through kernel parameters (such as isolcpus and nohz_full) is fully effective, thereby creating a clean and interference-free execution environment for hard real-time tasks. This verification process is accomplished by querying and parsing multiple proc filesystem interfaces provided by the Linux kernel. Specifically, it includes three levels of checks: First, by reading the / proc / cmdline or the proprietary / sys / devices / system / cpu / isolated file (in some kernel versions), it confirms that the isolcpus kernel boot parameter is correctly effective and verifies that the ID of the target real-time scheduling kernel is indeed in the system-published list of isolated CPU cores. This is the most basic kernel scheduling isolation verification. Second, by traversing the smp_affinity or smp_affinity_list files in the subdirectories corresponding to each interrupt number under the / proc / irq directory, it checks whether the affinity settings of all these interrupts (i.e., which CPU cores are allowed to handle the interrupt) do not include isolated real-time scheduling kernels, ensuring that no hardware interrupts can be routed to the target core, thereby avoiding unpredictable delays caused by hardware interrupts. Finally, by reading the / proc / schedstat file and parsing the statistics corresponding to the target real-time scheduling core, it was confirmed that no kernel thread scheduling activity was recorded on this core (such as zero or extremely low and stable yield and schedule call counts). The lack of records indicates that, apart from the bound user threads, the kernel scheduler has hardly scheduled any other kernel tasks on this core.
[0087] If any of the above verifications fails, indicating an abnormal situation where the configuration has not taken effect, corrective measures should be taken instead of blindly assuming the environment is ready. During actual operation, the verification program (which may be an initialization phase of a user application) will perform three checks sequentially. If any check fails, it is considered an "isolation failure." For example, if the real-time scheduling kernel is not in the isolation list, it may mean that the kernel boot parameters are not correctly recognized; if the affinity file of an interrupt shows that it can be routed to a real-time kernel, then the interrupt must be re-bound to another non-isolated kernel; if schedstat shows kernel thread scheduling records, further checks and termination of irrelevant kernel threads or adjustments to deeper kernel parameters may be necessary. Once a failure is detected, the system will not directly continue to step S6 (running the application), but will attempt to automatically or prompt the administrator to reconfigure the isolcpus parameters by writing to / proc / irq / <irq_num> The ` / smp_affinity` file is used to adjust interrupted routing settings or take other remedial measures, and then verification is performed again. This process is repeated until all verification items pass, ensuring that the live environment is absolutely as expected, before the actual live task is started.
[0088] Automated verification has clear judgment criteria, with three scenarios: First, the target real-time scheduling core's ID cannot be found in the system interface, meaning basic isolation is ineffective—this is the most fundamental failure. Second, any hardware interrupt affinity settings fail to exclude the real-time scheduling core, indicating that the interrupt remains a potential source of interference. Third, kernel scheduling statistics show that kernel threads have been scheduled on the real-time core, proving that isolation is incomplete and kernel-level tasks are still competing for CPU resources. These three scenarios define a multi-layered isolation effectiveness standard, from CPU core scheduling isolation to hardware interrupt isolation and then to kernel task scheduling, forming a rigorous verification system. By introducing a multi-layered isolation status verification process with feedback and correction mechanisms before execution, a crucial quality assurance and robustness control layer is added to the hard real-time optimization solution. It can proactively detect and assist in correcting isolation failures caused by system configuration errors or environmental differences, greatly reducing the risk of performance fluctuations or even failures of real-time tasks due to impure operating environments, and significantly improving the deployment success rate and operational reliability of the entire solution across different platforms and configurations.
[0089] In another technical solution, in step S6, if multiple user threads on real-time scheduling cores need to access the same physical memory space, the following scheduling method is used:
[0090] Create an independent eventfd event file descriptor for each shared physical memory space and initialize the count to 1. Before a user thread accesses this shared physical memory space, call eventfd_read to attempt to decrement the count by 1. If the count is 0, the user thread is blocked at the call to eventfd_read. After accessing the shared physical memory space, call eventfd_write to increment the count by 1. Use the futex system call to execute the FUTEX_WAIT operation when blocked at eventfd_read and the FUTEX_WAKE operation after eventfd_write is completed. Insert mfence memory barrier instructions before and after the instruction sequence for reading and writing shared variables. Map the shared memory region to WC write combination mode. Use futex(FUTEX_LOCK_PI, ...) and futex(FUTEX_UNLOCK_PI, ...) operations to lock and unlock futex variables with a priority inheritance protocol, and set the attribute of futex variables to PTHREAD_PRIO_INHERIT during initialization.
[0091] To address the challenge of securely and efficiently concurrently accessing the same shared physical memory space across multiple real-time scheduling cores, a lightweight synchronization and mutual exclusion scheme based on event file descriptors (eventfds) is provided. This scheme utilizes eventfds as counters with atomic operation semantics, maintained in the kernel and accessible via file descriptors. Specifically, an independent eventfd is created for each shared physical memory space requiring concurrent access, with its initial count set to 1 (indicating initial resource availability, equivalent to a binary semaphore). Before a user thread needs to access this shared memory, it first calls eventfd_read to attempt to read from the eventfd (i.e., attempts to atomically decrement the count by 1). If the read is successful (indicating the original count was greater than 0, resource acquisition was successful), the thread immediately enters the critical section to perform the read / write operation. If the read fails (i.e., blocked due to a count of 0), the thread enters a sleep state at the eventfd_read system call. After other threads complete their access to shared memory, they call `eventfd_write` to write a value (usually 1) to the same `eventfd`. This atomically increments the counter by 1, releasing the resource and waking up a waiting thread. This mechanism establishes a basic serialized access framework, ensuring that only one thread can enter the critical section at a time, effectively preventing data races.
[0092] By combining the efficient collaboration of futex and memory barriers, synchronization overhead is further reduced, response speed is improved, and the correctness of memory operations is guaranteed. The FUTEX (Fast Userspace muTEX) system call and memory barrier instructions are introduced. FUTEX allows conditions to be checked atomically in user space, trapping into the kernel only when sleep is truly necessary, which greatly reduces unnecessary kernel trap overhead. In specific implementation, when a thread is about to block at eventfd_read due to resource unavailability, it does not rely solely on the default blocking mechanism provided by the kernel. Instead, it executes a FUTEX_WAIT operation on an associated futex variable, allowing the thread to sleep efficiently in a queue managed collaboratively by user space and the kernel. Correspondingly, when a thread releases resources via eventfd_write, after the write operation, a FUTEX_WAKE operation is executed to efficiently wake up one or more threads waiting on the corresponding futex variable. This combination is generally more efficient than a blocking / wake mechanism that solely relies on eventfd. Furthermore, to ensure the visibility of shared memory data across multiple cores and to prevent out-of-order execution of CPUs from causing memory access logic errors, an `mfence` memory barrier instruction needs to be inserted before and after the critical instruction sequence for reading and writing shared variables. This instruction forces a flush to the CPU's write buffer and cache, ensuring that all memory write operations before this barrier are visible to other CPU cores in the system, and that read operations after this barrier are not reordered to be executed before the barrier, thus guaranteeing the atomicity and consistency of memory access within the critical section.
[0093] To address high-performance scenarios and resolve the classic priority inversion problem in real-time systems, deeper optimization strategies are introduced. First, to optimize write performance to shared memory, especially for large or consecutive write operations, it is recommended to modify page table attributes or map shared memory regions to Write-Combining (WC) mode. WC mode allows the CPU to merge multiple write operations and temporarily store them in a buffer before writing them to memory in batches more efficiently. This reduces memory bus overhead, significantly improving write throughput, which is particularly beneficial for scenarios such as video frame buffers and high-speed data acquisition buffers. Second, and most importantly, a priority inheritance protocol is integrated to resolve priority inversion. In the actual design, the `FUTEX_LOCK_PI` and `FUTEX_UNLOCK_PI` commands of the `futex` system call are used to perform priority-inherited locking and unlocking operations on the `futex` variable representing the lock. During thread initialization, the attribute of this `futex` variable needs to be set to `PTHREAD_PRIO_INHERIT`. When a high-priority thread attempts to acquire a lock already held by a low-priority thread, the low-priority thread temporarily inherits the high-priority thread's priority through the priority inheritance protocol. This allows the low-priority thread to execute and release the lock more quickly, after which its priority returns to its original state. This effectively prevents the priority inversion problem—where a mid-priority task preempts a low-priority task holding a lock, thus blocking the high-priority task indefinitely—and ensures the predictability of real-time task scheduling.
[0094] By providing a multi-layered, high-performance, and real-time-guaranteed integrated solution for shared memory synchronization access, the system significantly enhances the capabilities of multi-core real-time systems in handling complex data sharing scenarios. It not only guarantees the correctness of data access at the fundamental level but also significantly improves the efficiency and response speed of the synchronization mechanism through a series of advanced optimization strategies. In particular, by integrating a priority inheritance protocol, it effectively eliminates the risk of priority inversion caused by resource contention, thereby ensuring that high-priority real-time tasks maintain extremely high scheduling determinism and timely response even in the most complex shared memory access scenarios.
[0095] It should be noted that although the steps are described in a specific order above, this does not mean that they must be performed in that order. In fact, some of these steps can be executed concurrently, or even in a different order, as long as the required functionality is achieved. The number of devices and processing scale described herein are for simplification of the invention; applications, modifications, and variations of this invention will be readily apparent to those skilled in the art.
[0096] Although embodiments of the present invention have been disclosed above, they are not limited to the applications listed in the specification and embodiments. They can be applied to various fields suitable for the present invention. For those skilled in the art, other modifications can be easily made. Therefore, without departing from the general concept defined by the claims and their equivalents, the present invention is not limited to the specific details and illustrations shown and described herein.
Claims
1. A method for ensuring hard real-time performance in Linux user space based on a multi-core CPU, characterized in that, Includes the following steps: S1: Configure the Linux kernel to be fully preemptive; S2: Create user applications. In Linux user space, multiple user threads are created through user applications. The number of user threads created is equal to the number of processor cores of a multi-core CPU. Each user thread is bound to a different CPU processor core by setting affinity. S3: Select at least one CPU processor core as the real-time scheduling core. For each real-time scheduling core, set the scheduling policy of the user threads bound to it to SCHED_FIFO, and set the priority value of the user threads to be the same as the priority value of the lowest priority soft interrupt that can be generated on this real-time scheduling core. S4: Lock the stack space of the user threads bound to each real-time scheduling core; S5: The real-time scheduling core ID is specified by the Linux kernel boot parameter isolcpus to isolate the real-time scheduling core from other CPU processor cores, and the real-time scheduling core ID is specified by the Linux kernel boot parameter nohz_full to avoid the systick overhead of a single task. S6: Run user applications to enable user threads on the real-time scheduling core to execute real-time tasks; In step S3, the priority value of the lowest priority soft interrupt that can be generated on a real-time scheduling core is determined by the following method: The system parses the contents of the interrupts and softirqs files in the Linux system's proc filesystem to identify all soft interrupt types registered or triggered on this real-time scheduling kernel. It then queries the priority values corresponding to these soft interrupt types defined in the Linux kernel header files and selects the lowest priority value as the priority value for the lowest priority soft interrupt. When explicit soft interrupt priority information cannot be obtained by parsing the filesystem, the system calls the sched_get_priority_min function provided by the Linux system, passing the SCHED_FIFO parameter to obtain the minimum priority value of the SCHED_FIFO policy that the system allows to be set, and uses this value as the priority value for the lowest priority soft interrupt. Before executing step S6, a real-time scheduling kernel isolation status verification is performed, including the following steps: Verify the effectiveness of the isolcpus parameter by reading the cpuisol file in the proc file system and check if the real-time scheduling kernel is in the isolation list; verify that the affinity settings of all interrupts do not include the real-time scheduling kernel by reading the smp_affinity file corresponding to each interrupt number in the irq directory of the proc file system; and verify that there is no kernel thread scheduling activity on the real-time scheduling kernel by reading the schedstat file in the proc file system. If any isolation failure is detected, the isolcpus parameters will be reconfigured and the interrupt routing settings will be adjusted until all verifications pass before proceeding to step S6 to run the user application. Isolation failure scenarios include: The real-time scheduling core did not appear in the cpuisol file isolation list; Any interrupted smp_affinity file shows that the interrupt can be routed to the real-time scheduling core; The schedstat file shows that the real-time scheduling kernel has kernel thread scheduling records.
2. The method for ensuring hard real-time operation in Linux user space based on a multi-core CPU according to claim 1, characterized in that, In step S2, the method of setting affinity to bind the user thread to the CPU processor core includes: calling the function pthread_setaffinity_np and passing in the user thread's ID and CPU mask.
3. The method for ensuring hard real-time performance in Linux user space based on a multi-core CPU according to claim 1, characterized in that, In step S3, the method for locking the user thread's stack space includes: calling the function pthread_attr_getstack to obtain the starting address and size of the user thread's stack space, and calling the function mlock with the starting address and size of the stack space passed in.
4. The method for ensuring hard real-time operation in Linux user space based on a multi-core CPU according to claim 1, characterized in that, In step S2, the cancellation type of the user thread is also set to immediate cancellation, which includes calling the function pthread_setcanceltype and passing in the parameters PTHREAD_CANCEL_ASYNCHRONOUS and NULL.
5. The method for ensuring hard real-time operation in Linux user space based on a multi-core CPU according to claim 1, characterized in that, In step S6, the real-time tasks executed by the user thread include the execution of periodic timed tasks and the response to external events, and the specific execution methods include: The functions `timerfd_create` are called to create a timer file descriptor, `timerfd_settime` is called to set the timer's period, `eventfd` is called to create an event file descriptor, and `select` or `epoll` is called to monitor the readability of the timer and event file descriptors. When the event file descriptor is readable, the user thread executes the corresponding external event handling task and reads the contents of the event file descriptor; when the timer file descriptor is readable, the user thread executes the corresponding periodic task and clears the readable state of the timer file descriptor.
6. The method for ensuring hard real-time operation in Linux user space based on a multi-core CPU according to claim 1, characterized in that, In step S6, when the user thread executes a real-time task, it performs multiplexed I / O on multiple hardware peripheral communication interfaces. The specific multiplexing methods include: The `open` function is called to open the driver file of each communication hardware peripheral to obtain the driver file descriptor, and the `select` or `epoll` function is called to monitor the readable status of each driver file descriptor; when the driver file descriptor is readable, the user thread calls the `read` function to read the data received by the hardware from the driver file descriptor.
7. The method for ensuring hard real-time operation in Linux user space based on a multi-core CPU according to claim 1, characterized in that, In step S6, the methods by which user threads read and write physical memory include: The mmap function is called to map physical memory space to a virtual space that can be directly accessed by user threads. User threads can read and write physical memory directly without switching to kernel mode by reading and writing virtual space. When real-time tasks need to frequently access contiguous memory space whose size exceeds a preset threshold, the page size is increased through Linux's big page settings.
8. The method for ensuring hard real-time operation in Linux user space based on a multi-core CPU according to claim 1, characterized in that, In step S6, if multiple user threads on real-time scheduling cores need to access the same physical memory space, the following scheduling method is used: Create an independent eventfd event file descriptor for each shared physical memory space and initialize the count value to 1; before a user thread accesses this shared physical memory space, call eventfd_read to attempt to decrement the count value by 1. If the count value is 0, the user thread will block at the call to eventfd_read; after the access to the shared physical memory space is completed, call eventfd_write to increment the count value by 1; use the futex system call to execute the FUTEX_WAIT operation when blocked at eventfd_read, and the FUTEX_WAKE operation after eventfd_write is completed; insert mfence memory barrier instructions before and after the instruction sequence for reading and writing shared variables; map the shared memory region to WC write combination mode; use futex(FUTEX_LOCK_PI, ...) and futex(FUTEX_UNLOCK_PI, ...) operations to lock and unlock futex variables with priority inheritance protocol, and set the attribute of futex variables to PTHREAD_PRIO_INHERIT during initialization.
Citation Information
Patent Citations
Domestic processor virtualization adaptation tuning method
CN117215718A
Linux real-time enhancement method based on domestic real-time kernel
CN119829245A