中断屏蔽:
disable_irq(unsigned int irq); --关中断
enable_irq(unsigned int irq); --开中断
local_irq_disable(); --关全部中断
local_irq_enable(); --使能全部中断
local_bh_disable(); --禁止中断底半部
local_bh_enable(); --使能中断底半部
local_irq_save(flags); --保存cpu中断位信息
local_irq_restore(x); --恢复cpu中断位信息
原子操作:
(整型原子操作)
atomic_t --原子变量类型
atomic_set(v,i); --设置原子变量为i
atomic v = ATOMIC_INIT(0); --定义并初始化v
atomic_read(v); --读取原子变量的值
atomic_add(i,v); --加i
atomic_sub(i,v); --减i
atomic_inc_and_test(v); --
atomic_dec_and_test(v); --
atomic_sub_and_test(i,v); --
atomic_add_return(i,v);
atomic_sub_return(i,v);
atomic_inc_return(v);
atomic_dec_return(v);
(位原子操作)
set_bit(nr,addr); --置1
clear_bit(nr,addr); --清0
change_bit(nr,addr); --反转
test_bit(nr,addr); --测试
test_and_set_bit(nr, addr);
test_and_clear_bit(nr,addr);
test_and_change_bit(nr,addr);
(使用原子变量实现设备只能被一个进程打开,或保护临界资源)
static void open(.....)
{
//防止进程被多个设备打开
if( !atomic_dec_and_test(v) )
{
atomic_inc(v);
}
}
static void release(....)
{
//释放设备
atomic_inc(v);
}
自旋锁:
(关于使用自旋锁时要避免使用引起进程调度的操作,例如copy_from_user(), copy_to_user(), kmalloc(), msleep()等有可能导致进程阻塞,所以不能用。具体原因还为明白?)
spinlock_t lock;
spin_lock_init(lock);
spin_lock(lock); --没有获得锁则在原地自旋
spin_trylock(lock); --没有获得锁则立刻返回假
spin_unlock(lock);
(保护临界区资源)
void fun()
{
spinlock_t lock;
spin_lock_init(lock);
.....
spin_lock(lock);
//临界区,如果临界区较大不适合用自旋锁
spin_unlock(lock);
.....
}
能阻止抢占进程的打扰,但不能防止中断和底半部。需要结合中断屏蔽使用,下面是完整版
spin_lock_irq(lock) = spin_lock(lock) + local_irq_disable();
spin_unlock_irq(lock) = spin_unlock(lock)+ local_irq_enable();
spin_lock_irqsave(lock,flags) = spin_lock(lock) + local_irq_save(flags);
spin_unlock_irqrestore(lock,flags) = spin_unlock(lock) + local_irq_restore(x);
spin_lock_bh(lock) = spin_lock(lock) + local_bh_disable(void);
spin_unlock_bh(lock) = spin_unlock(lock) + local_bh_enable(void);
static int open(...)
{
//实现设备只能被一个进程打开
spin_lock_irq(lock);
if( cnt > 0 )
{
spin_unlock_irq(lock);
return -EBUSY;
}
cnt ++;
spin_unlock_irq(lock);
......
return 0;
}
static int release(...)
{
//释放资源
spin_lock_irq(lock);
cnt--;
spin_unlock_irq(lock);
.....
return 0;
}
一般的自旋锁同时锁定读写操作,实际上读操作是允许多个同时进行的。 读写自旋锁允许读的并发访问,禁止写的并发访问,禁止读与写的共同发生。
rwlock_t lock; (读写自旋锁)
rwlock_init(&lock);
/** 读锁定*/
read_lock(lock);
read_lock_irqsave(lock,flags);
read_lock_irq(lock); = read_lock(lock) + local_irq_disable;
read_lock_bh(lock);
/** 读解锁*/
read_unlock(lock);
read_unlock_irq(lock) = read_unlock(lock) + local_irq_enable;
read_unlock_irqrestore(lock,flags);
read_unlock_bh(lock);
/** 写锁定*/
write_lock(lock);
write_lock_irq(lock);
write_lock_bh(lock);
write_lock_irqsave(lock,flags);
write_trylock(lock);
/** 写解锁*/
write_unlock(lock);
write_unlock_bh(lock);
write_unlock_irq(lock);
write_unlock_irqrestore(lock,flags);
void example()
{
rwlock_t lock;
rwlock_init( &lock );
/** 使用读锁*/
read_lock(lock);
//临界区资源
read_unlock(lock);
/** 使用写锁*/
write_lock_irqsave( &lock, flag);
//临界区资源
write_unlock_irqrestore( &lock, flag);
}
还有一种RCU(read copy update)的方式。读操作时不加锁,写操作时先对拷贝数据进行写,然后使用回调机制在共享数据空闲时再将修改的数据写回。这对读操作较多的系统比较有利。
rcu_read_lock();
rcu_read_lock_bh();
rcu_read_unlock();
rcu_read_unlock_bh();
synchronize_rcu(void);
synchronize_sched();
call_rcu(struct rcu_head * head,void(* func)(struct rcu_head * rcu));
call_rcu_bh();
RCU还提供了一些为 list_head 和 hlist_head的共享数据访问工作的函数。
信号量:
//信号量会导致睡眠,所以不能在中断上下文使用
struct semaphore sem;
sema_init( &sem, 1);
DECLARE_MUTEX( sem); = struct semaphore sem = sema_init( temp, 1);
down( &sem); --睡眠后不能被信号打断
down_interruptible( &sem); --睡眠后可被信号打断
down_trylock( &sem); --不成功则直接返回
up( &sem);
static struct semaphore sem;
sema_init( &sem, 1);
static int open(....)
{
//使设备只能被打开一次
if( down_trylock( &sem))
{
return -EBUSY;
}
...
return 0;
}
static int release(...)
{
//释放
up( &sem);
return 0;
}
完成量:
struct completion com;
init_completion( &com);
DECLARE_COMPLETION( com); --declare and init a @completion
wait_for_completion( &com);
complete( &com); --rouse a @completion
complete_all( &com); --rouse all @completion
互斥体:
struct mutex mut;
mutex_init( &mutex);
mutex_lock( &mut);
mutex_trylock( &mut);
mutex_lock_interruptible( &mut);
mutex_unlock( &mut);
使用例子:
struct mutex mut;
mutex_init( &mut);
mutex_lock( &mut);
//....
mutex_unlock( &mut);
等待队列:
//定义等待队列
wait_queue_head_t wq;
init_waitqueue_head( &wq);
DECLARE_WAIT_QUEUE_HEAD( wq);
DECLARE_WAITQUEUE( wait, current);
//添加队列
add_wait_queue( &wq, &wait);
remove_wait_queue( &wq, &wait);
//相关操作
schedule( );
set_current_state( TASK_INTERRUPTIBLE);
__set_current_state( TASK_RUNNING);
wake_up( &wq);
wait_event( wq, condition);
static wait_queue_head_t wq;
void init(.....) //用来初始化等待队列
{
init_waitqueue_head( &wq);
}
void read(.....)
{
DECLARE_WAITQUEUE( wait, current); //用来定义一个实体
......
add_wait_queue( &wq, &wait); //添加进队列
......
set_current_state( TASK_INTERRUPTIBLE);
schedule( ); // 进程退出点 and 回归点
......
remove_wait_queue( &wq, &wait); //从队列里移除
set_current_state( TASK_RUNNING);
......
}
void fun(...)
{
wake_up( &wq); //唤醒阻塞的进程
}
轮询:
在应用层的使用例子
void AppFun(....)
{
fd_set rfds;
int fd_1, fd_2;
fd_1 = open( "/dev/fifo1", O_RDONLY|O_NONBLOCK);
fd_2 = open( "/dev/fifo2", O_RDONLY|O_NONBLOCK);
while(1)
{
FD_ZERO( &rfds);
FD_SET( fd_1, &rfds);
FD_SET( fd_2, &rfds);
select( MAX(fd_1, fd_2), &rfds, NULL, NULL, NULL);
if( FD_ISSET( fd_1, &rfds))
{
.....
}
if( FD_SET( fd_2, &rfds))
{
.....
}
}
}
而在驱动层的使用和原理还有待研究:
()