免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: iterator

关于内核线程的几个问题,请教 [复制链接]

论坛徽章:
0
发表于 2008-07-28 12:57 |显示全部楼层
原帖由 zx_wing 于 2008-7-28 10:00 发表

时间片的概念是有的,因为本身kthread就是用和普通进程一样的接口创建的,除了地址空间在内核态外,没有任何区别。
只是现在看来即使超时它也无法到达调度点。
嗯,我今天把这部分代码看一遍应该找到原因了



OK, 期待你的答案。

不过我还是觉得system kernel thread属于分时调度的话,还是讲不通。

论坛徽章:
0
发表于 2008-07-28 22:47 |显示全部楼层
果然是把问题想叉了,具体来说是我想叉了。
我一开始想成如果kthread不主动让出CPU就会一直执行下去。但实际上一个函数只要不是死循环,总有执行完返回的时候嘛。当kthread返回时,它的生命期就结束了。
当然,在非抢占的内核中,kthread会影响系统的实时性,因为它在执行的时候即使超时也会一直执行下去,这和普通进程进入内核态是一个道理。
不过看了看2.6.25内核kthread实现,还是比较有趣的。
ULK讲到的kthread的创建方式kernel_thread是一种,即直接调do_fork,这应该是老的实现。
目前内核还有一个本身就是kthread的kthreadd_task线程,它在内核初始化的适合被创建,会循环运行一个叫做kthreadd的函数,该函数的作用是运行kthread_create_list全局链表中维护的kthread。
我们可以调用kthread_create创建一个kthread,它会被加入到kthread_create_list链表中,同时kthread_create会weak up kthreadd_task。
kthreadd在执行kthread时也比较绕,它会调用老的接口——kernel_thread运行一个名叫“kthread”的内核线程去运行创建的kthread,被执行过的kthread会从kthread_create_list链表中删除,并且kthreadd会不断调用scheduler让出CPU。kthreadd引入一个新特性,其它内核执行路径是可以调用kthread_stop中止未执行的kthread。

论坛徽章:
0
发表于 2008-07-28 23:26 |显示全部楼层

  1. /*
  2. * in .26
  3. *
  4. * im not sure the following `curr` must be not kthread,
  5. * if scheduled by cfs
  6. */
  7. static void check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr)
  8. {
  9.         unsigned long ideal_runtime, delta_exec;

  10.         ideal_runtime = sched_slice(cfs_rq, curr);
  11.         delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;
  12.         if (delta_exec > ideal_runtime)
  13.                 resched_task(rq_of(cfs_rq)->curr);
  14. }

  15. static void entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued)
  16. {
  17.         /*
  18.          * Update run-time statistics of the 'current'.
  19.          */
  20.         update_curr(cfs_rq);

  21. #ifdef CONFIG_SCHED_HRTICK
  22.         /*
  23.          * queued ticks are scheduled to match the slice, so don't bother
  24.          * validating it and just reschedule.
  25.          */
  26.         if (queued) {
  27.                 resched_task(rq_of(cfs_rq)->curr);
  28.                 return;
  29.         }
  30.         /*
  31.          * don't let the period tick interfere with the hrtick preemption
  32.          */
  33.         if (!sched_feat(DOUBLE_TICK) &&
  34.                 hrtimer_active(&rq_of(cfs_rq)->hrtick_timer))
  35.                 return;
  36. #endif

  37.         if (cfs_rq->nr_running > 1 || !sched_feat(WAKEUP_PREEMPT))
  38.                 check_preempt_tick(cfs_rq, curr);
  39. }

  40. /*
  41. * scheduler tick hitting a task of our scheduling class:
  42. */
  43. static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
  44. {
  45.         struct cfs_rq *cfs_rq;
  46.         struct sched_entity *se = &curr->se;

  47.         for_each_sched_entity(se) {
  48.                 cfs_rq = cfs_rq_of(se);
  49.                 entity_tick(cfs_rq, se, queued);
  50.         }
  51. }

  52. /*
  53. * High-resolution timer tick.
  54. * Runs from hardirq context with interrupts disabled.
  55. */
  56. static enum hrtimer_restart hrtick(struct hrtimer *timer)
  57. {
  58.         struct rq *rq = container_of(timer, struct rq, hrtick_timer);

  59.         WARN_ON_ONCE(cpu_of(rq) != smp_processor_id());

  60.         spin_lock(&rq->lock);
  61.         update_rq_clock(rq);
  62.         rq->curr->sched_class->task_tick(rq, rq->curr, 1);
  63.         spin_unlock(&rq->lock);

  64.         return HRTIMER_NORESTART;
  65. }

  66. /*
  67. * This function gets called by the timer code, with HZ frequency.
  68. * We call it with interrupts disabled.
  69. *
  70. * It also gets called by the fork code, when changing the parent's timeslices.
  71. */
  72. void scheduler_tick(void)
  73. {
  74.         int cpu = smp_processor_id();
  75.         struct rq *rq = cpu_rq(cpu);
  76.         struct task_struct *curr = rq->curr;

  77.         sched_clock_tick();

  78.         spin_lock(&rq->lock);
  79.         update_rq_clock(rq);
  80.         update_cpu_load(rq);
  81.         curr->sched_class->task_tick(rq, curr, 0);
  82.         spin_unlock(&rq->lock);

  83. #ifdef CONFIG_SMP
  84.         rq->idle_at_tick = idle_cpu(cpu);
  85.         trigger_load_balance(rq, cpu);
  86. #endif
  87. }
