Linux中内核同步方法总结如下:
1、原子整数操作:
3、spinlock自旋锁
1、原子整数操作:
原子操作可以保证指令以原子的方式运行--执行过程不能被打断.原子操作把读取和改变变量的行为包含在一个单步中执行,从而避免了竞争.
定义:atomix_t t;
Atomic_t u = ATOMIC_INIT(0);
操作函数定义在中,原子操作是内联函数.通过内嵌汇编指令来实现.
Int atomic_read(atomic_t v)
Void atomic_set(atomic_t *v,int i)
Void atomic_add(int i,atomic_t *v)
Void atomic_sub(int i,atomic_t *v);
Void atomic_inc(atomic_t *v);
Void atomic_dec(atomic_t *v);
原子加减1
Int atomic_sub_and_test(int i,atomic_t *v)
原子地从v减i,如果等于0,返回真,否则返回假.
Int atomic_add_negative(int i,atomic_t *v)
结果为负数返回真,否则返回假.
Int atomic_dec_and_test(atomic_t *v)
Int atomic_inc_and_test(atomic_t *v);
结果等于0 返回真,否则返回假.
定义在 这些函数对某个普通的内存地址.
可用的位操作:
Void set_bit(nr,voie *addr);
设置addr指向的数据项的nr位.
Void clear_bit(nr,void *addr)
清除addr指向的数据项的第nr位.
Test_bit(nr,void *addr)
返回当前的指定位.
Int test_and_set_bit(nr,void *addr);
设置nr位的同时返回原来的值
3、spinlock自旋锁
自旋锁最多只能被一个可执行的线程持有,等待锁的进程采用忙循环等待.4、读取者/写入者自旋锁
定义在 and 自旋锁可以在中断程序中使用.在这种情况下,其他代码要获得锁必须首先关闭中断.在配置内核时,可以选定CONFIG_DEBUG_SPINLOCK选项,打开spinlock的调试功能.
1.spinlock的初始化
静态:spinlock_t my_lock=SPIN_LOCK_UNSIGNED ;
动态:void spin_lock_init(spinlock_t *lock);
2.加锁函数
Void spin_lock(spinlock_t *lock);
Void spin_lock_irqsave(spinlock_t *lock,unsigned long flags);
Void spin_lock_irq(spinlock_t *lock);
Void spin_lock_bh(spinlock_t *lock);
如果我们的一个自旋锁能够在中断处理程序中使用,或者在中断的下半部获得,则必 须使用禁止中断的spin_lock 形式.
3.解锁函数
Void spin_unlock(spinlock_t *lock);
Void spin_unlock_irqsave(spinlock_t *lock,unsingned long flags);
Void spin_unlock_irq(spinlock_t *lock);
Void spin_unlock_bh(spinlock_t *lock);
4.非阻塞的自旋锁
Int spin_trylock(spinlock_t *lock);
Int spin_trylock_bh(spinlock_t *lock);
成功返回0.否则返回负.
1.初始化5、信号量和互斥体
静态
Rwlock_t my_rwlock = RW_LOCK_UNLOCKED
动态
Rwlock_t my_rwlock;
Rwlock_init (&my_rwlock);
2.加锁
Read/write_lock();
Read/wrte_lock_irq();
Read/write_lock_irqsave();
Read/write_lock_bh();
3.解锁
Read/write_unlock();
Read/write_unlock_irq();
Read/write_unlock_irqsave();
Read/write_unlock_bh();
4.其他 write_trylock();
Rw_is_locked();
注意:读/写锁机制要更照顾读者.当读者持有时,写者必须等待.如果有大量的读者,必定会引起写者处于饥饿状态.所以如果加锁时间不长,并且代码不会睡眠,可以利用自旋锁.如果加锁时间很长,或者代码在持有锁时可能睡眠,则最好使用信号量来枷锁.
信号量的特征:6、信号量的实现
1.信号量适用于锁会被长期持有时,因为开销比较大.
2.只有在进程的上下文才能获得信号量,因为获取信号量时可能导致睡眠,不适用中 断上下文.
3.可以在持有信号量时去睡眠,因此可以在持有信号量的时候和用户空间同步.
4.不能在持有信号量的同时持有自旋锁.
5.信号量可以允许任意数量的持有者,而自旋锁在一个时刻只能有一个持有者.
6.只拥有一个持有者的信号量称为互斥信号量
1.申明7、Array.completion 机制
静态
Static DECLARE_SEMAPHORE_GENERIC(name,count);
或申明一个互斥信号量
Static DECLARE_MUTEX(name);
动态
Struct semaphore sem;
Sema_init(sem.count);
Int_MUTEX(sem);
Int_MUTEX_LOCKED(sem);
2.获取信号量
Void down(struct semaphore *sem)
减小信号量的值.返回获得的信号量
Int down_interruptible(struct semaphore *sem)
在等待信号量时,可以被用户空间程序通过信号中断.如果操作被中断,则返回非零 值,调用者无法获得信号量.注意:调用时始终要检查返回值.
Int down_trylock(struct semaphore *sem);
不会休眠,如果不能获得信号量,立即返回一个非零值.
3.释放信号量
Viod up(struct semaphore *sem);
1.创建8、如何禁内核抢占.
静态
DECLARE_COMPLETION(my_completion);
动态
Struct completion my_completion;
Init_completion(&my_completion);
2.等待completion
Void wait_for_completion(struct completion *c);
执行一个非中断的等待,用户空间无法通过signal来中断这个进程.
3.完成completion
Void completion(struct completiong *)
发信号唤醒任何等待任务.
1.preempt_disable()
增加抢占记数值,从而禁止内核抢占.记数为0时,可以抢占.
2.preempt_enable()
减少抢占记数,并当该值降为0时检查和执行被挂起的需调度的任务.
3.preempt_enable_no_resched();
激活内核抢占,但不检查被挂起的需调度的任务
4.preempt_count()
返回抢占数.