9.IMX6ULL LINUX驱动之原子操作原理

246 阅读3分钟

一、并发与竞争

linux是一个多任务的系统。多任务就存在抢占,抢占资源。但是不可以同时抢占,否则会产生混乱。因此,我们要保护资源,那么什么是资源呢?资源就是共享资源,就是一系列的变量存取的数据。

并发与竞争的处理方法

1.原子操作

原子操作简单说一下,就是程序只执行一句汇编。向自增,编译以后会执行三步,就肯定不是原子操作了。

竞争与并发为什么要提及原子操作呢?是因为,如果在多任务中,现在我们有两个任务。任务①自增变量a , 此时任务②也执行自增变量a,那么就会得到混乱的程序。

原子操作的API函数。

1.申请变量在linux/types.h中。

typedef struct {
    int counter;
} atomic_t;
atomic_t a;         //定义变量
a = ATOMIC_INIT(0);  //初始化

2.include/asm/atomic.h中的API

包括加减乘除,置位复位等。

2.自旋锁

一个锁适用于保护资源的。当一个任务要访问共享资源,就要申请自旋锁,当其他线程想要访问资源的时候,就要原地打转进入等待状态。只有当任务释放锁以后,其他任务才可以申请该锁。

一定不能使用使得CPU引起休眠或者阻塞的程序,否则会造成死锁。情景复现:当testA上锁后,调用休眠程序,如果再此期间,testB的权限比testA高,也想抢占锁的话,CPU就会离开testA的程序,B既无法抢占锁,程序就会卡在这里,造成死锁。

5_spin_lock.png

由上段的情景,我们的线程内有锁的话,中断也有锁的话,也容易产生死锁。因为中断时可以直接打断线程的,它比线程的优先级高。所以也要注意这种情况的发生。所以我们可以使用禁止本地中断上锁的函数API。spin_lock_irq(spinlock_t lock); 。但是中断太多,无法确保,就使用**spin_lock_irqsave(spinlock_t lock,unsigned long flags); 函数。

注意:自旋锁只适合于短时间的加锁,如果长时间的加锁会降低系统性能。如果想要长时间持锁,就要换其他办法。

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;

2.spinlock API

int spin_lock_init(_lock);          //申请锁
void spin_lock(spinlock_t *lock);   //上锁
void spin_unlock(spinlock_t *lock); //释放锁
int spin_trylock(spinlock_t *lock); //尝试上锁//处理中断中的锁函数
void spin_lock_irq(spinlock_t *lock);//禁止中断上锁
void spin_unlock_irq(spinlock_t *lock);//释放本地中断上锁
void spin_lock_irqsave(spinlock_t *lock,unsigned long flags);
void spin_unlock_irqsave(spinlock_t *lock,unsigned long flags);
​
//api中带bh的后缀是中断下半部
...

3,信号量

如果临界区比较大的话,使用信号量。信号量可以使得线程进入休眠状态。因此,信号量可以适合于资源比较久的场合。它不能使用与中断,因为会使得休眠,而我们不能让中断休眠。

信号量的效果:如果多任务处理,每一个任务都会处理到,只不过会有一个先后过程,而不会向自旋锁一样不让执行。

持有资源时间较短不适合信号量是因为频繁切换线程,使得线程休眠是划不来的,开销要大。

semaphore.h

struct semaphore {
    raw_spinlock_t      lock;
    unsigned int        count;
    struct list_head    wait_list;
};
​
static inline void sema_init(struct semaphore *sem, int val);
​
extern void down(struct semaphore *sem);        //申请信号量
extern int __must_check down_interruptible(struct semaphore *sem);
extern int __must_check down_killable(struct semaphore *sem);
extern int __must_check down_trylock(struct semaphore *sem);
extern int __must_check down_timeout(struct semaphore *sem, long jiffies);
extern void up(struct semaphore *sem);          //释放信号量

4.互斥体

mutex,和信号量一样。用于阻塞线程的。首选它。其他文档有详细写它。

\