10内核同步方法

linux内核提供了一套相当完备的内核同步方法。我们将介绍它们的接口、行为及用途。

一、原子操作

  原子操作可以保证指令以原子的方式执行——执行过程中不被打断。

  内核提供了两种原子操作接口:一组针对整数进行操作;一组针对单独的位进行操作。

  1.1 原子整数操作

  针对整数的原子操作只能对atomic_t类型的数据进行处理。atomic_t类型定义在<linux/types.h>中:

1 typedef struct {
2    volatile int counter;   
3 } atomic_t;

   使用原子操作需要的声明都在<asm/atomic.h>中,

  定义一个atomic_t类型的数据结构方法如下所示:

atomic_t v;  //定义v
atomic_t u = ATOMIC_INIT(0); //定义u并将其初始化为0

   原子操作如下:

atomic_set(&v,4); //v = 4 原子的
atomic_add(2,&v); //v = v + 2 = 6 原子的
atomic_inc(&v);  / / v = v + 1= 7; 原子的
printk("%d
", aotmic_read(&v)); //打印7 将atomic_t 转换为int
int atomic_dec_and_test(atomic_t *v) //用原子整数操作原子的执行一个操作并检查结果

   1.2 顺序性和原子性的比较

  原子性确保指令执行期间不被打断,要么全部执行完,要么根本不执行。顺序性确保两条或多条指令出现在独立的执行线程中,甚至独立的处理器上,它们本该的执行顺序却依然要保持。

  顺序性通过屏障(barrier)指令来实施。

  在编写代码时,能使用原子操作,就尽量不要使用复杂的加锁机制。

  1.3 64位原子操作

typedef struct{
  volatile long counter;   
}atomic64_t;

  1.4 原子位操作

  定义在<asm/bitops.h>中,位操作函数是对普通的内存地址进行操作的。它的参数是一个指针和一个位号。


二、自旋锁

  情况:先得从一个数据结构中移出数据,然后对其进行格式转换和数据解析,最后再将其加入另一个数据结构中,整个执行过程必须是原子的,在数据被更新完毕之前不允许被其他线程访问。

  自旋锁最多只能被一个可执行线程持有;如果一个执行线程试图获得一个被已经持有的自旋锁,那么该线程就会被一直进行忙循环——旋转——等待锁重新使用。要是锁未被争用,请求锁的执行线程便可以立即获得它,继续执行。

  一个被争用的自旋锁使得请求它的线程在等待锁重新可用时自旋(特别浪费处理时间),且持有自旋锁的时间最好小于完成两次上下文切换的消耗。

  1.4 自旋锁方法

  接口定义于<linux/spinlock.h>中,自旋锁的基本使用形式如下:

DEFINE_SPINLOCK(mr_lock);
spin_lock(&mr_lock);
/*临界区*/
spin_unlock(&mr_lock);

   一个时刻只有一个线程位于临界区内,linux内核实现的自旋锁是不可递归的,所以如果你试图获取一个你正持有的锁,你必须自旋,等待你自己释放这个锁;

  内核提供的进制中断同时请求锁的接口,如下所示:

  

  

原文地址:https://www.cnblogs.com/ccpang/p/11757051.html