目录
1.进程介绍
在进程模块里面,我们知道了进程就是一个task_struct的结构体,里面含有进程的各种信息。进程存放在进程数组task_struct的数组里面。
2.进程调度
在进程调度里面主要的两个函数:
//进程调度函数
void schedule(void);
//切换到下一个进程 这个功能使用宏定义完成的
switch_to(next);
2.1.进程状态
运行态: 可以被运行
就绪态: 进程切换时,只能在就绪态里面挑选进程
可中断睡眠状态: 可以被信号打断,变成就绪态或者运行态
不可中断睡眠状态: 只能被wakeup所唤醒变成就绪态或者运行态
暂停状态: 收到SIGTOP,SIGTSTP,SIGTTIN
僵死状态: 进程停止运行,但是父进程没有回收, waitpid函数。
2.2.进程调度函数 ---schedule
1. 检查所有进程的定时器,然后唤醒某一些进程,将进程从可中断睡眠状态变为就绪态
2.循环task列表 根据counter大小决定进程切换
(1)如果找到最大值,而且不为0,直接返回,没有(2)了。
(2)如果都为0,进行时间片的重新分配,然后重新执行2步骤。
3.切换到下一个进程------switch_to函数
// 时间片分配
void schedule(void)
{
int i,next,c;
struct task_struct ** p;
/* check alarm, wake up any interruptible tasks that have got a signal */
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p) {//alarm是用来设置警告,比如jiffies有1000个可能其中一些需要警告那么就用alarm来实现
if ((*p)->alarm && (*p)->alarm < jiffies) {
(*p)->signal |= (1<<(SIGALRM-1));
(*p)->alarm = 0;
}
//~(_BLOCKABLE & (*p)->blocked
//&&(*p)->state==TASK_INTERRUPTIBLE
//用来排除非阻塞信号
//如果该进程为可中断睡眠状态 则如果该进程有非屏蔽信号出现就将该进程的状态设置为running
if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
(*p)->state==TASK_INTERRUPTIBLE)
(*p)->state=TASK_RUNNING;
}
/* this is the scheduler proper: */
// 以下思路,循环task列表 根据counter大小决定进程切换
while (1) {
c = -1;
next = 0;
i = NR_TASKS;
p = &task[NR_TASKS];
while (--i) {
if (!*--p)
continue;//进程为空就继续循环
if ((*p)->state == TASK_RUNNING && (*p)->counter > c)//找出c最大的task
c = (*p)->counter, next = i;
}
if (c) break;//如果c找到了,就终结循环,说明找到了
//进行时间片的重新分配
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p)//这里很关键,在低版本内核中,是进行优先级时间片轮转分配,这里搞清楚了优先级和时间片的关系
//counter = counter/2 + priority
(*p)->counter = ((*p)->counter >> 1) +
(*p)->priority;
}
//切换到下一个进程 这个功能使用宏定义完成的
switch_to(next);
}
2.3.进程切换函数 ---switch_to()
1.先判断是否为当前进程,如果是,不用切换
2.切换进程:
(1)将进程赋值给全局变量current这个变量,就可以完成进程切换
(2)将进程的上下文(TSS和当前堆栈中的信息)切换
// 进程切换是用汇编宏定义实现的
//1. 将需要切换的进程赋值给当前进程的指针
//2. 将进程的上下文(TSS和当前堆栈中的信息)切换
#define switch_to(n) {\
struct {long a,b;} __tmp; \
__asm__("cmpl %%ecx,_current\n\t" \
"je 1f\n\t" \
"movw %%dx,%1\n\t" \
"xchgl %%ecx,_current\n\t" \
"ljmp %0\n\t" \
"cmpl %%ecx,_last_task_used_math\n\t" \
"jne 1f\n\t" \
"clts\n" \
"1:" \
::"m" (*&__tmp.a),"m" (*&__tmp.b), \
"d" (_TSS(n)),"c" ((long) task[n])); \
}
3.sleep_on函数
当某个进程想访问CPU资源,但是CPU资源被占用访问不到,就会休眠。休眠是以链表形成的,类似递归的情况。-----sleep等待队列
// 当某个进程想访问CPU资源,但是CPU资源被占用访问不到,就会休眠
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;//这两步相当于 给休眠链表添加了一个新node
// 其实核心就是把state置为TASK_UNINTERRUPTIBLE
current->state = TASK_UNINTERRUPTIBLE;
schedule();
if (tmp)
tmp->state=0;
}