当前位置: 代码迷 >> 综合 >> DeviceDriver(四):并发与竞争
  详细解决方案

DeviceDriver(四):并发与竞争

热度:65   发布时间:2023-10-24 00:37:43.0

一:原子操作

原子操作指不能再进一步分割的指令,一般原子操作用于变量或者位操作。

<1>原子整形操作

1、原子操作定义

typedef struct {int counter;
} atomic_t;/* 64位SOC */
typedef struct {long long counter;
} atomic64_t;

2、内核函数

(1)初始化原子变量:

#define ATOMIC_INIT(i)	{ (i) }atomic_t flag = ATOMIC_INIT(0);

(2)读取v值并返回:

#define atomic_read(v)	ACCESS_ONCE((v)->counter)

(3)设置v为

#define atomic_set(v,i)	(((v)->counter) = (i))

(4)v增加

static inline void atomic_add(int i, atomic_t *v)

(5)v减少

static inline void atomic_sub(int i, atomic_t *v)

(6)v自增:

static inline void atomic_inc(atomic_t *v)

(7)v自减:

static inline void atomic_dec(atomic_t *v)

(8)v自减并返回v值:

#define atomic_dec_return(v)    (atomic_sub_return(1, v))

(9)v自增并返回v值:

#define atomic_inc_return(v)    (atomic_add_return(1, v))

(10)v减少i如果等于0就返回真,否则为假:

#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)

(11)v加上i如果为负就返回真,否则为假:

#define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)

(12)v自增,结果为0为真,否则为假:

#define atomic_inc_and_test(v)	(atomic_add_return(1, v) == 0)

(13)v自减,结果为0为真,否则为假:

#define atomic_dec_and_test(v)	(atomic_sub_return(1, v) == 0)

<2>原子位操作

位操作是直接对内存进行操作

(1)设置p地址第nr位为1:

static inline void set_bit(int nr, volatile unsigned long *addr)

(2)设置p地址第nr位为0:

static inline void clear_bit(int nr, volatile unsigned long *addr)

(3)将p地址第nr位进行翻转:

static inline void change_bit(int nr, volatile unsigned long *addr)

(4)获取p地址第nr位的值:

static __always_inline int test_bit(unsigned int nr, const unsigned long *addr)

(5)将p地址第nr位置1,并返回nr位原来的值:

static inline int test_and_set_bit(int nr, volatile unsigned long *addr)

(6)将p地址第nr位清零,并返回nr位原来的值:

static inline int test_and_clear_bit(int nr, volatile unsigned long *addr)

(7)将p地址第nr位翻转,并返回nr位原来的值:

static inline int test_and_change_bit(int nr, volatile unsigned long *addr)

二:自旋锁

       自旋锁用于原子操作不能胜任的情况下,例如线程访问结构体。当一个线程要访问某个共享资源的时候首先要获取相应的锁,锁只能被一个线程持有,只有这个线程释放了锁,才可以被其他线程获取,否则会一直在“原地”等待锁可用为止。这样使得系统性能极低,因此自旋锁适用于短时期的轻量级加锁。

1、自旋锁定义

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、内核函数

(1)定义并初始化一个变量

DEFINE_SPINLOCK(spinlock_t lock)

(2)初始化自旋锁

spin_lock_init(spinlock_t *lock);

(3)获取自旋锁

static inline void spin_lock(spinlock_t *lock)

(4)释放自旋锁

static inline void spin_unlock(spinlock_t *lock)

(5)尝试获取自旋锁,如果没获取返回0

static inline int spin_trylock(spinlock_t *lock)

(6)检查自旋锁是否被获取,如果没有返回非0

static inline int spin_is_locked(spinlock_t *lock)

      以上自旋锁API函数适用于SMP或支持抢占的单CPU下多线程并发访问,但是被自旋锁保护的临界区一定不能调用任何引起睡眠和阻塞的API函数,否则可能会导致死锁现象。对于中断程序来讲,在获取锁之前一定要禁止本地中断,否则也会导致死锁(中断掌握CPU使用权,而拥有锁权限的线程却无法得到CPU使用权来释放锁,从而导致死锁发生)为此有以下API函数用来处理中断程序获取锁的问题。

(1)禁止本地中断,并获取自旋锁

static inline void spin_lock_irq(spinlock_t *lock)

(2)激活本地中断,并释放自旋锁

static inline void spin_unlock_irq(spinlock_t *lock)

(3)保存中断状态,禁止本地中断,并获取自旋锁

