Linux device driver time flow summary

xiaoxiao2021-03-06  47

Chapter 6 Time Flow

At this point, we basically know how to write a complete character module. In the real equipment driver, in addition to achieving the necessary operations, you have to do more, such as timing, memory management, hardware access, and more. Fortunately, many mechanisms provided in the kernel can simplify the work of driver developers, and we will discuss some kernel resources that drivers can access in the following chapters. This chapter, let's first see how the kernel code is processed for time problems. Increment in complex degree, this problem includes:

Understanding how the kernel time mechanism gets the current time how to schedule how to schedule an operational delay to execute an asynchronous function to the specified time. 6.1 Interval in the kernel. We must first involve the clock interrupt, the operating system is interrupted by clock interrupt to determine the interval . Interrupt is asynchronous event, usually triggered by external hardware. When the interrupt occurs, the CPU stops the ongoing task, and turn on another special code (ie interrupt service routine, also known as ISR) to respond to this interrupt. The implementation of interrupts and ISR will be discussed in Chapter 9. The clock interrupt is generated by the system timer at a periodic interval, which is set by the kernel according to the value of Hz, and Hz is a constant associated with the architecture, defined in the file . The current Linux version is a value of Hz defined by most platforms. Some platforms are 1024, and 2024 is 20. Driver developers should not count any particular HZ value, regardless of which value is used by your platform. When the clock is interrupted, the value of the variable Jiffies increases. Jiffies is initialized to 0 during system startup, and the Jiffies value is the number of clock ticks since the operating system. Jiffies is defined in the header file as a data type as unsigned long volatile type variable, this The variables may overflow after long-term continuous operation (but now there is no platform to overflow Jiffies in less than 16 months). In order to ensure that Jiffies can still work properly when Jiffies overflows, people have done a lot of efforts. Driver developers usually do not need to consider the Jiffies overflow problem, knowing this possibility. If you want to change the frequency of the system clock interrupt, you can modify the Hz value. Some people use Linux to handle hard real-time tasks, and they add HZ values ​​to get a faster response time, to endure additional clock interrupts generated system overhead. All in all, the best way to break the clock is to retain Hz default, because we can fully believe in the developers of the kernel, they must have selected the best value for us. 6.1.1 The processor's unique register requires a very short time, or requires extremely high time accuracy, you can use resources related to a particular platform, which is the importance of time accuracy over the transcatence of the code. Practice. Most newer CPUs include a high-precision counter that increments once each clock cycle. This counter can be used to accurately measure time. Since the instruction execution time in most systems has unpredictability (due to instruction scheduling, branch prediction, cache, etc.), using this clock counter is a uniquely reliable timing method when running with a small time granularity. In order to adapt to the high speed of the modern processor, meet the urgent needs of the performance indicators, and due to the unpredictability of the instruction time caused by multi-layer cach in the CPU design, the CPU manufacturer introduces the measurement time of the recording clock cycle. Simple and reliable method. So most modern processors contain a count register that is incremented by clock cycle. Based on a different platform, this register may be readable, or may not be readable; it may be writable or may not be written; may be 64-bit may also be 32-bit. If it is 32-bit, pay attention to the problem of processing overflow. Regardless of whether the register can set 0, we strongly recommend not reset it, even if the hardware is allowed to do. Because you can always read this register and compare the differences of reading values, we do not require exclusion of this register and modify its current value.

The most famous counter register is TSC (TimeStamp Counter, Time Stamp Counter), which provides the register from the Pentium processor of the x86 and includes all CPUs later. It is a 64-bit register that records the number of CPU clock cycles, kernel space and user space can read it. After the header file (means "Machine-Specific Registers, machine-specific registers"), you can use the following macros: RDTSC (Low, HiGH); RDTSCL (Low); the former macro Atomically read the 64-bit value into two 32-bit variables; the latter one reads only the low half of the register into a 32-bit variable, in most cases, this is enough. For example, a 500MHz system allows an overflow of a 32-bit counter to overflow to 8.5 seconds. If the time to process is definitely better than this, then there is no need to read the entire register. The following code can measure the runtime of the instruction yourself:

Unsigned long iNi, end; RDTSCL (INI); RDTSCL (END); Printk ("Time Lapse:% li / n", end - ini); other platforms also provide similar functions, and in the internal nuclear head files A function that is unrelated to the architecture can replace RDTSC, which is get_cycles, which is introduced during the development of version 2.1. The prototype is:

#include cycles_t get_cycles (void); this function can be used on various platforms, which always returns 0 on the platform without clock cycle register registers. The Cycles_t type is a suitable unsigned type that can be loaded into the corresponding CPU single register. Selecting the type that can be loaded into a single register means that, for example, GET_CYCLES returns only 32 bits when the clock cycle counter is used for Pentium. This choice is wise, it avoids the problem of multi-register operation, which does not hinder the normal use of the counter, that is, the time interval for metric is short. In addition to this function-independent function, we will use an example using an embedded assembly code. To do this, let's implement an RDTSCL function to the MIPS processor, the function is like x86. This example is based on MIPS because most MIPS processors have a 32-bit counter, named Register 9 registers in their internal "CoProcessor 0". In order to read the register from the kernel space, it can define the following macros, which performs the assembly instructions of "read from Coprocessor 0": *

#define rdtscl (dest) / _ _ASM_ _ _ _Volatile__ ("MFC0% 0, $ 9; NOP": "= r" (DEST))

Note: The NOP instruction is required to prevent the compiler from accessing the target register immediately after the instruction MFC0. This interlock is typical in the RISC processor, and during the delay period, the compiler can still schedule other instructions. We use NOP here because embedded instructions are a black box for compilers and cannot be optimized. By using this macro, the MIPS processor can perform the same code as the X86 shown in the previous side. The interest of the GCC embedded assembly is that the allocation of the general register is done by the compiler. The% 0 used in this macro is only "parameter 0" placeholder, parameter 0 is defined by any register (R) "as an output (=). This macro also illustrates the output register to correspond to the C expression DEST. The syntax function of embedding compilation is strong but also complicated, especially in the platform that is restricted to each register, such as the x86 series. A complete grammatical description is provided in the GCC documentation, usually found in Info. The short C code segment showing this show has been running on a K7 class X86 processor and a MIPS VR4181 processor (using just macro). The time given by the former is consumed to be 11 clock cycles, and the latter is only 2 clock cycles. This is understood because the RISC processor typically runs a command per hour cycle. 6.2 Getting the current time core usually gets the current time through the Jiffies value. This value is indicated by the most recent system to start to the current interval, and it is not related to the device driver because its life is limited to the system's runtime. However, the driver can utilize the current value of Jiffies to calculate the time interval between different events (such as using it in the input device driver to resolve the midst of the midst). Briefly, the measurement time interval is sufficient, and if you need to measure a shorter time, you can use a register-specific register. The driver generally does not need to know the wall clock time (referring to the time of daily life), usually only the wall clock time is required like CRON and AT. The case where the wall clock time is to use the special case of the device driver, at which time the wall clock time can be converted into a system clock via the user program. Direct treatment wall clock often means that some strategy is being realized, should be carefully examined. If the driver really needs to get the current time, you can use the Do_getTimeOfDay function. This function does not return today's week a week or similar information; it is a pointer variable pointing to Struct TimeVal in seconds or microsecond values, which is also the same variable in the GetTimeOfDay system call. The prototype of Do_gettimeOfDay is as follows: #include void do_gettimeofday (Struct Timeval * TV); DO_GETTIMEOFDAY is described in many architectures in many architectures, but actual accuracy is different The platform changes, and it will be lower in the old version of the kernel. Current time can also be obtained (but accuracy difference) by XTIME variable (type Struct TimeVal), but does not encourage the variable directly, because unless the interrupt is closed, otherwise, two members that cannot be atomically accessing the TIMEVAL variables TV_SEC and TV_USEC. In the 2.2 version of the kernel, a quick and secure gain time (possibly accurate will be different) use GET_FAST_TIME:

Void get_fast_time (struct timeval * TV); get the code for the current time can be found in the Just ("Just In Time" module, the source file can be obtained from O'Reilly's FTP site. The JIT module will create the / proc / currentime file, read the file to return three items in the form of the ASCII code: The current time returned by Do_getTimeOfDay from the xi_gettimeofday Current Time Jiffies of the current time Jiffies, we choose the dynamic / proc file, Because this module code amount will be small - it is not worthwhile to write a complete device driver for returning three lines of text. If you read this file multiple times in a clock tick with a CAT command, you will find that Xtime and Do_getTimeOfDay are different, and the number of XTime updates is not so frequent:

morgana% cd / proc; cat currentime currentime currentime gettime: 846157215.937221 xtime: 846157215.931188 jiffies: 1308094 gettime: 846157215.939950 xtime: 846157215.931188 jiffies: 1308094 gettime: 846157215.942465 xtime: 846157215.941188 jiffies: 1308095

转载请注明原文地址:https://www.9cbs.com/read-112480.html

New Post(0)