Linux驱动学习笔记
1 linux设备驱动中的并发控制
1.1中断屏蔽
local_irq_disable()
critical section
local_irq_enable()
这些函数只能禁止和时能本CPU的中断,不能解决SMP引起的中断,适合与自旋锁联合使用。
local_irq_save(flag)
local_irq_restore(flag)
与上面的函数不同,这两个函数可以保存中断位的信息。
1.2原子操作
1.2.1 设置院子变量
void atomic_set(atomic_t* v,int i)设置原子变量v的值为i;
atomic_t v = ATOMIC_INIT(0)设置原子变量v的值为0;
1.2.2 获取原子变量的值
atomic_read(atomic_t* v)返回原子变量的值;
1.2.3 原子变量自加、自减
void atomic_inc(atomic_t* v)原子变量加1
void atomic_dec(atomic_t* v)原子变量减1
1.2.4 操作并测试
int atomic_inc_and_test(atomic_t* v)
int atomic_dec_and_test(atomic_t* v)
int atomic_sub_and_test(int i, atomic_t* v)
上面三个函数对原子变量执行自增,自减,和减操作(没有加)测试其是否返回零
为零:返回true
否则:返回false
1.2.5 操作并返回
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
上述操作对原子变量进行加/减和自增/自减操作,并返回新的值。
1.3 位原子操作
1.3.1设置位
void set_bit(nr, void *addr);
上述操作设置addr地址的第nr位,所谓设置位即将位写为1。
1.3.2 清除位
void clear_bit(nr, void *addr);
上述操作清除addr地址的第nr位,所谓清除位即将位写为0。
1.3.3 改变位
void change_bit(nr, void *addr);
上述操作对addr地址的第nr位进行反置。
1.3.4 测试位
test_bit(nr, void *addr);
上述操作返回addr地址的第nr位。
1.3.5 测试并操作位
int test_and_set_bit(nr, void *addr);
int test_and_clear_bit(nr, void *addr);
int test_and_change_bit(nr, void *addr);
上述test_and_xxx_bit(nr, void *addr)操作等同于执行test_bit(nr, void *addr)
后再执行xxx_bit(nr, void *addr)
1.4 自旋锁
1 定义自旋锁
spinlock_t spin;
2 初始化自旋锁
spin_lock_init(lock)
该宏用于动态初始化自旋锁lock
3 获得自旋锁
spin_lock(lock)
该宏用于获得自旋锁lock,如果能够立即获得锁,它就马上返回,否则,它将自旋在那里,直到该自旋锁的保持者释放;
spin_trylock(lock)
该宏尝试获得自旋锁lock,如果能立即获得锁,它获得锁并返回真,否则立即返回假,实际上不再“在原地打转”
4.释放自旋锁
spin_unlock(lock)
该宏释放自旋锁lock,它与spin_try
自旋锁一般这样被使用,如下所示:
spinlock_t lock;
spin_lock_init(&lock);
spin_lock (&lock) ; //获取自旋锁
...//临界区
spin_unlock (&lock) ; //解锁
自旋锁的衍生
spin_lock_irq() = spin_lock() + local_irq_disable()
spin_unlock_irq() = spin_unlock() + local_irq_enable()
spin_lock_irqsave() = spin_unlock() + local_irq_save()
spin_unlock_irqrestore() = spin_unlock() + local_irq_restore()
spin_lock_bh() = spin_lock() + local_bh_disable()
spin_unlock_bh() = spin_unlock() + local_bh_enable()
驱动工程师应谨慎使用自旋锁,而且在使用中还要特别注意如下几个问题。
1.5 读写自旋锁
1 定义和初始化读写自旋锁
rwlock_t my_rwlock = RW_LOCK_UNLOCKED; /* 静态初始化 */
rwlock_t my_rwlock;
rwlock_init(&my_rwlock); /* 动态初始化 */
2 读锁定
void read_lock(rwlock_t *lock);
void read_lock_irqsave(rwlock_t *lock, unsigned long flags);
void read_lock_irq(rwlock_t *lock);
void read_lock_bh(rwlock_t *lock);
3 读解锁
void read_unlock(rwlock_t *lock);
void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
void read_unlock_irq(rwlock_t *lock);
void read_unlock_bh(rwlock_t *lock);
4 写锁定
void write_lock(rwlock_t *lock);
void write_lock_irqsave(rwlock_t *lock, unsigned long flags);
void write_lock_irq(rwlock_t *lock)
void write_lock_bh(rwlock_t *lock);
int write_trylock(rwlock_t *lock);
5 写解锁
void write_unlock(rwlock_t *lock);
void write_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
void write_unlock_irq(rwlock_t *lock);
void write_unlock_bh(rwlock_t *lock);
1.5 信号量
1 定义信号量
struct semaphore sem;
2 初始化信号量
void sema_init (struct semaphore *sem, int val);
该函数初始化信号量,并设置信号量sem的值为val。尽管信号量可以被初始化为大于1的值从而成为一个计数信号量,但是它通常不被这样使用。
void init_MUTEX(struct semaphore *sem);
该函数用于初始化一个用于互斥的信号量,它把信号量sem的值设置为1,等同于sema_init (struct semaphore *sem, 1)。
void init_MUTEX_LOCKED (struct semaphore *sem);
该函数也用于初始化一个信号量,但它把信号量sem 的值设置为0,等同于sema_init (struct semaphore *sem, 0)
DECLARE_MUTEX(name)
DECLARE_MUTEX_LOCKED(name)
前者定义一个名为name的信号量并初始化为1,后者定义一个名为name的信号量并初始化为0
3.获得信号量
void down(struct semaphore * sem);
该函数用于获得信号量sem,它会导致睡眠,因此不能在中断上下文使用。
int down_interruptible(struct semaphore * sem);
该函数功能与down()类似,不同之处为,因为down()而进入睡眠状态的进程不能被信号打断,而因为down_interruptible()而进入睡眠状态的进程能被信号打断,信号也会导致该函数返回,这时候函数的返回值非0。
int down_trylock(struct semaphore * sem);
该函数尝试获得信号量sem,如果能够立刻获得,它就获得该信号量并返回0,否则,返回非0值。它不会导致调用者睡眠,可以在中断上下文使用。 在使用down_interruptible()获取信号量时,对返回值一般会进行检查,如果非0,通常立即返回-ERESTARTSYS,
4.释放信号量
void up(struct semaphore * sem);
1.6 互斥体
struct mutex my_mutex;
mutex_init(&my_mutex);
下面的两个函数用于获取互斥体。
void fastcall mutex_lock(struct mutex *lock);
int fastcall mutex_lock_interruptible(struct mutex *lock);
int fastcall mutex_trylock(struct mutex *lock);
下列函数用于释放互斥体。
void fastcall mutex_unlock(struct mutex *lock);
1 阻塞与非阻塞IO
1.1 等待队列
1 定义“等待队列头”
wait_queue_head_t my_queue;
2 初始化“等待队列头”
init_waitqueue_head(&my_queue);
3 定义并初始化“等待队列头”
DECLARE_WAIT_QUEUE_HEAD (name)
4 定义等待队列
DECLARE_WAITQUEUE(name, tsk)
该宏用于定义并初始化一个名为name的等待队列。
5 添加/移除等待队列。
void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
add_wait_queue()用于将等待队列wait 添加到等待队列头q指向的等待队列链表
中,而remove_wait_queue()用于将等待队列wait 从附属的等待队列头q指向的等待队
列链表中移除。
5.等待事件
wait_event(queue, condition)
wait_event_interruptible(queue, condition)
wait_event_timeout(queue, condition, timeout)
wait_event_interruptible_timeout(queue, condition, timeout)
等待第一个参数queue 作为等待队列头的等待队列被唤醒,而且第二个参数
condition必须满足,否则阻塞。wait_event()和wait_event_interruptible()的区别在于后
者可以被信号打断,而前者不能。加上_timeout后的宏意味着阻塞等待的超时时间,
6.唤醒队列
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
上述操作会唤醒以queue作为等待队列头的所有等待队列中所有属于该等待队列
头的等待队列对应的进程。
7.在等待队列上睡眠
sleep_on(wait_queue_head_t *q );
interruptible_sleep_on(wait_queue_head_t *q );
sleep_on()函数的作用就是将目前进程的状态置成TASK_UNINTERRUPTIBLE,
并定义一个等待队列,之后把它附属到等待队列头q,直到资源可获得,q引导的等待队列被唤醒。
interruptible_sleep_on()与sleep_on()函数类似,其作用是将目前进程的状态置成
TASK_ INTERRUPTIBLE,并定义一个等待队列,之后把它附属到等待队列头q,直到资源可获得,q引导的等待队列被唤醒或者进程收到信号。