transferred from: http://blog.csdn.net/joker0910/article/details/7782765
[CPP]View PlainCopy
- #define PREEMPT_DISABLE () \
- do{\
- Inc_preempt_count (); \
- Barrier (); \
- } while(0)
What is this barrier doing?
Memory barrier occurs because the compiler or the current processor is often smart about the sequence of instructions to do some processing, such as data caching, read and write instructions in order to execute and so on. If the optimized object is normal memory, it generally improves performance without generating a logic error. However, similar optimizations to I/O operations are likely to cause fatal errors. So use a memory barrier to force the instructions before and after the statement to complete in the correct order. In fact, the effect of putting a WMB in the instruction sequence is that when the instruction is executed there, all the cached data is written to the place where it was written, and the written instruction in front of the WMB must be executed before the WMB write instruction. RMB (read memory barrier) ensures that the read operation before the barrier will be completed before the subsequent read operation is performed. WMB guarantees that the write operation does not go out of order, and the MB instruction guarantees neither. These functions are a superset of the barrier function.
These functions insert a hardware memory barrier into the compiled instruction stream, and the specific insertion method is platform-dependent.
About the barrier () macro is actually an optimization barrier:
#define BARRIER () __asm__ __volatile__ (""::: "Memory")
After the CPU crosses the memory barrier, it refreshes its buffering state to the memory. This statement does not actually generate any code, but it allows GCC to flush the allocation of the register to the variable after barrier ().
1) set_mb (), MB (), barrier () function trace to the end, that is, __asm__ __volatile__ (""::: "Memory"), and this line of code is the Ram barrier.
2) __asm__ is used to instruct the compiler to insert assembly statements here
3) __volatile__ is used to tell the compiler that it is forbidden to re-combine the assembly statement here with its statements. That is, the assembly of this place is handled in the original way.
4) Memory forcing the GCC compiler assumes that all memory units in RAM are modified by the assembly instructions, so that the data in the registers and cached memory units in the cache are invalidated. The CPU will have to re-read the data in memory when needed. This prevents the CPU from using the data in the Registers,cache to optimize the instruction and avoids accessing the memory.
5) ""::: Indicates that this is an empty instruction. Barrier () do not insert a serialization assembly directive here.
6) __asm__,__volatile__,memory already explained in front.
Whether the GCC compiler is optimized or the processor itself uses a lot of optimizations such as write buffer, lock-up free, non-blocking reading, Register allocation, Dynamic scheduling, M Ultiple issues, and so on, can make actual execution possible to violate the sequence of programs, so introduce memory barriers to ensure that the order in which events are executed is strictly in order of procedure.
Note that barrier () can only prevent the compiler from ordering the command to be optimized, but does not prevent the CPU from disorderly execution, to really avoid this optimization, you need to use the $, WMB, MB class of functions. ()
Second, why only use the barrier here
[CPP]View PlainCopy
- int i = 0;
- int A;
- i + +;
- A = i;
The execution of CPU chaos is not an all-in-one execution, it is simply a disorderly execution of instructions without dependencies.
In the example above, a=i will not be executed before i++ because there is a dependency between the two instructions, called Waw dependency (write after write). Also, there is raw, war-dependent.
So preempt_disable in the preemption counter Plus is a safe operation, and this counter has associated instructions will not be executed in a disorderly order, just to prevent the compiler to the relevant instructions in advance, with barrier enough.
So when do you want to prevent chaos? Typically in a block of memory, both the CPU is visible and the device is visible. As an example:
A structural body
[CPP]View PlainCopy
- struct DEV
- {
- int enable;
- void *ptr;
- }dev;
This structure is located in the memory, device and CPU can be seen. The order of the correct operation of the device is to assign a value to the PTR pointer first, enabling the device on the Enable write 1. The following code, then, reflects the process:
[CPP]View PlainCopy
- dev.ptr = buffer;
- dev.enable = 1;
There are no correlations for the two write operations here. So CPUs can execute them in random order. This creates a situation where the enable has been written 1 before PTR has been assigned a value. Then the device may start execution when PTR is an illegal value. To prevent this, we need to use a memory barrier. As follows:
[CPP]View PlainCopy
- dev.ptr = buffer;
- WMB ();
- dev.enable = 1;
Three, this preempt_disable ()
First of all, the Linux scheduling mechanism, Linux under two kinds of scheduling methods:
1) Explicit scheduling, the process itself because of the lack of the corresponding requested resources, explicitly call the scheduler, let out the processor, such as: The core application of the signal blocked, spin lock locked.
2) Implicit dispatch, the entire Linux system in the running process of the non-display call scheduler, this is divided into two situations:
A) User-state preemption scheduling such as: In the system call, interrupt processing, the exception processing returned to the user state, the process of the time slice has been exhausted.
B) kernel-state preemption scheduling such as: The current kernel state execution process in advance does not prohibit the kernel state preemption, interrupt generation, interrupt processing and the resulting higher priority process, then will directly preempt the previous kernel state execution.
Common dispatch points
1) When a process is blocked, such as when a resource is being blocked
2) when adjusting parameters such as through Sched_setscheduler (), nice () functions such as the adjustment process scheduling policy, static priority
3) When the sleep process is awakened such as Wake_up wakes up a process in the queue, the current
is set if the process has a higher priority Process tif_need_resched, if kernel preemption is allowed, it will be dispatched once,
( This is controlled by the default wake-up function in the wait queue, the default wake-up function is:
int default_wake_function (wait_queue_t*,unisgned int mode,int sync,void* key)
export_symbol (default_wake_function)
Because Export_symbol has default_wake_function, we can make our own wake-up function.
4) When the interrupt is processed if the tif_need_sched flag is set during interrupt processing, a preemption occurs whenever the interrupt is returned, whether it is to return the kernel state or the user state. Of course, in this will also check whether there is a soft interrupt to be processed.
5) executes the preempt_enable () function.
While we are in the preemptive kernel, there are three places to show the Disable preemption:
1. operation PER-CPU variables, such as smp_processor_id () is this type of problem, but a process is preempted after rescheduling, It is possible to dispatch to other CPUs, when the defined PER-CPU variable is problematic. Here is an example:
struct this_needs_locking Tux[nr_cpus];
tux[smp_processor_id ()] = some_value;
/* task is preempted ... */
something = tux[smp_processor_id ()];
If there is no preemption protection, some_value and something may return different values. When processing the CPU ID, consider using the GET_PCU ()/put_cpu () interface, which implements a sequence that disables preemption, obtains the CPU ID, and enables preemption. It is the recommended method of kernel. The
2. must protect the state of the CPU. This type of problem is architecture-dependent. For example, on x86, entering and exiting the FPU is a critical area that must be used without preemption. The
3. acquire and release locks must be implemented in a process. This means that a lock is held by a process and must be released in this process. The
Disable/enable preemption function mainly includes:
Spin_lock ()/spin_unlock ()
Disable_preempt ()/enable_preempt () (Disable or enable kernel preemption) call the following inc_ Preempt_count ()/dec_preempt_count (), and added the memory barrier.
Inc_preempt_count ()/dec_preempt_count ()
Get_cpu ()/put_cpu ()
The relevant data structures and functions are as follows:
in struct thread_info
{
unisgned int preempt_count;-----(preempt 0-7-bit indicates a kernel state prohibit preemption counter, SOFTIRQ 8-15 indicates a soft interrupt suppression counter, HARDIRQ 16-27 indicates the depth of the interrupt nesting)
}
Kernel-State preemption is allowed only if preempt is 0 o'clock.
Preempt_disable ()--------------primarily performs inc_preempt_count () (increases preempt, thereby prohibiting kernel-state preemption)
Preempt_enable ()--------------primarily perform preempt_enable_no_resched () and preempt_check_resched ()
Preempt_enable_no_resched () Main executive Dec_preempt_count ()
Preempt_check_resched () Main executive Test_thread_flag (tif_need_resched)
Barrier and preempt_disable () Learn "turn"