版权声明:本文为博主原创文章,未经博主允许不得转载。
1.线程描述符:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
重要字段解释:
flags: 该字段中有一个位是用于设置内核是否应该被重新调度的标志–need_resched,set_tsk_need_resched()和clear_tsk_need_resched()函数实际上就是对该字段的某一位进行操作(第31位 ?)
saved_preempt_count: 内核此时是否可以进行抢占的标志,为0表示当前进程不持有锁,可以被抢占.大于0则表示此时持有锁,不能被抢占
preempt_counter 字段是32位的, 除了抢占计数器之外还包括其他标志位, 只要 preempt_counter 整体不为0,
就不能进行内核抢占, 这个设计一下
子简化了对众多不能抢占的情况的检测:
Bit 0-7: 抢占计数器, 表示显式禁用内核抢占的次数
Bit 8-15: 软中断计数器, 记录可延迟函数被禁用的次数
Bit 16-27: 硬中断计数器, 表示中断处理程序的嵌套数, irq_enter()递增它, irq_exit()递减它
Bit 28: PREEMPT_ACTIVE 标志, 内核抢占的标志
2.schedule()函数被调度的时机:
1.内核从系统调用或中断处理程序返回时:
1.内核要返回到用户空间时:
如果当前进程的thread_info结构中的need_resched标志被设置,那么就可以调用schedule(),因为内核返回用
户空间时,它知道自己是安全的,因为既然它可以继续去执行当前进程,那么它当然可以再去选择一个新的进程去执行
2.内核要返回内核空间时:
如果当前进程的thread_info结构体中的need_resched标志被设置而且saved_preempt_count为0时, 通过调用preempt_schedule_irq()函数来间接调用schedule()函数:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
此时thread_info->saved_preempt_count中的PREEMPT_COUNT位被设置了, 表示schedule()函数的调用不是进程自己主动调用的,而是被抢占了
3.schedule函数 部分代码:
- 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
- 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
4.PREEMPT_ACTIVE位的作用:
考虑这样的代码:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
如果在set_current_state()函数执行之后,产生了中断,这时如果没有设置PREEMPT_ACTIVE位的话,将会是这样一个执行情况:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
prev->state的状态是TASK_UNINTERUPTIBLE, !preempt_count()的结果也为1, 此时第一个if语句条件满足. 此时进程的状态为TASK_UNINTERRUPTIBLE, 第二个if语句条件不满足, 执行else语句, 此时该进程不会加入红黑树. 而在中断之前, 进程只是把自己的状态设为TASK_UNINTERRUPTIBLE, 还没有将自己加入等待队列. 这样进程不在运行队列中, 同时当等待队列条件满足时, 也不会被该等待队列给唤醒, 因为进程还没有加入到等待队列中, 所以此时进程就再也没有机会运行了
如果设置了PREEMPT_ACTIVE位的情况下, 上述代码将会是这样的执行情况.
内核通过调用preempt_schedule_irq()函数将saved_preempt_count的PREEMPT_ACTIVE位设置为1,此时上述的第一个if语句的条件就不会满足,因为!(preempt_count() & PREEMPT_ACTIVE)的值为0. 所以进程会被加入红黑树, 这样进程就还有被重新调度的机会
总的来说,PREEMPT_ACTIVE位的使用, 就是为了表示此时schedule()函数的调用是否是被进程自己主动调用的还是被内核抢占的. 如果是进程主动调用schedule()函数的, 那么当当前进程的状态不是TASK_RUNNING时, 内核不会把当前进程加入红黑树中. 如果是内核抢占的情况,则会把当前进程加入红黑树.
5.几个与抢占有关的函数:
preempt_disable(): 抢占计数加1, 禁止内核抢占
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
preempt_enable(): 抢占计数减1, 并判断是否可以重新调度
- 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
- 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
从上述两个函数的源代码来看, 抢占计数和调度标志都是对当前进程的thread_info->preempt_count和thread_info->flags变量进行操作的
本文引述自:
http://www.cnblogs.com/openix/archive/2013/03/09/2952041.html
http://blog.chinaunix.net/uid-12461657-id-3353217.html
http://edsionte.com/techblog/archives/3819