Linux实时补丁的思想和方式

CONFIG_PREEMPT_RT的引入意味着linux内核拥有了实时性,虽然还远远达不到硬实时,可是基本的实时性还是挺不错的,这又一次说明了linux内核的灵活性,想加入什么新特性很简单而且很直接。
这个实时性补丁主要考虑了几个方面,一个是将自旋锁进行了改进,一个是将中断线程化了,还有一个就是又引入了几个内核线程专门处理一些不得不延后的操作。频繁的中断很影响实时性,因为它们是不确定的,对于中断的发生时刻是未知的,另外中断发生时无论cpu正在处理什么任务都要停下来来处理中断,即使一个无关紧要的中断也比最高优先级的进程的优先级别高,任何任务都无条件向中断让路,这大大影响了实时性,实时任务被中断压得不得不一会一停,这只是其一,还有一个影响实时性的因素就是内核的自旋锁,自旋锁在某种意义上就是抢占禁用,这样的话即使再高优先级的进程也无法抢占占有自旋锁的进程,在以前的rcu锁中,为了一个rcu读端加锁,整个系统的抢占被禁用,这些都影响了实时性。于是实时补丁就着手改变这个现状,具体就是:第一,将传统的自旋锁用mutex实现,这样的话,自旋锁就可以睡眠了,但是中断中不允许睡眠,而中断中运用了大量的自旋锁,因此引出了第二点,就是中断线程化,每个中断向量都有一个专门的线程负责处理,这些中断处理线程拥有全局的优先级,受到调度器的全局调度,因此,它们不一定能竞争得过优先级很高的实时进程,所有这一切还没有完,因为自旋锁可以睡眠了,那就意味着抢占禁用期间不能再利用自旋锁了,当然可以用传统的自旋锁,这就引出了第三点,也就是说专门单列出来一个机制,用来将抢占禁用期间的不得不用自旋锁的操作进行适当的延迟,这个也是一个作重要的机制,实际上有两种方式,一个就是使用原来的RCU机制,因为第一代rcu的一个“期限”到期就是所有的读端都释放了rcu读锁,而释放了rcu读锁意味着打开了抢占,开着抢占就可以睡眠了,也就是说可以用实时的mutex实现的自旋锁了,注意,第二代的可睡眠的rcu是不能用于这里的,因为在rcu读锁占有期间,抢占并不禁用,而释放了锁,也不会影响抢占位,因此第二代的可睡眠rcu和抢占是无关的,因此不能用,在抢占禁用期间调用延时的利用自旋锁函数的第二个机制就是单独开一些内核线程来负责这些工作,
内核线程拥有进程上下文,可以睡眠,因而可以随意调用可睡眠的自旋锁函数,从这里可以再次看出进程在linux中的意义,进程太伟大了,如果有什么事情,那么就让进程来做吧,这样限制会最少。

