前面说到内核并发和竞争导致了共享数据容易被破坏,甚者造成程序功能不可
预知的效果。因此在我们设计驱动时在任何时候应该避免共享资源;应尽量避免使
用全局变量。再者在任何时候共享资源被两个或者两个以上的线程,并且可能存在
一个线程看到那个资源的不一致时, 你必须明确地管理对那个资源的存取.所以内核
提供了对保护共享数据的机制==>同步操作。
1)原子操作
2)自旋锁
3)信号量
1、原子操作
何谓原子操作:是不可分割的,在执行完毕不会被任何其它任务或事件中断。
同时它需要硬件支持,而且与架构相关,不能纯粹由软件来实现。
原子操作主要运用实现计数器,简单的计数要采用锁来同步,对系统的开销相
对比较大的啊。
原子操作方法:
- 1、atomic_t 数据项必须通过这些函数存取. 如果你传递一个原子项给一
- 个期望一个整数参数的函数, 你会得到一个编译错误.
- 2、你还应当记住, atomic_t 值只在当被置疑的量真正是原子的时候才起作用.
- 需要多个 atomic_t 变量的操作仍然需要某种其他种类的加锁.
- 3、for example:
- atomic_sub(amount, &first_atomic);
- atomic_add(amount, &second_atomic);
- 从第一个原子值中减去 amount, 但是还没有加到第二个时, 存在一段时间. 如果事情的
- 这个状态可能产生麻烦给可能在这 2 个操作之间运行的代码, 某种加锁必须采用.
- 头文件:
- #include <asm/atomic.h>
- /*
- *设置原子变量 v 为整数值 i.
- *你也可在编译时使用宏定义 ATOMIC_INIT 初始化原子值.
- */
- ATOMIC_INIT(int i)
- void atomic_set(atomic_t *v)
- /*
- *读取变量 v 的值.
- */
- int atomic_read(atomic_t *v)
- /*
- *由 v 指向的原子变量加 /减 i. 返回值是 void, 因为有一个额外的开销来返回新值,
- *并且大部分时间不需要知道它.
- */
- void atomic_add(int i, atomic_t *v)
- void atomic_sub(int i, atomic_t *v)
- /*
- *由 v 指向的原子变量加 /减 1
- */
- void atomic_inc(atomic_t *v)
- void atomic_dec(atomic_t *v)
- /*
- *它们返回原子变量的新值给调用者
- */
- 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)
- /*
- *进行一个特定的操作并且测试结果; 如果, 在操作后, 原子值是 0, 那么返回值是
- *真; 否则, 它是假. 注意没有 atomic_add_and_test.
- */
- 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)
- /*
- *加整数变量 i 到 v. 如果结果是负值返回值是真, 否则为假.
- */
- int atomic_add_negative(int i, atomic_t *v)
- 【位原子操作】
- /*
- *设置第 nr 位在 addr 指向的数据项中
- */
- void set_bit(nr, void *addr);
- /*
- *清除指定位在 addr 处的无符号长型数据. 它的语义与 set_bit 的相反.
- */
- void clear_bit(nr, void *addr);
- /*
- *翻转这个位.
- */
- void change_bit(nr, void *addr);
- /*
- *这个函数是唯一一个不需要是原子的位操作; 它简单地返回这个位的当前值.
- */
- test_bit(nr, void *addr);
- /*
- *原子地动作如同前面列出的, 除了它们还返回这个位以前的值
- */
- int test_and_set_bit(nr, void *addr);
- int test_and_clear_bit(nr, void *addr);
- int test_and_change_bit(nr, void *addr);
2、自旋锁
何谓自旋锁:它是为为实现保护共享资源而提出一种锁机制。一个执行单元要
想访问被自旋锁保护的共享资源,必须先得到锁,在访问完共享资源后,必须释放
锁。如果在获取自旋锁时,没有任何执行单元保持该锁,那么将立即得到锁;如果
在获取自旋锁时锁已经有保持者,那么获取锁操作将自旋在那里,直到该自旋锁的
保持者释放了锁。
[NOTE]自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。
无论是
互斥锁,还是
自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任
何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互
斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起
调用者睡眠。
[初衷]在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程
在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持
有时间过长。如果需要长时间锁定的话, 最好使用信号量。
自旋锁操作方法:
- 【使用方法】
- 1、初始化
- 2、加锁
- 3、解锁
- spinlock_t my_lock = SPIN_LOCK_UNLOCKED;
- void spin_lock_init(spinlock_t *lock);
- 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);
- void spin_unlock(spinlock_t *lock);
- void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);
- void spin_unlock_irq(spinlock_t *lock);
- void spin_unlock_bh(spinlock_t *lock);
3、信号量
信号量是一种睡眠锁,若你试图想要获取一个已被争用的信号量时,就会被推
到一个等待队列中,睡眠直到重新被唤醒。
由于信号量是一种睡眠锁,它就有以下几点特征:
(1)它运用于进程上下文中,因为原子上下文和中断上下文均不可睡眠,重新被
调度。
(2)使用信号量的时候,不能使用自旋锁,因为持有自旋锁的进程必须不可睡眠
而且持锁时间要短。
(3)信号量可以允许多个进程拥有锁持有者,这些进程只是会睡眠而不会造成死
锁;而自旋锁在任何时刻只能允许被一个任务持有。
(4)自旋锁一般在同一进程或者同一线程成对出现,而非由跨越进程或线程;
而信号量则相反。
信号量使用方法:
- 【使用方法】
- 1、定义和初始化
- 2、睡眠
- 3、唤醒
- 头文件:
- #include <asm/semaphore.h>
- void sema_init(struct semaphore *sem, int val)
- /*
- *互斥锁
- */
- void init_MUTEX(struct semaphore *sem);
- void init_MUTEX_LOCKED(struct semaphore *sem);
- /*
- *down 递减旗标值并且等待需要的时间
- */
- void down(struct semaphore *sem);
- /*
- *down_interruptible 递减旗标值并且等待需要的时间;但可中断的
- *如果操作是可中断的, 函数返回一个非零值, 并且调用者不持有旗标.
- *正确的使用 down_interruptible 需要一直检查返回值并且针对性地响应.
- */【-ERESTARTSYS】
- int down_interruptible(struct semaphore *sem);
- /*
- *( down_trylock ) 从不睡眠;
- *如果旗标在调用时不可用, down_trylock 立刻返回一个非零值.
- */
- int down_trylock(struct semaphore *sem)
- /*
- *一旦 up 被调用, 调用者就不再拥有旗标.
- */
- void up(struct semaphore *sem);
- 【读写信号量】
- 头文件:
- #include <linux/rwsem.h>
- void init_rwsem(struct rw_semaphore *sem);
- void down_read(struct rw_semaphore *sem);
- int down_read_trylock(struct rw_semaphore *sem);
- void up_read(struct rw_semaphore *sem);
- void down_write(struct rw_semaphore *sem);
- int down_write_trylock(struct rw_semaphore *sem);
- void up_write(struct rw_semaphore *sem);
- void downgrade_write(struct rw_semaphore *sem);
4、完成变量
完成变量是协调两个任务之间关系的简单操作,比如说任务A发出通知,告诉任
务B我已经完成操作,你可以继续往下做。正常情况下,完成变量大多数运用于单一
设备,使用一次就放弃的。
- 头文件:
- #include <linux/completion.h>
- struct completion
- {
- unsigned int done;
- wait_queue_head_t wait;
- };
- INIT_COMPLETION(struct completion c);
- void init_completion(struct completion *c);
- void wait_for_completion(struct completion *c);
- void complete(struct completion *c);
- void complete_all(struct completion *c);
- void complete_and_exit(struct completion *c, long retval);
阅读(1091) | 评论(0) | 转发(0) |