void spin_lock_irqsave(spinlock_t *lock, unsigned long flags)

(4)将中断状态恢复到之前的状态,并且激活本地中断,释放自旋锁

static inline void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)

三:读写锁

读写自旋锁规定一次只能允许一个写操作,而且不能进行读操作。当没有写操作的时候允许一个或多个线程持有读锁并发进行读操作。

1、读写锁定义

typedef struct {arch_rwlock_t raw_lock;
} rwlock_t;

2、内核函数

(1)定义初始化读写锁

DEFINE_RWLOCK(rwlock_t lock)void rwlock_init(rwlock_t *lock)

(2)获取读/写锁

void read_lock(rwlock_t *lock)void write_lock(rwlock_t *lock)

(3)释放读/写锁

void read_unlock(rwlock_t *lock)void write_unlock(rwlock_t *lock)

(4)禁止本地中断并获取读/写锁

void read_lock_irq(rwlock_t *lock)void write_lock_irq(rwlock_t *lock)

(5)打开本地中断并释放读/写锁

void read_unlock_irq(rwlock_t *lock)void write_unlock_irq(rwlock_t *lock)

(6)保存中断状态,禁止本地中断并获取读/写锁

void read_lock_irqsave(rwlock_t *lock, unsigned long flags)void write_lock_irqsave(rwlock_t *lock, unsigned long flags)

(7)将中断状态恢复到以前的状态,激活本地中断并释放读/写锁

void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags)void write_unlock_irqrestore(rwlock_t *lock, unsigned long flags)

四:顺序锁

顺序锁是在读写锁基础上衍生而来,使用顺序锁的时候允许在写的时候进行读操作,但不允许并发的写操作。

1、顺序锁定义

typedef struct {struct seqcount seqcount;spinlock_t lock;
} seqlock_t;

2、顺序锁写操作

(1)定义并初始化顺序锁

DEFINE_SEQLOCK(seqlock_t sl)void seqlock_ini seqlock_t *sl)

(2)顺序锁写操作(作用参考读写锁)

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)顺序锁读操作

读单元访问共享资源的时候调用此函数,此函数会返回顺序锁的顺序号

unsigned read_seqbegin(const seqlock_t *sl)

读结束以后调用此函数检查在读的过程中有没有资源进行写操作,如果有就重新读

unsigned read_seqretry(const seqlock_t *sl, unsigned start)

五:信号量

       信号量可以使等待资源的线程进入休眠状态从而提高处理器的使用效率,但是信号量的开销较大,因为信号量使线程进入休眠状态后会切换线程,因此信号量适用于那些占用资源比较久的场合,其次信号量中不能用于中断中,因为信号量会引起休眠,而中断不能休眠。

1、信号量定义

struct semaphore {raw_spinlock_t		lock;unsigned int		count;struct list_head	wait_list;
};

2、内核函数

(1)定义信号量并设置信号量的值为1

DEFINE_SEMAPHORE(name)

(2)初始化信号量

static inline void sema_init(struct semaphore *sem, int val)

(3)获取信号量

void down(struct semaphore *sem)

(4)尝试获取信号量,如果获取到信号量就获取并返回0,否则返回非0

int down_trylock(struct semaphore *sem)

(5)获取信号量,和down不同的是进入休眠后可以被信号打断

int down_interruptible(struct semaphore *sem)

(6)休眠超时

int down_timeout(struct semaphore *sem, long timeout)

(7)释放信号量

void up(struct semaphore *sem)

六:互斥体

        互斥表示一次只能有一个线程可以访问共享内存,不能递归申请互斥体。互斥体会导致休眠,因此不能在中断中使用mutex,中断中只能使用自旋锁。

1、互斥体定义

struct mutex {/* 1: unlocked, 0: locked, negative: locked, possible waiters */atomic_t		count;spinlock_t		wait_lock;struct list_head	wait_list;
};

2、内核函数

(1)定义并初始化一个mutex变量

DEFINE_MUTEX(mutexname

(2)初始化mutex

void mutex_init(mutex *lock)

(3)mutex上锁,如果无法上锁就休眠

void mutex_lock(struct mutex *lock)

(4)mutex解锁

void mutex_unlock(struct mutex *lock)

(5)尝试获取mutex,成功返回1,失败返回0

int mutex_trylock(struct mutex *lock)

(6)判断mutex是否被获取,是的话返回1,否则为0

int mutex_is_locked(struct mutex *lock)

(7)获取信号量失败进入休眠后可以被信号打断

int __must_check mutex_lock_interruptible(struct mutex *lock)