DeviceDriver (4): Concurrency and competition

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)

 

Guess you like

Origin blog.csdn.net/qq_34968572/article/details/103583840