进程描述符是struct task_struct类型,定义在<linux/sched.h>,进程描述符包含了进程的所有信息。
task_struct结构使用slab allocator分配。
2.6之前,task_struct存储在进程内核栈的底部。这样就不需要额外的寄存器来存储。
现在进程描述符使用slab allocator,一个新的结构体struct thread_info创建在栈的底部。
系统通过PID来标识进程。其类型是pid_t,最大为32768。可以通过/proc/sys/kernel/pid_max修改。
在x86中,current_thread_info() 可以获取 thread_info结构体的指针:
1 2 | movl $-8192, %eax andl %esp, %eax |
进而得到task_struct.
1 | current_thread_info()->task |
在PPC架构中,current存储在r2寄存器中。
进程描述符中的state域存储了进程的当前状态。
TASK_RUNNING。进程是可运行的,它要么正在运行,要么在run queue中等待运行。这是执行在用户态的进程的唯一状态。
TASK_INTERRUPTIBLE。进程正在睡眠,正等待一个条件发生,来进入运行状态。
TASK_UNINTERRUPTIBLE。和TASK_INTERRUPTIBLE一样,除了收到信号不被唤醒外。
多任务操作系统分为:cooperative multitasking 和 preemptive multitasking。
I/O-Bound Process
进行频繁的IO处理
Processor-Bound Process
进行频繁的处理器运行
Linux有两种优先级。
第一种是nice值,从-20到+19。默认是0。nice值越大优先级越低。在Linux中,nice值控制时间片的比例。
第二种是 real-time priority。
timeslice
睡眠是通过wait queue处理的。一个wait queue是一个简单的进程列表,它们等待一个事件发生。在内核中wait queue使用wake_queue_head_t表示。
wait queue可以通过DECLARE_WAITQUEUE()静态创建,也可以通过init_waitqueue_head()动态创建。
进程可以主动把它们自己放入等待队列,然后标记自己为不可运行。当等待队列关联的事件发生时,等待队列中的线程被唤醒。
等待、唤醒的典型逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 | DEFINE_WAIT(wait); add_wait_queue(q, &wait); while (!condition) { /* condition is the event that we are waiting for */ prepare_to_wait(&q, &wait, TASK_INTERRUPTIBLE); if (signal_pending(current)) /* handle signal */ schedule(); } finish_wait(&q, &wait); |
1. 创建一个wait queue entry,使用DEFINE_WAIT()
2. 使用add_wait_queue()将自己添加到等待队列q
3. prepare_to_wait()来将进程的状态变为TASK_INTERRUPTIBLE,并且会再次尝试将进程添加到等待队列。
使用finish_wait()将进程从等待队列中移除。
Waking Up
通过wake_up()
上下文从切换由context_switch()处理,定义在kernel/sched.c。被schedule()调用。
user preemption 发生在
从系统调用返回到用户空间时
从中断处理返回到用户空间时
kernel preemption 发生在
中断处理退出,返回到kernel-space时
内核代码再次变成可抢断时
显示调用schedule()
If a task in the kernel blocks (which results in a call to schedule() )
内核提供两种real-time调度策略,SCHED_FIFO和SCHED_RR。非real-time调度策略即SCHED_NORMAL。
real-time调度不是CFS调度的。由特殊的real-time scheduler,定义在kernel/sched_rt.c调度。
SCHED_FIFO是一个陷入先出的调度算法,没有时间片。这种task总是调度在SCHED_NORMAL之前,当SCHED_FIFO task变成可运行后,它会次序运行,直到block或者显式放弃处理器。更高优先级的SCHED_FIFO和SCHED_RR可以抢占SCHED_FIFO。相同优先级的使用round-robin,但是只有task显式的放弃处理器时才会这样调度。如果SCHED_FIFO任务是可运行的,低优先级的不能被运行。
SCHED_RR和SCHED_FIFO基本一样,但是它们一直运行,直到预定义的时间片耗尽。
实时优先级从0到MAX_RT_PRIO-1。通常MAX_RT_PRIO为100
SCHED_NORMAL 任务的nice值,映射到 MAX_RT_PRIO到 MAX_RT_PRIO+40,-20, +19 映射到100, 139。
如下
System call | Description |
nice() | set a process's nice value |
sched_setscheduler() | set a processos's scheduling policy |
sched_getscheduler() | Gets a process’s scheduling policy |
sched_setparam() | Sets a process’s real-time priority |
sched_getparam() | Gets a process’s real-time priority |
sched_get_priority_max() | Gets the maximum real-time priority |
sched_get_priority_min() | Gets the minimum real-time priority |
sched_rr_get_interval() | Gets a process’s timeslice value |
sched_setaffinity() | Sets a process’s processor affinity |
sched_getaffinity() | Gets a process’s processor affinity |
sched_yield() | Temporarily yields the processor |