Chinaunix首页 | 论坛 | 博客
  • 博客访问: 66828
  • 博文数量: 21
  • 博客积分: 290
  • 博客等级: 二等列兵
  • 技术积分: 286
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-30 16:16
文章分类
文章存档

2019年(3)

2017年(1)

2012年(17)

我的朋友

分类: LINUX

2012-04-07 21:05:38

     前面说到内核并发和竞争导致了共享数据容易被破坏,甚者造成程序功能不可
 
预知的效果。因此在我们设计驱动时在任何时候应该避免共享资源;应尽量避免使
 
用全局变量。再者在任何时候共享资源被两个或者两个以上的线程,并且可能存在
 
一个线程看到那个资源的不一致时, 你必须明确地管理对那个资源的存取.所以内核
 
提供了对保护共享数据的机制==>同步操作。
 
    1)原子操作
 
    2)自旋锁
 
    3)信号量
 
    1、原子操作
 
    何谓原子操作:是不可分割的,在执行完毕不会被任何其它任务或事件中断。
 
同时它需要硬件支持,而且与架构相关,不能纯粹由软件来实现。
 
    原子操作主要运用实现计数器,简单的计数要采用锁来同步,对系统的开销相
 
对比较大的啊。
 
   原子操作方法:
  
  

点击(此处)折叠或打开

  1. 1、atomic_t 数据项必须通过这些函数存取. 如果你传递一个原子项给一
  2.    个期望一个整数参数的函数, 你会得到一个编译错误.
  3. 2、你还应当记住, atomic_t 值只在当被置疑的量真正是原子的时候才起作用.
  4.    需要多个 atomic_t 变量的操作仍然需要某种其他种类的加锁.
  5. 3、for example:
  6.    atomic_sub(amount, &first_atomic);
  7.    atomic_add(amount, &second_atomic);
  8.    从第一个原子值中减去 amount, 但是还没有加到第二个时, 存在一段时间. 如果事情的
  9.    这个状态可能产生麻烦给可能在这 2 个操作之间运行的代码, 某种加锁必须采用.

  10. 头文件:
  11. #include <asm/atomic.h>
  12. /*
  13.  *设置原子变量 v 为整数值 i.
  14.  *你也可在编译时使用宏定义 ATOMIC_INIT 初始化原子值.
  15.  */
  16.  ATOMIC_INIT(int i)
  17. void atomic_set(atomic_t *v)

  18. /*
  19.  *读取变量 v 的值.
  20.  */
  21. int atomic_read(atomic_t *v)

  22. /*
  23.  *由 v 指向的原子变量加 /减 i. 返回值是 void, 因为有一个额外的开销来返回新值,
  24.  *并且大部分时间不需要知道它.
  25.  */
  26. void atomic_add(int i, atomic_t *v)
  27. void atomic_sub(int i, atomic_t *v)

  28. /*
  29.  *由 v 指向的原子变量加 /减 1
  30.  */
  31. void atomic_inc(atomic_t *v)
  32. void atomic_dec(atomic_t *v)

  33. /*
  34.  *它们返回原子变量的新值给调用者
  35.  */
  36. int atomic_add_return(int i, atomic_t *v)
  37. int atomic_sub_return(int i, atomic_t *v)
  38. int atomic_inc_return(atomic_t *v)
  39. int atomic_dec_return(atomic_t *v)
  40. /*
  41.  *进行一个特定的操作并且测试结果; 如果, 在操作后, 原子值是 0, 那么返回值是
  42.  *; 否则, 它是假. 注意没有 atomic_add_and_test.
  43.  */
  44. int atomic_inc_and_test(atomic_t *v)    
  45. int atomic_dec_and_test(atomic_t *v)    
  46. int atomic_sub_and_test(int i, atomic_t *v)

  47. /*
  48.  *加整数变量 i 到 v. 如果结果是负值返回值是真, 否则为假.
  49.  */
  50. int atomic_add_negative(int i, atomic_t *v)

  51. 【位原子操作】
  52. /*
  53.  *设置第 nr 位在 addr 指向的数据项中
  54.  */
  55. void set_bit(nr, void *addr);

  56. /*
  57.  *清除指定位在 addr 处的无符号长型数据. 它的语义与 set_bit 的相反.
  58.  */
  59. void clear_bit(nr, void *addr);

  60. /*
  61.  *翻转这个位.
  62.  */
  63. void change_bit(nr, void *addr);

  64. /*
  65.  *这个函数是唯一一个不需要是原子的位操作; 它简单地返回这个位的当前值.
  66.  */
  67. test_bit(nr, void *addr);

  68. /*
  69.  *原子地动作如同前面列出的, 除了它们还返回这个位以前的值
  70.  */
  71. int test_and_set_bit(nr, void *addr);
  72. int test_and_clear_bit(nr, void *addr);
  73. int test_and_change_bit(nr, void *addr);

    2、自旋锁
 
    何谓自旋锁:它是为为实现保护共享资源而提出一种锁机制。一个执行单元要
 
