读书笔记Linux内核设计与实现part 2

Chapter 9~10 内核同步》

临界区(CS):访问共享资源的代码段。

造成并发的原因:

  1. 中断
  2. 软中断和tasklet
  3. 内核抢占
  4. 睡眠及用户空间的同步
  5. 对称多处理器

 

锁的使用本身并不是难点,真正的挑战在于辨认出需要共享的数据和相应的临界区。

在最开始设计代码的时候就要想到要使用锁,而不是在完成代码后再去加锁。

interrupt-safe / SMP-safe / preempt-safe

预防死锁的一个比较方便的方法(不能彻底解决):按顺序枷锁。

 

死锁的四个条件:

互斥(mutual exclusion),

请求与保持(hold and wait),

非剥夺(non-preempt),

循环等待(circular wait)。

 

Linux锁粒度越来越细,可扩展性也很好。

 

原子变量,原子位操作。

 

自旋锁:

自旋锁可以使用在中断处理函数中(这里不能使用信号量,因为信号量会导致睡眠)。

申请自旋锁前,先关本地中断(有接口实现关中断和申请自旋锁)。

针对数据结构加锁。

spin_try_lock()试图去获得某个自旋锁,如果该锁已被争用,立刻返回非零值,不会等待锁被释放。

读写自旋锁:共享读,互斥写。

   错误使用读写自旋锁,导致死锁:

        read_lock(&mr_rwlock);

        write_lock(&mr_rwlock);//等待所有读者退出(包括自己这个读者)

 

信号量:

一种睡眠锁。1986年由Dijkstra提出,接口P(),V()(来自荷兰语Proberen和Vershogen,分别是探查和增加的意思,Linux中称为down(),up())。

读写信号量。

 

互斥体:

可以睡眠的强制互斥锁。

 

完成变量:

一个任务完成后需要通知另一个任务,使用完成变量(completion variable)。

三个API:

init_completion(struct completion *);

wait_for_completion(struct completion *);

complete(struct completion *);

 

BKL大内核锁:
   Linux支持单CPU到支持SMP过渡期的锁。

 

顺序锁:

   u64 get_jiffies_64(){
       unsigned long seq;
       u64 ret;
       do{
          seq = read_seqbegin(&xtime_lock);
          ret = jiffies64;
       }while(read_seqretry(&xtime_lock,seq));
       return ret;
  }
//写jiffies write_seqlock(&xtime_lock); jiffies_64 += 1; write_sequnlock(&
xtime_lock);

 

顺序和屏障:

rmb()保证其后的读内存操作在其前的读内存操作之后执行(现代处理器中为优化流水线可能会有乱序执行的情况)。

wmb()与之类似,mb()是rmb(),wmb()两种屏障。

例如:a,b原来初始值为1,2;

线程1                     线程2                    
④a=3; ---
mb(); ---
①  b=4; ②  c=b;
--- rmb();
--- ③  d=a;

如果没有mb(),rmb(),则可能发生c=4,d=1的情况(执行序列如图中序号,我们并不希望是这样的)。加上mb(),rmb()之后就避免了这种情况。

 

barrier()阻止编译器跨屏障对载入或存储操作优化。

 

Chapter 11 定时器和时间管理》

 

#define time_before(unkown,know)  ((long)(known)-(long)(unknow)<0)

x86 PIT(可编程中断时钟)

 

定时器:由这个结构体实现。

struct timer_list {

struct list_head entry;         //定时器链表的入口

unsigned long expires;          //以jiffies为单位的时值

void (*function)(unsigned long) //定时器处理函数

unsigned long data;             //定时器处理函数的参数

struct tvec_t_base_s *base; //定时器内部值

}

 

延迟执行:

 

1、忙等:

忙等:
while( time_before(jiffies, timeout) );

忙等变体:
while( time_before(jiffies, timeout) )
    cond_resched();

2、内核接口:

void udelay(…);

void ndelay(…);

void mdelay(…);

 

3、schedule_timeout();

 

原文地址:https://www.cnblogs.com/apprentice89/p/2748846.html