正在加载
请稍等

菜单

红楼飞雪 梦

15526773247

文章

Home android 嵌入式开发 进程调度的实现
Home android 嵌入式开发 进程调度的实现

进程调度的实现

android 嵌入式开发, linux开发学习 by

调度程序在内核中就是一个函数,为了讨论方便,我们同样对其进行了简化,略其对SMP的实现部分。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
asmlinkage void schedule( void )
 
{
    struct task_struct *prev, *next, *p; /* prev表示调度之前的进程,
                                          *
                                          * next表示调度之后的进程 */
 
    struct list_head *tmp;
 
    int this_cpu, c;
 
 
    if ( !current->active_mm )
        BUG();          /*如果当前进程的的active_mm为空,出错*/
 
need_resched_back:
 
    prev = current;         /*让prev成为当前进程 */
 
    this_cpu = prev->processor;
 
 
    if ( in_interrupt() )   /*如果schedule是在中断服务程序内部执行,
                         *
                         * 就说明发生了错误*/
 
    {
        printk( "Scheduling in interrupt\n" );
 
        BUG();
    }
 
    release_kernel_lock( prev, this_cpu );          /*释放全局内核锁,
                                                     *
                                                     * 并开this_cpu的中断*/
 
    spin_lock_irq( &runqueue_lock );                /*锁住运行队列,并且同时关中断*/
 
    if ( prev->policy == SCHED_RR )                 /*将一个时间片用完的SCHED_RR实时
                                                 *
                                                 * goto move_rr_last;      进程放到队列的末尾 */
 
move_rr_back:
 
        switch ( prev->state )                  /*根据prev的状态做相应的处理*/
 
        {
        case TASK_INTERRUPTIBLE:                /*此状态表明该进程可以被信号中断*/
 
            if ( signal_pending( prev ) )   /*如果该进程有未处理的
                                         *
                                         * 信号,则让其变为可运行状态*/
 
            {
                prev->state = TASK_RUNNING;
 
                break;
            }
 
        default:                                /*如果为可中断的等待状态或僵死状态*/
 
            del_from_runqueue( prev );      /*从运行队列中删除*/
 
        case TASK_RUNNING:;                     /*如果为可运行状态,继续处理*/
        }
 
    prev->need_resched = 0;
 
 
    /*下面是调度程序的正文 */
 
repeat_schedule:                        /*真正开始选择值得运行的进程*/
 
    next = idle_task( this_cpu );   /*缺省选择空闲进程*/
 
    c = -1000;
 
    if ( prev->state == TASK_RUNNING )
 
        goto still_running;
 
still_running_back:
 
    list_for_each( tmp, &runqueue_head )    /*遍历运行队列*/
    {
        p = list_entry( tmp, struct task_struct, run_list );
 
        if ( can_schedule( p, this_cpu ) )
        {                               /*单CPU中,该函数总返回1*/
            int weight = goodness( p, this_cpu, prev->active_mm );
 
            if ( weight > c )
 
                c = weight, next = p;
        }
    }
 
 
/* 如果c为0,说明运行队列中所有进程的权值都为0,也就是分配给各个进程的
 *
 *   时间片都已用完,需重新计算各个进程的时间片 */
 
    if ( !c )
    {
        struct task_struct *p;
 
        spin_unlock_irq( &runqueue_lock );      /*锁住运行队列*/
 
        read_lock( &tasklist_lock );            /* 锁住进程的双向链表*/
 
        for_each_task( p )                      /* 对系统中的每个进程*/
 
        p->counter = (p->counter >> 1) + NICE_TO_TICKS( p->nice );
 
        read_unlock( &tasklist_lock );
 
        spin_lock_irq( &runqueue_lock );
 
        goto repeat_schedule;
    }
 
 
    spin_unlock_irq( &runqueue_lock );      /*对运行队列解锁,并开中断*/
 
 
    if ( prev == next )                     /*如果选中的进程就是原来的进程*/
 
    {
        prev->policy &= ~SCHED_YIELD;
 
        goto same_process;
    }
 
 
    /* 下面开始进行进程切换*/
 
    kstat.context_swtch++; /*统计上下文切换的次数*/
 
 
    {
        struct mm_struct *mm = next->mm;
 
        struct mm_struct *oldmm = prev->active_mm;
 
        if ( !mm )      /*如果是内核线程,则借用prev的地址空间*/
 
        {
            if ( next->active_mm )
                BUG();
 
            next->active_mm = oldmm;
        else {        /*如果是一般进程,则切换到next的用户空间*/
            if ( next->active_mm != mm )
                BUG();
 
            switch_mm( oldmm, mm, next, this_cpu );
        }
 
 
        if ( !prev->mm )                /*如果切换出去的是内核线程*/
 
        {
            prev->active_mm = NULL; /*归还它所借用的地址空间*/
 
            mmdrop( oldmm );        /*mm_struct中的共享计数减1*/
        }
    }
 
 
    switch_to( prev, next, prev );          /*进程的真正切换,即堆栈的切换*/
 
    __schedule_tail( prev );                /*置prev->policy的SCHED_YIELD为0 */
 
 
same_process:
 
    reacquire_kernel_lock( current );       /*针对SMP*/
 
    if ( current->need_resched )            /*如果调度标志被置位*/
 
        goto need_resched_back;         /*重新开始调度*/
 
    return;
}

 

 

以上就是调度程序的主要内容,为了对该程序形成一个清晰的思路,我们对其再给出进一步的解释:

  • 如果当前进程既没有自己的地址空间,也没有向别的进程借用地址空间,那肯定出错。另外, 如果schedule()在中断服务程序内部执行,那也出错.
  • 对当前进程做相关处理,为选择下一个进程做好准备。当前进程就是正在运行着的进程,可是,当进入schedule()时,其状态却不一定是TASK_RUNNIG,例如,在exit()系统调用中,当前进程的状态可能已被改为TASK_ZOMBE;又例如,在wait4()系统调用中,当前进程的状态可能被置为TASK_INTERRUPTIBLE。因此,如果当前进程处于这些状态中的一种,就要把它从运行队列中删除。
  • 从运行队列中选择最值得运行的进程,也就是权值最大的进程。
  • 如果已经选择的进程其权值为0,说明运行队列中所有进程的时间片都用完了(队列中肯定没有实时进程,因为其最小权值为1000),因此,重新计算所有进程的时间片,其中宏操作NICE_TO_TICKS就是把优先级nice转换为时钟滴答。
  • 进程地址空间的切换。如果新进程有自己的用户空间,也就是说,如果next->mm与next->active_mm相同,那么,switch_mm( )函数就把该进程从内核空间切换到用户空间,也就是加载next的页目录。如果新进程无用户空间(next->mm为空),也就是说,如果它是一个内核线程,那它就要在内核空间运行,因此,需要借用前一个进程(prev)的地址空间,因为所有进程的内核空间都是共享的,因此,这种借用是有效的。

用宏switch_to()进行真正的进程切换,后面将详细描述。

 

 

 

13 2015-09

 

我要 分享

 

 

本文 作者

 

相关 文章