想访问被自旋锁保护的共享资源,必须先得到锁,在访问完共享资源后,必须释放
 
锁。如果在获取自旋锁时,没有任何执行单元保持该锁,那么将立即得到锁;如果
 
在获取自旋锁时锁已经有保持者,那么获取锁操作将自旋在那里,直到该自旋锁的
 
保持者释放了锁。
 
    [NOTE]自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。
 
无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任
 
何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互
 
斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起
 
调用者睡眠。
 
    [初衷]在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程
 
在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持
 
有时间过长。如果需要长时间锁定的话, 最好使用信号量。
 
    自旋锁操作方法:

点击(此处)折叠或打开

  1. 【使用方法】
  2. 1、初始化
  3. 2、加锁
  4. 3、解锁

  5. spinlock_t my_lock = SPIN_LOCK_UNLOCKED;
  6. void spin_lock_init(spinlock_t *lock);

  7. void spin_lock(spinlock_t *lock);
  8. void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);
  9. void spin_lock_irq(spinlock_t *lock);
  10. void spin_lock_bh(spinlock_t *lock);

  11. void spin_unlock(spinlock_t *lock);
  12. void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);
  13. void spin_unlock_irq(spinlock_t *lock);
  14. void spin_unlock_bh(spinlock_t *lock);

    3、信号量
   
    信号量是一种睡眠锁,若你试图想要获取一个已被争用的信号量时,就会被推
 
到一个等待队列中,睡眠直到重新被唤醒。
   
    由于信号量是一种睡眠锁,它就有以下几点特征:
 
    (1)它运用于进程上下文中,因为原子上下文和中断上下文均不可睡眠,重新被
 
调度。
 
    (2)使用信号量的时候,不能使用自旋锁,因为持有自旋锁的进程必须不可睡眠
 
而且持锁时间要短。
 
    (3)信号量可以允许多个进程拥有锁持有者,这些进程只是会睡眠而不会造成死
 
锁;而自旋锁在任何时刻只能允许被一个任务持有。
    
    (4)自旋锁一般在同一进程或者同一线程成对出现,而非由跨越进程或线程;
 
而信号量则相反。
 
     信号量使用方法:

点击(此处)折叠或打开

  1. 【使用方法】
  2. 1、定义和初始化
  3. 2、睡眠
  4. 3、唤醒

  5. 头文件:
  6. #include <asm/semaphore.h>

  7. void sema_init(struct semaphore *sem, int val)
  8. /*
  9.  *互斥锁
  10.  */
  11. void init_MUTEX(struct semaphore *sem);
  12. void init_MUTEX_LOCKED(struct semaphore *sem);

  13. /*
  14.  *down 递减旗标值并且等待需要的时间
  15.  */
  16. void down(struct semaphore *sem);

  17. /*
  18.  *down_interruptible 递减旗标值并且等待需要的时间;但可中断的
  19.  *如果操作是可中断的, 函数返回一个非零值, 并且调用者不持有旗标.
  20.  *正确的使用 down_interruptible 需要一直检查返回值并且针对性地响应.
  21.  */-ERESTARTSYS】
  22. int down_interruptible(struct semaphore *sem);

  23. /*
  24.  *( down_trylock ) 从不睡眠;
  25.  *如果旗标在调用时不可用, down_trylock 立刻返回一个非零值.
  26.  */
  27. int down_trylock(struct semaphore *sem)

  28. /*
  29.  *一旦 up 被调用, 调用者就不再拥有旗标.
  30.  */
  31. void up(struct semaphore *sem);


  32. 【读写信号量】
  33. 头文件:
  34. #include <linux/rwsem.h>

  35. void init_rwsem(struct rw_semaphore *sem);

  36. void down_read(struct rw_semaphore *sem);
  37. int down_read_trylock(struct rw_semaphore *sem);
  38. void up_read(struct rw_semaphore *sem);

  39. void down_write(struct rw_semaphore *sem);
  40. int down_write_trylock(struct rw_semaphore *sem);
  41. void up_write(struct rw_semaphore *sem);
  42. void downgrade_write(struct rw_semaphore *sem);
     
    4、完成变量
 
    完成变量是协调两个任务之间关系的简单操作,比如说任务A发出通知,告诉任
 
务B我已经完成操作,你可以继续往下做。正常情况下,完成变量大多数运用于单一
 
设备,使用一次就放弃的。
 
   

点击(此处)折叠或打开

  1. 头文件:
  2. #include <linux/completion.h>

  3. struct completion
  4. {
  5.     unsigned int done;
  6.     wait_queue_head_t wait;
  7. };

  8. INIT_COMPLETION(struct completion c);
  9. void init_completion(struct completion *c);

  10. void wait_for_completion(struct completion *c);

  11. void complete(struct completion *c);
  12. void complete_all(struct completion *c);

  13. void complete_and_exit(struct completion *c, long retval);


    
阅读(1091) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~