日期:2014-05-16  浏览次数:20692 次

linux内核笔记-内核同步

    linux内核就相当于不断对请求进行响应的服务器,这些请求可能来自CPU,可能来自发出中断的外部设备。我们将内核看作两种请求的侍者。

  (1)老板提出请求,侍者如果空闲,为老板服务。(系统调用或异常)

  (2)侍者正在无顾客服务时,老板提出请求,则转去为老板服务。(中断异常嵌套)

  (3)侍者正为老板服务时,另一老板提出请求,此时转去为另一个老板服务,回头再给这个老板服务。(中断嵌套)

  (4)老板可命令老板停止为当前客户服务,侍者完成老板请求之后,可能为新选中的顾客服务。(内核抢占)

内核抢占特点:一个内核态运行的进程,可以被执行内核函数期间被另一个进程取代。有关内核抢占参照本文:内核抢占


同步概念

      临界区是一段代码,进入临界区的内核控制路径必须全部执行这段代码,其他控制路径才能进入临界区。单核cpu可以通过简单的关闭中断、禁止内核抢占来进行保证临界区的互斥访问,多cpu就不行 了。

      下列条件不需要同步:

    (1)执行中断的内核路径不可能被可延迟或系统调用服务例程内核路径所中断,所以不需要同步;

    (2)软中断和tasklet不能再同一个CPU上交替执行,所以仅这两者存在时,不需要同步;

    (3)仅被一种tasklet访问的数据结构不需要同步。


同步原语

1.每CPU变量

     最重要的同步技术就是把内核变量声明为每CPU变量,每CPU变量主要是数据结构的数组,系统每个CPU对应数组的一个元素。一个CPU不应该访问与其他CPU对应的数组元素。每个CPU可以随意修改自己的元素不用担心出现竞争条件。每CPU变量对来自异步函数(中断和可延迟函数)的访问不提供保护,在这种情况下需要提供另外的同步原语。内核抢占可导致每CPU变量产生竞争条件

2.原子操作

     这样的操作是芯片原子级的,执行这样的指令,中间不能中断,且避免其他的CPU访问同样的内存单元。这种原子操作,通过“锁定”内存总线实现,直到这条指令执行结束为止,其他处理器不能访问该内存单元。原子操作:

      atom_read(v),atom_set,atom_add,atom_sub.....

3.优化和内存屏障

     当使用编译器的时候,编译器可能重新安排汇编语言指令以使寄存器以最优的方式执行。当处理同步时,必须避免指令重新排序。可通过设置内存屏障,确保原语之后的操作语句执行之前,原语操作之前的语句已经完成,这样保证汇编语句顺序执行。

4.自旋锁

     自旋锁是用来多处理环境中的一种特殊锁。如果内核路径发现锁是开着的,则可获取锁进入临界区。相反如果内核路径发现锁由另一个CPU上的控制路径所持有,则在周围“旋转”(其实,占着cpu死等,等到为止)。

     自旋锁的循环指令表示“忙等”(这种忙等,是nop操作,经过linux优化了,说是节省能源)。注意,在忙等期间,内核抢占有效,就是等待的进程可能被其他进程替代。一般来说,由自旋锁所保护的每个临界区都是禁止内核抢占的。

spinlock_t含有的两个字段:

      slock;自旋锁的状态,1表示未加锁

      break_lock:表示有无等待该锁的进程

提供的操作:

      spin_lock_init();//初始化一把锁

      spin_lock();//循环试图获得自旋锁

      spin_unlock();//释放锁

      spin_unlock_wait();//等待锁

      spin_is_locked();//判断锁释是否被持有


5.读写自旋锁

      读写锁是为了增加内核的并发能力。该锁的特点:读者进程可以同时多个进入临界区;写者只能一个进入临界区(有写者的时候,读者无法进入)。读者和写者具有相同的优先权