Linux内核分析(二)之简单的进程调度

陈立立

原创作品转载请注明出处

《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

 

本文实现了一个简单的程序调度内核。

实验环境:操作系统Ubuntu 14.04 LTS,gcc版本4.8.4

搭建环境的方法:https://github.com/mengning/mykernel

首先在mykernel/下建立一个与程序控制块相关的头文件mypcb.h,代码如下:

 

/*

 * linux/mykernel/mypcb.h

 *

 * Kernel internal PCB types

 *

 * Copyright (C) 2016 Chen Lili

 *

 */

 

#defineMAX_TASK_NUM    10

#defineKERNEL_STACK_SIZE   1024*8  /* 8k bytes */

#defineUNRUNNABLE  -1

#defineRUNNABLE  0

#define FALSE 0

#define TRUE 1

 

/* CPU-specificstate of a task, only fit for 32-bit system. */

struct Thread {

    unsignedlong ip;/* instruction pointer */

    unsignedlong sp;/* stack pointer */

};

 

typedefstruct PCB{

    int pid;                /* process id */

    volatilelong state;    /* -1 unrunnable, 0 runnable */

    char stack[KERNEL_STACK_SIZE];

    /* CPU-specific state of this task */

    struct Thread thread;

    unsignedlong task_entry;

    struct PCB *next;

} tPCB;

 

void my_schedule(void);

 


该头文件定义了程序控制块的结构体。该结构体中包括了:

1.        进程ID,类型int。进程在操作系统中的唯一标识。

2.        进程状态,类型volatile long。目前支持“可运行”和“不可运行”。

3.        进程栈空间,类型char[]。本次代码中将栈大小设置为8k字节。

4.        当前线程。用于标识进程中正在执行的线程。

5.        进程的入口地址。

6.        下一个进程指针。用于指向下一个需要执行的进程的PCB。

同时,头文件中还定义了线程的结构体。该结构体中包括了:

1.        指令指针。线程执行的下一条指令的地址。

2.        栈顶指针。线程的栈顶指针。

此外,头文件中还声明了调度函数my_schedule()。

 

接下来介绍进程调度的主程序mymain.c

/*

 * linux/mykernel/mymain.c

 *

 * Kernel internal my_start_kernel

 *

 * Copyright (C) 2016  Chenlili

 *

 */

 

/*

 * Headers are omitted.

 */

 

#ifdefCONFIG_X86_LOCAL_APIC

#include

#endif

 

#include"mypcb.h"

 

#define SLICING(1000000000)

 

tPCB task[MAX_TASK_NUM];

tPCB *my_current_task =NULL;

volatileint my_need_schedule = FALSE;

 

void my_process(void);

 

void __init my_start_kernel(void)

{

    int pid =0;

    int i;

    /* Initialize process 0 */

    task[pid].pid = pid;

    task[pid].state = RUNNABLE;

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

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

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

 

    /* fork more process */

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

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

        task[i].pid = i;

        task[i].state = UNRUNNABLE;

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

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

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

    }

 

    /* start process by task[0] */

    pid =0;

    my_current_task =&task[pid];

    my_need_schedule = TRUE;

    asm volatile(

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

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

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

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

        "popl %%ebp\n\t"

        :

        :"c"(my_current_task->thread.ip),"d"(my_current_task->thread.sp)/* "c" = ecx, "d" =edx*/

    );

}

 

void my_process(void){

    int i =0;

    while(1){

        i ++;

        if(i % SLICING ==0){

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

            if(my_need_schedule == TRUE){

                my_need_schedule = FALSE;

                my_schedule();

            }

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

        }

    }

}


初始化进程队列,将所有进程添加到队列中。队列链表为单向循环链表。

 

最后介绍简单调度的核心代码,myinterupt.c。

/*

 * linux/mykernel/myinterrupt.c

 *

 * Kernel internal my_timer_handler

 *

 * Copyright (C) 2013  Mengning

 *

 */

 

 /*

 *  Headers are omitted.

 */

 

#defineCREATE_TRACE_POINTS

#include

 

#include"mypcb.h"

 

#define SLICING1000

 

extern tPCB task[MAX_TASK_NUM];

extern tPCB *my_current_task;

externvolatileint my_need_schedule;

volatileint time_count =0;

/*

 * Called by timer interrupt.

 * It runs in the name of the current process.

 */

void my_timer_handler(void)

{

    if(time_count % SLICING ==0&& my_need_schedule != TRUE){

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

        my_need_schedule = TRUE;

    }

 

    time_count ++;

    return;

}

 

void my_schedule(void){

    tPCB *next;

    tPCB *prev;

    if(my_current_task ==NULL

            || my_current_task->next ==NULL){

        printk(KERN_ERR "Error occurs in my_schedule!");

        return;

    }

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

 

    next = my_current_task->next;

    prev = my_current_task;

    if(next->state == RUNNABLE){

        /* switch to next process */

        asm volatile(

            "pushl %%ebp\n\t"      /* save prevprocess’s ebp */

            "movl %%esp,%0\n\t"    /* save prev process’s esp */

            "movl %2,%%esp\n\t"    /* restore nextprocess's ebp */

            "movl $start,%1\n\t"       /* save eip, $start就是指标号start:的代码在内存中存储的地址*/

            "pushl %3\n\t"

            "ret\n\t"              /* restore nextprocess's eip */

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

            "popl %%ebp\n\t"       /* restore next process's ebp */

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

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

        );

        my_current_task = next;

        printk(KERN_NOTICE ">>>>>> switch from process %d toprocess %d <<<<<<\n",

                prev->pid, next->pid);

    }else{

        next->state = RUNNABLE;

        my_current_task= next;

        printk(KERN_NOTICE ">>>>>> switch from process %d toprocess %d <<<<<<\n",

                prev->pid, next->pid);

        /* switch to next process */

        asm volatile(

            "pushl %%ebp\n\t"      /* save prevprocess’s ebp */

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

            "movl %2,%%esp\n\t"    /* restore nextprocess's sp to esp */

            "movl %2,%%ebp\n\t"

            /* restore next process's sp to ebp

             * because the process has neverrun

             * bp equals to sp

             */

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

            "pushl %3\n\t"         /* restore next process's eip, hereeip->my_process */

            "ret\n\t"

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

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

        );

    }

    my_need_schedule = TRUE;

}


 

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