说起进程的切换,先说一下进程的一共有哪些状态,如下,在Linux中定义了进程的5种状态,在stack_struct结构体中的state就是用来表示这个进程的运行状态。
#define TASK_RUNNING 0 /*运行状态*/ #define TASK_INTERRUPTIBLE 1 /*可中断睡眠状态*/ #define TASK_UNINTERRUPTIBLE 2 /*不可中断睡眠状态*/ #define TASK_ZOMBIE 3 /*僵死状态*/ #define TASK_STOPPED 4 /*暂停状态*/
需要注意的是
运行状态:进程的切换只有当进程转换到运行状态时才会被执行。
可中断睡眠状态:可以被信号中断,使其变成运行态。这与linux内核中的poll机制有关。linux当中有许多信号,比如kill -9。我们可以通过信号的方式给进程发消息。例如,父进程等待子进程结束时会wait_pid,也就是说在子进程结束的时候会给父进程发送一个signal的信号,父进程收到信号后,就会退出wait_pid函数,执行下面代码。
不可中断睡眠状态:缺少进程运行的资源,会sleep进入睡眠状态,不可以被信号中断,只能被wakeup 唤醒变成运行态
暂停状态:进程收到某些信号后就会变成暂停状态
僵死状态:子进程停止运行了,但是他的task_struct还没有被父进程清空,则变成僵死状态。父进程一直在wait_pid,直到子进程发来信号后,父进程才将其task_struct等变量清除。
说起进程的调度就不得不提sched.c中的schedule()函数。代码如下
void schedule(void) /*进程调度*/ { int i,next,c; struct task_struct ** p; for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) /*task链表中最多存放64个,这是从后往前遍历*/ if (*p) { if ((*p)->alarm && (*p)->alarm < jiffies) { (*p)->signal |= (1<<(SIGALRM-1)); (*p)->alarm = 0; } if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&(*p)->state==TASK_INTERRUPTIBLE) /*信号不为空并且去除掉不能引发进程就绪状态的阻塞信号,当前进程状态是可中断睡眠*/ (*p)->state=TASK_RUNNING; /*则将进程变为运行态*/ } /* this is the scheduler proper: */ while (1) { /*进程调度--寻找最大的时间片,即对counter的比较*/ c = -1; next = 0; i = NR_TASKS; p = &task[NR_TASKS]; /*task[NR_TASKS]为进程向量表*/ while (--i) { if (!*--p) /*task链表中某个位置为空,则不用比较时间片*/ continue; /*如果当前状态是可运行的,并且时间片大于最大进程的时间片c*/ if ((*p)->state == TASK_RUNNING && (*p)->counter > c) c = (*p)->counter, next = i; /*最大counter的pid在next中存储*/ } if (c) break; /*还有某个进程时间片没有用完,则退出while*/ /*若所有进程时间片都用完,则执行下面代码重新分配时间片*/ for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) if (*p)/*如果进程槽里有进程*/ (*p)->counter = ((*p)->counter >> 1) + (*p)->priority; /*counter/2 + priority*/ } switch_to(next); /*进程切换函数*/ }
进程切换分为几个步骤:
1.将需要切换的进程赋值给当前进程的指针current(宏定义)
2.进程相关的通用寄存器,特殊寄存器(tss)等信息的切换
如上图,当没有进行进程切换的时候,原进程tss描述符的地址在tss段地址寄存器当中,可以通过该寄存器找到tss描述符从而找到tss段。当进程进行切换的时候,将新进程的tss描述符放到段地址寄存器中,将原来进程执行时cpu中的一些值保存到原tss段中,再将新的tss段载入到cpu里面去。
下面看一看sleep_on进程唤醒函数
void sleep_on(struct task_struct **p) { struct task_struct *tmp; if (!p) return; if (current == &(init_task.task)) /*如果当前进程时0号进程*/ panic("task[0] trying to sleep"); tmp = *p; *p = current; current->state = TASK_UNINTERRUPTIBLE; /*睡眠是不可中断的,只能wake_up唤醒*/ schedule(); if (tmp) tmp->state=0; }
以上代码可实现进程的唤醒,当进程请求资源未得时,需要进行睡眠,多个进程请求资源未得,就会形成一个sleep_on的链表。当有资源时,又一个一个的被唤醒。