原来的自旋锁利用了禁用抢占这个内核特性活泼了很久很久,一直都不错,但是实际上,禁用抢占和锁其实是两个概念,非实时内核的自旋锁实现中由于自旋的要求 将抢占和锁耦合在一起,让它们一起实现一个功能,比如对于up上的抢占内核,自旋锁就是禁用抢占而已,对于up非抢占内核,自旋锁什么也不做,对于smp 上的抢占内核,不但要禁用抢占还要加锁,...如此一看,看似很奇妙,实际上很糟糕,因此才有了那么多的情况下的那么多的机制,一般初学者会发晕的,不统 一的操作杂糅在一起形成了拙劣的设计,不过linux就是这样,这也是linux的优点。现在的实时补丁下的内核看看情况会是怎样,在实时补丁下,自旋锁 已经不再自旋了,回归成了一个真正的锁,以往的spin_lock_irqsave要disable preemption,但是现在不用了,也就是说,现在的自旋锁不靠禁用抢占来保证锁不被抢走(其实是cpu不被抢走,也就是自旋锁保证进程不离开当前的 执行环境),而是用了一把真正的锁。这时,即使一个进程抢占了这个持有锁的进程,那么在它想获得这把锁的时候还是会被阻塞进而睡眠等锁的。我们来看一下这个补丁的部分代码:
static int desched_thread(void * __bind_cpu)  //这就是这个专门的回收线程,其实我觉得可以用工作队列来实现,没有必要单独开这么一个线程。
{
         set_user_nice(current, -10);
         current->flags |= PF_NOFREEZE | PF_SOFTIRQ;
         set_current_state(TASK_INTERRUPTIBLE);
         while (!kthread_should_stop()) {
                 if (mmdrop_complete())  //回收mm_struct,将从链表中取出延时请求
             continue;
         if (put_task_struct_complete()) //回收task_struct
                         continue;
                 schedule();
...
                 set_current_state(TASK_INTERRUPTIBLE);
         }
         __set_current_state(TASK_RUNNING);
         return 0;
}
void __put_task_struct_inline(struct task_struct *tsk)  //在desched_thread的上下文中进行这个实际的操作,因为操作当中要用到自旋锁,而自旋锁可能要睡眠,因此要在独立进程上下文中调用这个函数。
{
...
     if (!profile_handoff_task(tsk))
         free_task(tsk);
}
void fastcall __put_task_struct(struct task_struct *task)
{
    struct list_head *head;
    head = &get_cpu_var(delayed_put_task_struct_list);
    list_add_tail(&task->delayed_drop, head);  //仅仅将任务加入到一个链表当中而不进行实际的操作。
    _wake_cpu_desched_task();   //唤醒上面的那个desched_thread线程
    put_cpu_var(delayed_put_task_struct_list);
}
除了task_struct的释放,mm_struct也用到了相同的机制,这里就不列出代码了。
以上就是延时的调用的内核代码,关于中断线程化的代码我在前面的文章分析过了,这里就不赘述了。可见,实时性补丁并不是想的那么复杂,最重要的就是自旋锁可睡眠,正是因为这一点,才引出了中断线程化和单独的回收线程等机制,实时性补丁的打入有一个意义就是操作的主体几乎都有了独立进程上下文,这样的话管理更加统一,也更加容易在它们中间按照其实时性需求分配等级,这样统一于进程的做法使得代码量最低,因为没有了那么多的所谓的额外的情况要考虑,比如以前要单独对待中断路径,中断又不能这又不能那的很麻烦,现在都是进程,而在linux中进程受到保护,因此统一的操作更加合理和安全,没有了有个性的需要特殊照顾的操作主体。像自旋锁可睡眠之类的变化,看起来很恐怖的,这给了内核多大的限制啊,很多不能睡眠的地方都不能用自旋锁了,可是真的有那么严重吗?又有几个不能睡眠的地方呢?中断都线程化了还有哪些地方不能睡眠呢?如果找到了不能睡眠的地方,比如禁用了抢占的情形,那么很简单,把任务交给一个拥有独立上下文的进程(内核线程)不就得了吗?其实我倒是觉得可以交给工作队列,至于什么时候完成你交给的工作,那你就管不着了,那就是单独的线程的事情了。这个机制在某种意义上实现了一定的动态性管理,模块分的更加细,各个模块进一步解除耦合,就连回收一个结构体也要单独一个模块。这其实在设计上是很不错的,比如你只管用一个结构体,等到释放它的时候,你就不用操更多的心了,只要将“要释放”它的信息注册给一个回收线程,那么回收的事情就是那个线程的了,应用者根本不用管同步问题和死锁问题了。不知道这可不可以称为内核的动态垃圾回收机制。
在shedule中是显然要禁用抢占的,但是在其中切换完进程后调用的finish_task_switch中却可能要释放已经死亡进程的task_struct,而释放过程要用到自旋锁,自旋锁在实时内核中可以睡眠了,但是此时由于禁用了抢占约定中不允许睡眠的发生,因此好像是不能在此调用put_task_struct了,但是因为有了专门的回收内核线程,一切变得可能!

实时补丁的可睡眠的自旋锁可睡眠的代价就是睡眠唤醒的开销增加了,想象一下,自旋锁之所以被发明出来就是因为它空转cpu,不切换,不睡眠,这样的好处就 是省了睡眠/唤醒的开销,缺点就是cpu利用率不高,毕竟cpu空转嘛。实时性为了保障公平,不得不引入统一的进程架构,将一切统一于进程,这样便于统一 调度,当然公平性是达到了,用mutex实现的自旋锁并没有自旋,因此它的开销将会比自旋锁的开销大,但是鱼与熊掌不可兼得,毕竟是要付出的啊 。
总结一句:哪里限制最少,哪个机制限制最少,我们就想办法将一切向那个机制统一。

原文链接: https://blog.csdn.net/dog250/article/details/5303277

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    Linux实时补丁的思想和方式

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/410437

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月26日 下午12:01
下一篇 2023年4月26日 下午12:01

相关推荐