Linux0.11内核分析三之进程调度

Linux进行进程调度一般有两种情况
1.允许进程运行的时间结束
2.进程的运行停止 比如当进程阻塞时需要等待外设提供数据 或者等待其他程序的运行结果 这时就会进行进程调度
这里是执行for(;;) pause(); 最终执行到schedule()函数切换到其他进程运行 这里切换到进程1

Linux创建了进程1 后开始进行进程调度

static inline _syscall0(int,pause)
void main(void)     /* This really IS void, no error here. */
{
    ...
    if (!fork()) {      /* we count on this going ok */
        init();
    }
/*
 *   NOTE!!   For any other task 'pause()' would mean we have to get a
 * signal to awaken, but task0 is the sole exception (see 'schedule()')
 * as task 0 gets activated at every idle moment (when no other tasks
 * can run). For task0 'pause()' just means we go check if some other
 * task can run, and if not we return here.
 */
    for(;;) pause();
    ...
}

接下来是循环的运行pause() 函数 pause()也是系统调用 系统调用的过程和fork open类似 都是通过调用unistd.h的syscall0 通过int 0x80中断 在system_call.s的call _sys_call_table(,%eax,4)映射到sys_pause()的系统调用函数去执行

//unistd.h
int pause(void);
#define __NR_pause  29
#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##name)); \
if (__res >= 0) \
    return (type) __res; \
errno = -__res; \
return -1; \
}

替换后就是

int pause(void) 
{ 
long __res; \
__asm__ volatile ("int $0x80" 
    : "=a" (__res) 
    : "0" (__NR_pause)); 
if (__res >= 0) 
    return (int) __res; 
errno = -__res; 
return -1; 
}

这里int 0x80后就去system_call.s去执行调用 最后执行sys_pause()函数

int sys_pause(void)
{
    current->state = TASK_INTERRUPTIBLE;//设置进程0为可中断等待状态
    schedule(); //切换进程
    return 0;
}
void schedule(void)
{
    int i,next,c;
    struct task_struct ** p;

/* check alarm, wake up any interruptible tasks that have got a signal */
 //遍历所有进程 判断报警定时值alarm和信号位图进行处理 这里不会产生什么效果
    for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
        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) {
        c = -1;
        next = 0;
        i = NR_TASKS;
        p = &task[NR_TASKS];
        while (--i) {
            if (!*--p)
                continue;
            if ((*p)->state == TASK_RUNNING && (*p)->counter > c) //找出处于就绪态的进程和counter最大的那个
                c = (*p)->counter, next = i;
        }
        if (c) break;
        for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
            if (*p)
                (*p)->counter = ((*p)->counter >> 1) +
                        (*p)->priority;
    }
    switch_to(next);  //切换到进程1 
}
#define FIRST_TSS_ENTRY 4
#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
//FIRST_TSS_ENTRY<<3也就是100000 (unsigned long) n)<<4)对进程1是10000 所以_TSS(1)就是110000
//最后两位是特权级00表示内核特权级 第三位0表示GDT 110表示GDT的第六项 也即tss0的下标
#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])); \
}

了解switch_to函数的话可以去看这篇博客讲的很清楚 我就不再重复一遍
http://blog.csdn.net/smallmuou/article/details/6837087

最重要的是ljmp %0\n\t这句 ljmp通过cpu的任务门机制从未实际使用任何任务门,将cpu的各个寄存器值保存在进程0的tss中,讲进程1的tss数据以及LDT的代码段,数据段描述符数据恢复给CPU的各个寄存器,实现从特权0的内核代码切换到3特权级的进程1代码执行。

你可能感兴趣的:(Linux内核源代码分析)