1: Atomic manipulation
Atomic operations refer to instructions that cannot be further divided. Generally, atomic operations are used for variable or bit operations.
<1>Atomic shaping operation
1. Definition of atomic operation
typedef struct {
int counter;
} atomic_t;
/* 64位SOC */
typedef struct {
long long counter;
} atomic64_t;
2. Kernel function
(1) Initialize atomic variables:
#define ATOMIC_INIT(i) { (i) }
atomic_t flag = ATOMIC_INIT(0);
(2) Read the value of v and return:
#define atomic_read(v) ACCESS_ONCE((v)->counter)
(3) Set v to
#define atomic_set(v,i) (((v)->counter) = (i))
(4) v increases
static inline void atomic_add(int i, atomic_t *v)
(5) v decreases
static inline void atomic_sub(int i, atomic_t *v)
(6) v increases automatically:
static inline void atomic_inc(atomic_t *v)
(7) v decrement:
static inline void atomic_dec(atomic_t *v)
(8) v is decremented and returns the value of v:
#define atomic_dec_return(v) (atomic_sub_return(1, v))
(9) v increments and returns v value:
#define atomic_inc_return(v) (atomic_add_return(1, v))
(10) If v is reduced by i, it returns true if it is equal to 0, otherwise it is false:
#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)
(11) If v plus i is negative, it returns true, otherwise it is false:
#define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)
(12) v increases automatically, the result is 0, it is true, otherwise it is false:
#define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0)
(13) v is decremented, the result is 0 is true, otherwise it is false:
#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0)
<2>Atomic bit operation
Bit manipulation is to directly manipulate the memory
(1) Set the nr bit of the p address to 1:
static inline void set_bit(int nr, volatile unsigned long *addr)
(2) Set the nr bit of the p address to 0:
static inline void clear_bit(int nr, volatile unsigned long *addr)
(3) Flip the nr bit of the p address:
static inline void change_bit(int nr, volatile unsigned long *addr)
(4) Get the value of the nr bit of the p address:
static __always_inline int test_bit(unsigned int nr, const unsigned long *addr)
(5) Set the nr position of the p address to 1, and return the original value of the nr bit:
static inline int test_and_set_bit(int nr, volatile unsigned long *addr)
(6) Clear the nr bit of the p address to zero and return the original value of the nr bit:
static inline int test_and_clear_bit(int nr, volatile unsigned long *addr)
(7) Flip the nr bit of the p address and return the original value of the nr bit:
static inline int test_and_change_bit(int nr, volatile unsigned long *addr)
Two: Spin lock
Spin locks are used in situations where atomic operations are not adequate, such as threads accessing structures. When a thread wants to access a shared resource, it must first acquire the corresponding lock. The lock can only be held by one thread. Only when this thread releases the lock can it be acquired by other threads, otherwise it will always wait "in place" The lock is available. This makes the system performance extremely low, so the spin lock is suitable for short-term lightweight locking.
1. Definition of spin lock
typedef struct spinlock {
union {
struct raw_spinlock rlock;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
struct {
u8 __padding[LOCK_PADSIZE];
struct lockdep_map dep_map;
};
#endif
};
} spinlock_t;
spinlock_t lock;
2. Kernel function
(1) Define and initialize a variable
DEFINE_SPINLOCK(spinlock_t lock)
(2) Initialize the spin lock
spin_lock_init(spinlock_t *lock);
(3) Acquire a spin lock
static inline void spin_lock(spinlock_t *lock)
(4) Release the spin lock
static inline void spin_unlock(spinlock_t *lock)
(5) Try to obtain the spin lock, if not obtained, return 0
static inline int spin_trylock(spinlock_t *lock)
(6) Check whether the spin lock is acquired, if not return non-zero
static inline int spin_is_locked(spinlock_t *lock)
The above spin lock API functions are suitable for SMP or multi-threaded concurrent access under a single CPU that supports preemption, but the critical area protected by the spin lock must not call any API functions that cause sleep and blocking, otherwise it may cause deadlock. For interrupt programs, local interrupts must be disabled before acquiring the lock, otherwise it will lead to deadlocks (interrupts control the CPU usage rights, and threads with lock rights cannot get the CPU usage rights to release the locks, resulting in deadlocks. ) To this end, the following API functions are used to handle the problem of obtaining locks by interrupt programs.
(1) Disable local interrupts and obtain spin locks
static inline void spin_lock_irq(spinlock_t *lock)
(2) Activate the local interrupt and release the spin lock
static inline void spin_unlock_irq(spinlock_t *lock)
(3) Save the interrupt status, prohibit local interrupts, and obtain the spin lock
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags)
(4) Restore the interrupt state to the previous state, activate the local interrupt, and release the spin lock
static inline void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
Three: read-write lock
The read-write spin lock stipulates that only one write operation is allowed at a time, and no read operation is allowed. When there is no write operation, one or more threads are allowed to hold the read lock and perform read operations concurrently.
1. Definition of read-write lock
typedef struct {
arch_rwlock_t raw_lock;
} rwlock_t;
2. Kernel function
(1) Define initialization read-write lock
DEFINE_RWLOCK(rwlock_t lock)
void rwlock_init(rwlock_t *lock)
(2) Acquire a read/write lock
void read_lock(rwlock_t *lock)
void write_lock(rwlock_t *lock)
(3) Release the read/write lock
void read_unlock(rwlock_t *lock)
void write_unlock(rwlock_t *lock)
(4) Disable local interrupts and obtain read/write locks
void read_lock_irq(rwlock_t *lock)
void write_lock_irq(rwlock_t *lock)
(5) Open the local interrupt and release the read/write lock
void read_unlock_irq(rwlock_t *lock)
void write_unlock_irq(rwlock_t *lock)
(6) Save the interrupt status, prohibit local interrupts and obtain a read/write lock
void read_lock_irqsave(rwlock_t *lock, unsigned long flags)
void write_lock_irqsave(rwlock_t *lock, unsigned long flags)
(7) Restore the interrupt state to the previous state, activate the local interrupt and release the read/write lock
void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags)
void write_unlock_irqrestore(rwlock_t *lock, unsigned long flags)
Four: sequence lock
Sequential locks are derived from read-write locks. When using sequential locks, read operations are allowed while writing, but concurrent write operations are not allowed.
1. Sequence lock definition
typedef struct {
struct seqcount seqcount;
spinlock_t lock;
} seqlock_t;
2. Sequence lock write operation
(1) Define and initialize the sequence lock
DEFINE_SEQLOCK(seqlock_t sl)
void seqlock_ini seqlock_t *sl)
(2) Sequence lock write operation (refer to read-write lock for function)
void write_seqlock(seqlock_t *sl)
void write_sequnlock(seqlock_t *sl)
void write_seqlock_irq(seqlock_t *sl)
void write_sequnlock_irq(seqlock_t *sl)
void write_seqlock_irqsave(seqlock_t *sl, unsigned long flags)
void write_sequnlock_irqrestore(seqlock_t *sl, unsigned long flags)
(3) Sequence lock read operation
This function is called when the reading unit accesses the shared resource, this function will return the sequence number of the sequence lock
unsigned read_seqbegin(const seqlock_t *sl)
After reading, call this function to check if there are any resources for writing during the reading process, and read again if there is
unsigned read_seqretry(const seqlock_t *sl, unsigned start)
Five: Semaphore
The semaphore can make the thread waiting for the resource enter the dormant state to improve the efficiency of the processor, but the overhead of the semaphore is relatively large, because the semaphore causes the thread to enter the dormant state will switch threads, so the semaphore is suitable for those who occupy resources for a long time In the case of semaphore, the semaphore cannot be used in interrupts, because the semaphore will cause sleep, and interrupts cannot sleep.
1. Semaphore definition
struct semaphore {
raw_spinlock_t lock;
unsigned int count;
struct list_head wait_list;
};
2. Kernel function
(1) Define the semaphore and set the value of the semaphore to 1
DEFINE_SEMAPHORE(name)
(2) Initialize the semaphore
static inline void sema_init(struct semaphore *sem, int val)
(3) Obtain semaphore
void down(struct semaphore *sem)
(4) Try to acquire the semaphore, if the semaphore is acquired, then acquire and return 0, otherwise return non-zero
int down_trylock(struct semaphore *sem)
(5) Obtain the semaphore, which is different from down in that it can be interrupted by the signal after entering sleep
int down_interruptible(struct semaphore *sem)
(6) Sleep timeout
int down_timeout(struct semaphore *sem, long timeout)
(7) Release the semaphore
void up(struct semaphore *sem)
Six: Mutex
Mutual exclusion means that only one thread can access shared memory at a time and cannot apply for a mutex recursively. Mutex will cause sleep, so mutex cannot be used in interrupt, only spin lock can be used in interrupt.
1. Definition of Mutex
struct mutex {
/* 1: unlocked, 0: locked, negative: locked, possible waiters */
atomic_t count;
spinlock_t wait_lock;
struct list_head wait_list;
};
2. Kernel function
(1) Define and initialize a mutex variable
DEFINE_MUTEX(mutexname
(2) Initialize mutex
void mutex_init(mutex *lock)
(3) Mutex is locked, if it cannot be locked, it will sleep
void mutex_lock(struct mutex *lock)
(4) Mutex unlock
void mutex_unlock(struct mutex *lock)
(5) Try to obtain mutex, return 1 if successful, and return 0 if failed
int mutex_trylock(struct mutex *lock)
(6) Determine whether mutex is acquired, if yes, return 1, otherwise 0
int mutex_is_locked(struct mutex *lock)
(7) Failure to acquire semaphore can be interrupted by signal after entering sleep
int __must_check mutex_lock_interruptible(struct mutex *lock)