1:代码环境
linux 0.11
2: 其他函数
2.1 显示进程show_task()
void show_task(int nr,struct task_struct * p)//nr为pid,代表第几个进程
{
int i,j = 4096-sizeof(struct task_struct); //找到栈堆的大小
printk("%d: pid=%d, state=%d, ",nr,p->pid,p->state);//打印pid 和状态
i=0;
while (i<j && !((char *)(p+1))[i])
i++;
printk("%d (of %d) chars free in kernel stack\n\r",i,j);//打印栈的内容
}
2.2 协处理器函数 math_state_restore()
完成进程切换时,协处理器的内容切换
/*
* 'math_state_restore()' saves the current math information in the
* old math state array, and gets the new ones from the current task
*/
void math_state_restore()
{
if (last_task_used_math == current)
return;
__asm__("fwait");
if (last_task_used_math) {
__asm__("fnsave %0"::"m" (last_task_used_math->tss.i387));
}
last_task_used_math=current;
if (current->used_math) {
__asm__("frstor %0"::"m" (current->tss.i387));
} else {
__asm__("fninit"::);
current->used_math=1;
}
}
3: 进程调度
3.1 调度函数schedule()
进程的状态
#define TASK_RUNNING 0 可运行状态,就绪态
#define TASK_INTERRUPTIBLE 1 可中断睡眠状态(可以被信号中断,使其变成runing)
#define TASK_UNINTERRUPTIBLE 2 不可中断睡眠状态
//1 2 可以通过sleep_on函数改变成这种状态
#define TASK_ZOMBIE 3 僵死状态, 进程已经停止运行,父进程没有把它清空。直到子进程向父进程发送信号,父进程会使用waitpid()来接收信号
#define TASK_STOPPED 4 暂停状态 (收到暂停信号 SIGSTOP SIGSTP..)
/*
* 'schedule()' is the scheduler function. This is GOOD CODE! There
* probably won't be any reason to change this, as it should work well
* in all circumstances (ie gives IO-bound processes good response etc).
* The one thing you might take a look at is the signal-handler code here.
*
* NOTE!! Task 0 is the 'idle' task, which gets called when no other
* tasks can run. It can not be killed, and it cannot sleep. The 'state'
* information in task[0] is never used.
*/
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) {
if ((*p)->alarm && (*p)->alarm < jiffies) { //alarm不为0,并且当jiffies的大于了arlarm,说明时间到达
(*p)->signal |= (1<<(SIGALRM-1)); //设置警告信号SIGALRM
(*p)->alarm = 0;//清空alem
}
if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
(*p)->state==TASK_INTERRUPTIBLE)//((信号存在 & 信号不是KILL和STOP &信号是不阻塞的)&& 进程是可中断的睡眠状态)
(*p)->state=TASK_RUNNING; //把信号设置为可运行的状态
}
/* this is the scheduler proper: */
//选择一个couter最大的进程结构体
while (1) {
c = -1;
next = 0;
i = NR_TASKS;
p = &task[NR_TASKS]; //p指向最后的位置
while (--i) {
if (!*--p)//判断进程数组中指针是否为空
continue;
if ((*p)->state == TASK_RUNNING && (*p)->counter > c) //(进程可运行 && 并且进程中counter比当记录的最大值c大)
c = (*p)->counter, next = i; //更新c的值为当前counter,记录当前计数到next
}
if (c) break; //如果c存在,就break出while(1),说明进程数组中还有可以执行的进程
/*如果执行了下面函数,说明c为0,表示进程数组中,所有进程都执行完了,就会进行时间片的重分配*/
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p)
(*p)->counter = ((*p)->counter >> 1) +
(*p)->priority; //对counter赋值 counter = counter/2 + priority
}
switch_to(next);//切换进程
}
使用这个调度的函数如下:
3.2 切换函数switch_to(next);
将进程切换
1:将需要切换的进程赋值给current
2:进行进程的上下文切换 。上下文指的是CPU的特殊寄存器,通用寄存器 + 当前堆栈的信息
/*
* switch_to(n) should switch tasks to task nr n, first
* checking that n isn't the current task, in which case it does nothing.
* This also clears the TS-flag if the task we switched to has used
* tha math co-processor latest.
*/
#define switch_to(n) {\
struct {long a,b;} __tmp; \
__asm__("cmpl %%ecx,_current\n\t" \ //比较要切换的线程是不是当前线程 _current == %%ecx ?
"je 1f\n\t" \ //为当前线程则不做任何事
"movw %%dx,%1\n\t" \
"xchgl %%ecx,_current\n\t" \ //将当前线程_current设置为要切换的线程 _current = %%ecx
"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.3 进程阻塞函数sleep_on()
当进程要访问的资源不存在的时候,进行调用
void sleep_on(struct task_struct **p) //传递的是当前睡眠进程链表的地址末尾地址
{
struct task_struct *tmp; //创建一个只针tmp
if (!p)
return;
if (current == &(init_task.task)) //当前进行为第一个进程时,就会打印panic信息, 这个进程不允许休眠
panic("task[0] trying to sleep");
tmp = *p;//让tmp指向是当前睡眠进程链表的地址末尾地址p
*p = current;//p指向即将要休眠的当前进程成为新的睡眠进程链表的地址末尾地址
current->state = TASK_UNINTERRUPTIBLE;//改变当前线程的状态为不可打断休眠状态,进行休眠
schedule();//进行调度 并不会释放栈中的变量tmp
if (tmp)
tmp->state=0;
}
其中上面指针指向的,等待链表状态如下图所示
3.4 进程唤醒函数wake_up()
void wake_up(struct task_struct **p)
{
if (p && *p) {
(**p).state=0; //把进程状态修改成可运行状态
*p=NULL;
}
}
下面用inode节点中等待为例子说明其中的关系
1:第一个睡眠进程
2:第二个睡眠进程
3:第三个睡眠进程
inode节点中的i_wait会指向一个睡眠进程的链表,链表之间通过每个进程内部tmp来进行链接
4:唤醒进程
唤醒时,链表中所有的进程都会被唤醒,并不是只唤醒链表中个一个或者部分。
但是唤醒后不一定本次inode资源可以满足所有的等待的进程,那么不能满足的进程会再次形成新的睡眠链表,还是由inode中的i_wait来指向。