Linux source code research-kernel development-volatile type

C programmers usually think that volatile means that a variable can be changed outside the currently executing thread, so when using shared data structures in the kernel, C programmers often use variables such as volatile, but using volatile in the kernel is almost always wrong.

The key to understanding volatile is to know that its purpose is to eliminate optimizations. In the kernel, programmers must prevent accidental concurrent access from corrupting shared data structures, which is actually a completely different task.

Like volatile, the kernel provides many primitives to ensure data safety during concurrent access (spinlocks, mutexes, memory barriers, etc.), and also to prevent accidental optimizations. If these kernel primitives can be used correctly, then there is no need to use volatile. In correct kernel code, all volatile can do is make things slower.

Consider this typical kernel code

spin_lock(&the_lock);
do_something_on(&shared_data);
do_something_else_with(&shared_data);
spin_unlock(&the_lock);

If all code follows locking rules, it is impossible to accidentally change the value of shared_data while the_lock is held. Any other code that might access that data will wait on this lock. Spinlock primitives, like memory barriers, are written explicitly so that data accesses are not optimized across their pages. So originally the compiler thought it knew what was going to be in shared_data, but because the spin_lock() call is the same as a memory barrier, it will force the compiler to forget everything it knows, so there will be no problem when accessing the data. .

The volatile storage type was originally defined for those memory-mapped I/O registers. In the kernel, register accesses should also be protected by locks, but one also does not want the compiler to "optimize" register accesses in critical sections. Memory access for I/O in the kernel is done through access functions. Direct access to I/O memory through pointers is deprecated and does not work on all architectures. Those access functions are written to prevent accidental optimizations, so, again, volatile types are not necessary.

In the kernel, there are some rare cases where volatile still makes sense:

  • On systems with some architectures that allow direct I/O memory access, the aforementioned access function can use volatile.
  • Some inline assembly code that changes memory has no other obvious side effects, but it is possible that it will be deleted by GCC. This deletion can be prevented by adding the volatile keyword to the assembly declaration.
  • The Jiffies variable is a special case, and although it can have a different value each time it is referenced, reading the jiffies variable does not require any special locking protection. So jiffies variables can use volatile, but other variables of the same type as jiffies are not approved to use volatile. Jiffies are considered a "stupid legacy" because solving the problem is more troublesome than maintaining the status quo.
  • Since some I/O devices may have contiguous memory, sometimes pointers to data structures in contiguous memory need to use volatile correctly. An example of such a situation is the ring buffer used by a network adapter, where the adapter uses a change pointer to indicate which descriptors have been processed.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325787231&siteId=291194637