复制代码

论坛徽章:
0
发表于 2008-07-28 23:49 |显示全部楼层
原帖由 Solaris12 于 2008-7-28 12:57 发表



OK, 期待你的答案。

不过我还是觉得system kernel thread属于分时调度的话,还是讲不通。


别讨论了,Linux和Solaris的kernel thread的唯一相同点,就是都叫“kernel thread”,除了名字相同,压根就不是一类概念。

论坛徽章:
0
发表于 2008-07-29 08:07 |显示全部楼层
原帖由 albcamus 于 2008-7-28 23:49 发表


别讨论了,Linux和Solaris的kernel thread的唯一相同点,就是都叫“kernel thread”,除了名字相同,压根就不是一类概念。


NO. 不管叫什么,Linux OS里一定有只在内核空间存在的系统线程,这些系统线程一定不是分时调度,只是通过抢占或者主动让出CPU来实现调度。

论坛徽章:
0
发表于 2008-07-29 09:35 |显示全部楼层
原帖由 Solaris12 于 2008-7-29 08:07 发表


NO. 不管叫什么,Linux OS里一定有只在内核空间存在的系统线程,这些系统线程一定不是分时调度,只是通过抢占或者主动让出CPU来实现调度。

是的,它有时间片的概念,但不是分时调度。
虽然可以在中断返回路径(内核到内核)增加调度点来实现分时调度内核线程。不过这样确实不合理

论坛徽章:
0
发表于 2008-07-29 10:47 |显示全部楼层
原帖由 zx_wing 于 2008-7-29 09:35 发表

是的,它有时间片的概念,但不是分时调度。
虽然可以在中断返回路径(内核到内核)增加调度点来实现分时调度内核线程。不过这样确实不合理



这么看来,虽然有些差异,Linux和Solaris目前的实现是差不多的。

[ 本帖最后由 Solaris12 于 2008-7-29 10:49 编辑 ]

论坛徽章:
0
发表于 2008-07-29 10:51 |显示全部楼层
原帖由 sisi8408 于 2008-7-28 23:26 发表

/*
* in .26
*
* im not sure the following `curr` must be not kthread,
* if scheduled by cfs
*/
static void check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr)
{
        u ...


看来你的结论和zx_wing似乎是一致的,不过你太cool了,用代码回答,实在是为难了像我这样的菜鸟。

论坛徽章:
0
发表于 2008-07-29 11:14 |显示全部楼层
原帖由 Solaris12 于 2008-7-29 10:51 发表


看来你的结论和zx_wing似乎是一致的,不过你太cool了,用代码回答,实在是为难了像我这样的菜鸟。

哈哈,不知道是不是我太菜了
我认为sis同志贴的代码都是风马牛不相及的东西,说明不了任何问题。

论坛徽章:
0
发表于 2008-07-29 19:02 |显示全部楼层
说一下我的看法
sisi8408 28楼贴的这段代码本质(就帖子里讨论的方面来看)上和之前的版本是一样的,只是调度算法的问题

scheduler_tick 函数最后调用entity_tick, 咋一看它其实就是看看是否可能需要进行抢占, 如果可能,,则调用check_preempt_tick, 如果需要就resched_task
不过这个一切都和老版本没有本本没有本质的区别,就是时间到了重新调度。

但是resched_task 本身只是说需要调度,因为和是否UP没有关系,所以UP代码:

static void __resched_task(struct task_struct *p, int tif_bit)
{
        assert_spin_locked(&task_rq(p)->lock);
        set_tsk_thread_flag(p, tif_bit);
}

它只是说需要调度,但是它并不是调度点,在时钟中断返回时,才考虑是否需要scheduler工作

ret_from_intr:
    GET_THREAD_INFO(%ebp)
check_userspace:
    movl PT_EFLAGS(%esp), %eax    # mix EFLAGS and CS
    movb PT_CS(%esp), %al
    andl $(X86_EFLAGS_VM | SEGMENT_RPL_MASK), %eax
    cmpl $USER_RPL, %eax
    jb resume_kernel        # not returning to v8086 or userspace

ENTRY(resume_userspace) //这里返回用户态所做的事情咱不关心



#ifdef CONFIG_PREEMPT
ENTRY(resume_kernel)
    DISABLE_INTERRUPTS(CLBR_ANY)
    cmpl $0,TI_preempt_count(%ebp)    # non-zero preempt_count ? //禁止抢占,那么只能返回,根本不考虑__resched_task的工作结果,如果没有开启抢占,那么resume_kernel 就定义为 restore_nocheck直接返回了(内核态不允许抢占)
    jnz restore_nocheck
need_resched:
        //接下来才重新调度
    movl TI_flags(%ebp), %ecx    # need_resched set ?
    testb $_TIF_NEED_RESCHED, %cl
    jz restore_all
    testl $X86_EFLAGS_IF,PT_EFLAGS(%esp)    # interrupts off (exception path) ?
    jz restore_all
    call preempt_schedule_irq
    jmp need_resched//
END(resume_kernel)
#endif


所以如果禁止抢占,内核态的进程如果愿意,可以一直运行,调度不会发生
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP