一个简单的linux内核mykernel分析

mypcb.h文件

#define MAX_TASK_NUM        4

#define KERNEL_STACK_SIZE   1024*8


/* CPU-specific state of this task */

struct Thread {

    unsigned long ip;

    unsigned long sp;

};

定义了一个任务结构体,此结构体包含的ip通过cs:ip确定程序的下一跳指令,sp为任务的堆栈的栈顶,这两个成员变量确饱了一个任务 在计算机上的正确执行;

typedef struct PCB{

    int pid;

    volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */

    char stack[KERNEL_STACK_SIZE];

    /* CPU-specific state of this task */

    struct Thread thread;

    unsigned long task_entry;

    struct PCB *next;

}tPCB;


void my_schedule(void);

pcb结构体包含了一个任务的各种重要信息,pid为进程标识符,唯一标识一个进程;state确定进程目前的状态;stack数组为进程开辟了一个1024*8大小的堆栈,供进程被调用时使用;Thread结构体包含ip和sp,在进程获取cpu资源后ip装入pc运行,sp指向进程的栈顶;task_entry为任务入口;*next供进程链链接下一个进程,构成进程就绪队列;void my_schedule(void);是进程调度程序的声明;

mymain.c

void __init my_start_kernel(void)函数中进行对任务的初始化

task[pid].pid = pid;

    task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */

    task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;

    task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];

    task[pid].next = &task[pid];

通过对一个pid为0的任务初始化,将pcb结构体的各项内容进行初始化并构建只有一个任务块的链表。

    for(i=1;i<MAX_TASK_NUM;i++)

    {

        memcpy(&task[i],&task[0],sizeof(tPCB));

        task[i].pid = i;

        task[i].state = -1;

        task[i].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];

        task[i].next = task[i-1].next;

        task[i-1].next = &task[i];

    }

通过一个for循环创建MAX_TASK_NUM个任务,将任务的PCB数组初始化,并且链接到最初创建的只有一个任务块的链表中。

    pid = 0;

    my_current_task = &task[pid];

asm volatile(

    "movl %1,%%esp\n\t" /* set task[pid].thread.sp to esp */

    "pushl %1\n\t"        /* push ebp */

    "pushl %0\n\t"        /* push task[pid].thread.ip */

    "ret\n\t"            /* pop task[pid].thread.ip to eip */

    "popl %%ebp\n\t"

   

    : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/

);

通过嵌入一段汇编代码将pid为0的任务的bp,sp,ip初始化,使其可以在cpu上运行。因为此进程的堆栈空间是私有的,只供此任务使用,所以不需要像函数调用一样重新建立堆栈,调用结束拆除堆栈。在这里只需要将esp的地址指向任务的堆栈即可。

void my_process(void)

{

    int i = 0;

    while(1)

    {

        i++;

        if(i%10000000 == 0)

        {

            printk(KERN_NOTICE "this is process %d -\n",my_current_task->pid);

            if(my_need_sched == 1)

            {

                my_need_sched = 0;

           my_schedule();

        }

        printk(KERN_NOTICE "this is process %d +\n",my_current_task->pid);

        }     

    }

}


void my_timer_handler(void)

{

#if 1

    if(time_count%1000 == 0 && my_need_sched != 1)

    {

        printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");

        my_need_sched = 1;

    } 

    time_count ++ ;  

#endif

    return;  

}

当发生时钟中断(time_count%1000 == 0)时,把my_need_sched置位为1,这是my_process中if部分开始,把my_need_sched复位为0,并调用my_schedule

void my_schedule(void)

{

    tPCB * next;

    tPCB * prev;


    if(my_current_task == NULL 

        || my_current_task->next == NULL)

    {

    return;

    }

    printk(KERN_NOTICE ">>>my_schedule<<<\n");

    /* schedule */

    next = my_current_task->next;

    prev = my_current_task;

以上为判断目前的任务是否存在,并且任务链表中也没有任务的情况。

    if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */

    {

    /* switch to next process */

    asm volatile(

        "pushl %%ebp\n\t"    /* save ebp */

        "movl %%esp,%0\n\t" /* save esp */

        "movl %2,%%esp\n\t"     /* restore  esp */

        "movl $1f,%1\n\t"       /* save eip */

        "pushl %3\n\t" 

        "ret\n\t"            /* restore  eip */

        "1:\t"                  /* next process start here */

        "popl %%ebp\n\t"

        : "=m" (prev->thread.sp),"=m" (prev->thread.ip)

        : "m" (next->thread.sp),"m" (next->thread.ip)

    ); 

    my_current_task = next; 

    printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);  

    }

以上为当 下一个任务的state状态为正在执行,则会发生进程的切换,现将上一个任务的sp,ip等内容从新装回进程的pcb的stack中,将将要执行的进程的sp,ip装入cpu的寄存器中开始执行。最好,将目前进程的标识更改。

    else

    {

        next->state = 0;

        my_current_task = next;

        printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);

    /* switch to new process */

    asm volatile(

        "pushl %%ebp\n\t"    /* save ebp */

        "movl %%esp,%0\n\t" /* save esp */

        "movl %2,%%esp\n\t"     /* restore  esp */

        "movl %2,%%ebp\n\t"     /* restore  ebp */

        "movl $1f,%1\n\t"       /* save eip */

        "pushl %3\n\t" 

        "ret\n\t"            /* restore  eip */

        : "=m" (prev->thread.sp),"=m" (prev->thread.ip)

        : "m" (next->thread.sp),"m" (next->thread.ip)

    );          

    }   

    return;

}

以上与next->state = 0;类似,都进行了进程的切换。








你可能感兴趣的:(linux内核)