分类: LINUX
2012-08-29 21:11:47
1 preemptible critical sections
1.1 用支持PI的rt_mutex代替传统的禁用抢占的spin_lock,定义了新的数据类型:spin_lock_t。凡是使用spin_lock_t做参数的spin_lock函数行为有可能引起调度,不会禁用抢占。如果,希望使用传统的spin_lock,则需要使用raw_spin_lock_t类型作为参数。
1.2 同时,要注意per CPU变量的问题,以前用spin_lock保护的per CPU变量,现在会出现问题,即在现在的spin_lock中进程照样会睡眠,然后调度到别的CPU上。因此,引入了一个新的宏来定义一种可以锁住的per CPU变量。DEFINE_PER_CPU_LOCKED,就是这样的宏,通过get_cpu_var_locked来操作per CPU变量。不过,以前使用get_cpu/put_cpu保护的per CPU变量不受影响。
1.3 还有一种情况,就是在显示的改变的进程状态之后,调用spin_lock,参考下面的代码序列:
spin_lock(&mylock1);
current->state = TASK_UNINTERRUPTIBLE;
spin_lock(&mylock2); // [*]
blah();
spin_unlock(&mylock2);
spin_unlock(&mylock1);
在锁住mylock2之前,进程被设置为TASK_UNINTERRUPTIBLE状态,但是现在spin_lock的行为与mutex一样,所以当获得mylock2的时候,进程有可能被设置为TASK_RUNNING状态,则与前面的逻辑相矛盾。所以,引入了TASK_MUTEX_RUNNING状态。这样,代码会选择将进程状态设置为以前的状态还是TASK_RUNNING。
2 preemptible interrupt handlers
在preempt-rt环境下,中断处理程序是运行在进程上下文中的,这与传统情况大相径庭,所以处理上有很多需要注意的地方。大概的过程是,redirect_hardirq唤醒某个中断处理程序(实际上是一个进程),然后交由do_irqd在进程上下文中执行。
如果,希望某个中断处理程序继续运行在中断上下文中,需要将该中断处理程序设置为SA_NODELAY。在这种情况下,任何于进程上下文的互斥都要使用raw_spin_lock_t来处理。
3 preemptible “interrupt disable” code sequence
在preempt-rt环境下,spin_lock_irqsave并不会禁用中断和抢占,而且local_irq_save也不会禁用中断,只会禁用抢占。必须与SA_NODELAY类型中断打交道的代码不能使用local_irq_save,因为它不会真正禁用中断,此时,必须使用raw_local_irq_save。
4 priority inheritance for in-kernel spinlocks and semaphore
对于preempt-rt中的spinlock来说,实现都是支持PI的rt_mutex,即能够解决优先级翻转问题。优先级翻转是实时系统中比较严重的问题,具体成因是:
进程A,B,C,其中优先级A>B>C。C持有锁S,A需要S,则A睡眠在S上,但是B抢占C,则A只有等到B运行结束,C释放S后才能继续运行。这样就造成了优先级翻转问题。
解决办法是,在A尝试获得S的时候,判断持有S的进程与A的优先级,如果A的优先级更高,则提升C的优先级与A一样高。如果,C还依赖于某一个锁,这样循环下去,则需要循环提升持有锁的进程的优先级。当C释放S时,恢复C的优先级。
一旦,C释放S,则A尝试获得S,这时S在pending状态,这时,更高优先级的进程可以尝试抢占A,否则A将彻底获得S。这种优化通过在spin_lock_t中加入task_struct指针实现,指针的低两位作为锁的状态位。在pending向held状态转化过程中,是更高优先级进程“steal”锁的时机。在这个例子里面,A就是S的top waiter。
针对于读写锁,由于依赖情况比较负责,preempt-rt做了简化,即只容许一个进程在一个时间持有一个读锁。
但是,这里面也有例外,某些用于同步的semaphore,不能执行PI,因为,不知道哪个进程持有这个semaphore,所以无法比较优先级。这种情况需要使用compat_semaphore或者compat_rw_semaphore代替。
5 deferred operations
由于spin_lock可以睡眠,因此在禁用抢占期间代用spin_lock是非法的。在这种情况下preempt-rt提供了一些解决办法:
put_task_struct_delayed排队put_task_struct稍后执行
mmdrop_delayed排队mm_drop稍后执行
TIF_NEED_RESCHED_DELAYED执行重调度,在进程准备返回到用户空间时,或者直到下一个preempt_check_resched_delayed
6 latency-reduction measures
preempt-rt中还有一些改变的主要目的是减少调度或中断延迟。
第一个例子是x86的MMX/SSE硬件。这些硬件在内核中处理时禁用抢占,需要等待前面的指令完成,但是一些指令执行时间很长,preempt-rt做了改变。
第二个例子是使用slab分配器分配per CPU变量。