Read-Write Spin lock (Rwlock) is a spin lock mechanism with smaller spin-lock granularity, which retains the concept of "spin". However, in the write operation, there can be only one write process, in terms of reading, can have more than one execution unit, of course, neither read nor write at the same time. In a word: prevent Writing from Reading
Read and write spin lock implementation principle, it is actually spin lock upgrade version. Again, let's take a look at what it does: for writing: Up to one write process; for reading: You can have multiple read and write units at the same time. But reading and writing can't be done at the same time. It is defined under Include\linux\rwlock.h, and it provides a form of function similar to a spin lock, replacing only "Spin_" with "Read_" or "Write_". Includes Read_lock (lock) and Read_unlock (lock), Write_lock (lock), and Write_unlock (lock). Now let's look at how it should be used. And the use of it is actually very simple, as shown in Figure 4.1, very good understanding, that is, in the critical area before and after the addition of the lock function can be, here is not in detail.
Figure 4.1 Example of using a read-write spin lock
And then we'll discuss the core of its implementation. In fact, its implementation is also very simple to discuss, is a bit around. The implementation process is almost identical to Spin_lock, except that the last call to architecture-related functions is arch_read_lock rather than arch_spin_lock (spin-lock Arch_spin_lock final call __ticket_spin_ Lock ()), and so on.
The following analysis of its source code implementation. If there is a process P1 to the operating system for read-write spin lock, set read-write lock variable A has been defined, its initial value is rw_lock_unlocked (0x01000000). In conjunction with Figure 4.2, the source content shown in Figure 4.3, if the process P1 it to apply for read-lock operation, then Read_lock () to read-write lock variable a minus 1, if subtracted after A's value result is negative, then the system already has a process (set to P2) to this read-write lock variable using write-lock function writing _lock () lock, this time the system will continue to wait for P2 to write lock release, the continuous waiting process source code implementation as shown in Figure 4.5. If the process P1 read lock application success, if P1 in the use of read lock process, there is another process P3 request write lock operation. At this point Write_lock () to read and write lock variable a minus 0x01000000 and judge, if the result is Non-zero, then the lock variable A has been locked by the Write_lock () function or with the Read_lock () function lock, It then jumps to the assembly function of the write lock failure shown in Figure 4.6, and continues to wait for the lock variable A to be released by the process P1.
Fig. 4.2 Kernel source code of reading lock function 4.3 kernel source of write lock function
The corresponding unlocking functions are given below. As shown in Figure 4.4.
Figure 4.4 Kernel Source of unlock function
It can be seen that the implementation of the unlock function is actually simple plus 1 and minus 1 operations. The following focuses on two of the two link functions in the lock function that failed to apply for the lock, as shown in Figure 4.5, figure 4.6. The use of pure assembly implementation, as shown in Figure 4.5 of the source code and figure 4.6 of the source content implementation mechanism is actually the same, so here only focus on the analysis of Figure 4.5 source. Figure 4.5 of the source code to understand, figure 4.6 of the source is not a problem.
Before analyzing the source code, the reader may have some questions about the rep instructions in the source code. In fact, rep repeats until the contents of the CX registers are 0. The combination of instructions is limited, only Movs, STOs, LODs, ins and outs, such as Rep STOSB; And rep; NOP is a mixed instruction that is translated into pause instructions. Semicolons represent valid delimiters for instructions in this at&t. The pause directive gives the processor a hint that tells the processor that the sequence of code executed is a spin-waiting state, and the processor avoids the memory sequence conflict based on this hint (Intel manual description).
When the Read_lock () function requests a read lock failure, first add the RW variable to 1 (note: Rdi actually represents RW, which is passed through registers, and the value of RW before 1 is 1). Immediately thereafter, the RDI value is 0 before executing the cmpl instruction, and is compared with the immediate number 1. If not, proceed to the "1" flag bit to continue the cycle comparison until equal, because the system performs a read lock release function, adds the RW variable 1, and can see the read_unlock () function implementation (Figure 4.4). At this point, before exiting the __read_lock_failed function, the RDI variable, the RW variable, is reduced by 1, which means that another process has succeeded in reading the lock, thus ensuring the correctness of the process of subsequent application for read and write locks. So far, the __read_lock_failed function source analysis has been completed, as for __writed_lock_failed is similar to the idea, the reader can be understood by analogy reasoning. Fig. 4.5 Jump function in read lock function Figure 4.6 jump function in write lock function
Operations: defined in #include<linux/rwlock.h> or #include<linux/spinlock.h> rwlock_t x; Rwlock_init (&X); Dynamic initialization of rwlock_t x=rw_lock_unlocked; Static initialization void Read_lock (rwlock_t *lock);
If not obtained, it spins until the read-write lock void Read_unlock (rwlock_t *lock) is obtained; Before you can read a shared resource, you should call the read lock function to lock the shared resource, and then call the Read unlock function to release the shared resource void Write_lock (rwlock_t *lock);
If not obtained, it spins until the read-write lock void Write_unlock (rwlock_t *lock) is obtained;
Before you write to a shared resource, you should call the Write lock function to lock the shared resource, and then call the Write unlock function to release the shared resource Read_trylock (lock); Write_trylock (lock); READ_LOCK_IRQ (lock); Readers acquire read-write locks and prohibit local interrupts of READ_UNLOCK_IRQ (lock);
The reader releases the read-write lock and enables the local interrupt Write_lock_irq (lock),//writer acquires the read-write lock, and disables the local interrupt Write_unlock_irq (lock); Read_lock_irqsave (lock, flags),//reader acquires read-write locks, saves interrupt flags, and disables local interrupts Read_unlock_irqrestores (Lock,flags); Readers release read-write locks while resuming interrupt flags
, and enables local interrupt Write_lock_irqsave (lock,flags),//writer acquires read-write lock while preserving interrupt flag, and prohibits local interrupt write_unlock_irqstore (lock,flags);
READ_LOCK_BH (lock),//reader acquires read-write lock, and disables local soft interrupt read_unlock_bh (lock);
WRITE_LOCK_BH (lock),//writer acquires read-write lock, and disables local soft interrupt write_unlock_bh (lock); Use case: RWlock_t lock; Define Rwlock Rwlock_init (&lock);
Initialize Rwlock//read-time Acquisition lock Read_lock (&lock); ...
Critical area ... read_unlock (&lock);
Get lock Write_lock_irqsave (&lock, flags) when writing; ... Critical area ... write_unlock_irqrestore (&lock, flags);