完成一个简单的时间片轮转多道程序内核代码

实验要求

完成一个简单的时间片轮转多道程序内核代码,代码见视频中或从mykernel找。仔细分析进程的启动和进程的切换机制,总结部分需要阐明自己对“操作系统是如何工作的”理解。

实验步骤

  1. 使用实验楼的虚拟机打开shell,完成以下指令:
# 注意路径是区分大小的
# 切换目录至linux源码目录;因为我们的小kernel需要借助它的文件
$ cd ~/LinuxKernel/linux-3.9.4 

# 这里为什么要先删除mykernel目录:
$ rm -rf mykernel
# 参数p1表示去掉前第一个路径
$ patch -p1 < ../mykernel_for_linux3.9.4sc.patch

$ make allnoconfig

# 编译内核请耐心等待
$ make

$ qemu -kernel arch/x86/boot/bzImage

这里为什么要先使用这个命令:

$ rm -rf mykernel

笔者猜测可能和下面这个补丁有关系:

patch -p1 < ../mykernel_for_linux3.9.4sc.patch

那我们就看看吧,这个补丁里面到底卖的什么瓜,用gedit看看补丁当中的内容。要使我们的mykernel借助linux-3.9.4运行起来,我们必须对一些内容进行修改,这些修改我们通过打补丁的方式实现:

Linux patch命令用于修补文件。
patch指令让用户利用设置修补文件的方式,修改,更新原始文件。倘若一次仅修改一个文件,可直接在指令列中下达指令依序执行。如果配合修补文件的方式则能一次修补大批文件,这也是Linux系统核心的升级方法之一。

在diff命令中已经明确地给出了文件路径,一看即知,不再赘述。前面带+号的都是增添的内容,这一部分代码增加了头文件,一个自己的timer handler,还有一个printk。

diff -Naur linux-3.9.4/arch/x86/kernel/time.c linux-3.9.4.new/arch/x86/kernel/time.c
--- linux-3.9.4/arch/x86/kernel/time.c	2013-05-24 11:45:59.000000000 -0700
+++ linux-3.9.4.new/arch/x86/kernel/time.c	2013-06-25 21:39:34.641299852 -0700
@@ -13,6 +13,7 @@
 #include <linux/interrupt.h>
 #include <linux/i8253.h>
 #include <linux/time.h>
+#include <linux/timer.h>
 #include <linux/export.h>
 
 #include <asm/vsyscall.h>
@@ -57,6 +58,7 @@
 static irqreturn_t timer_interrupt(int irq, void *dev_id)
 {
 	global_clock_event->event_handler(global_clock_event);
+    my_timer_handler();
 	return IRQ_HANDLED;
 }
 
@@ -68,6 +70,7 @@
 
 void __init setup_default_timer_irq(void)
 {
+    printk(KERN_NOTICE "timer interrupt setup\n"); 
 	setup_irq(0, &irq0);
 }

这里很明显,start_kernel头文件当中增加了一个函数声明:

diff -Naur linux-3.9.4/include/linux/start_kernel.h linux-3.9.4.new/include/linux/start_kernel.h
--- linux-3.9.4/include/linux/start_kernel.h	2013-05-24 11:45:59.000000000 -0700
+++ linux-3.9.4.new/include/linux/start_kernel.h	2013-06-25 19:18:58.396722448 -0700
@@ -8,5 +8,6 @@
    up something else. */
 
 extern asmlinkage void __init start_kernel(void);
+extern void __init my_start_kernel(void);
 
 #endif /* _LINUX_START_KERNEL_H */

把自己的timer handler声明加进去:

diff -Naur linux-3.9.4/include/linux/timer.h linux-3.9.4.new/include/linux/timer.h
--- linux-3.9.4/include/linux/timer.h	2013-05-24 11:45:59.000000000 -0700
+++ linux-3.9.4.new/include/linux/timer.h	2013-06-25 21:28:37.609026644 -0700
@@ -251,6 +251,8 @@
 
 extern void init_timers(void);
 extern void run_local_timers(void);
+extern void my_timer_handler(void);
+
 struct hrtimer;
 extern enum hrtimer_restart it_real_fn(struct hrtimer *);
 

注意了,开始修改main.c了:

diff -Naur linux-3.9.4/init/main.c linux-3.9.4.new/init/main.c
--- linux-3.9.4/init/main.c	2013-05-24 11:45:59.000000000 -0700
+++ linux-3.9.4.new/init/main.c	2013-06-25 20:16:17.452763103 -0700
@@ -574,7 +574,6 @@
 	console_init();
 	if (panic_later)
 		panic(panic_later, panic_param);
-
 	lockdep_info();
 
 	/*
@@ -641,6 +640,7 @@
 
 	ftrace_init();
 
+    my_start_kernel();
 	/* Do the rest non-__init'ed, we're now alive */
 	rest_init();
 }

然后是Makefile文件:

diff -Naur linux-3.9.4/Makefile linux-3.9.4.new/Makefile
--- linux-3.9.4/Makefile	2013-05-24 11:45:59.000000000 -0700
+++ linux-3.9.4.new/Makefile	2013-06-25 19:16:00.177364039 -0700
@@ -733,7 +733,7 @@
 
 
 ifeq ($(KBUILD_EXTMOD),)
-core-y		+= kernel/ mm/ fs/ ipc/ security/ crypto/ block/
+core-y		+= kernel/ mm/ fs/ ipc/ security/ crypto/ block/ mykernel/
 
 vmlinux-dirs	:= $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
 		     $(core-y) $(core-m) $(drivers-y) $(drivers-m) \
diff -Naur linux-3.9.4/mykernel/Makefile linux-3.9.4.new/mykernel/Makefile
--- linux-3.9.4/mykernel/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-3.9.4.new/mykernel/Makefile	2013-06-25 21:40:24.174030881 -0700
@@ -0,0 +1,6 @@
+#
+# Makefile for the linux mykernel.
+#
+
+obj-y     = mymain.o myinterrupt.o
+

myinterrupt.c文件

diff -Naur linux-3.9.4/mykernel/myinterrupt.c linux-3.9.4.new/mykernel/myinterrupt.c
--- linux-3.9.4/mykernel/myinterrupt.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-3.9.4.new/mykernel/myinterrupt.c	2013-06-25 21:45:38.408915841 -0700
@@ -0,0 +1,47 @@
+/*
+ *  linux/mykernel/myinterrupt.c
+ *
+ *  Kernel internal my_timer_handler
+ *
+ *  Copyright (C) 2013  Mengning
+ *
+ */
+#include <linux/kernel_stat.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/percpu.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/pid_namespace.h>
+#include <linux/notifier.h>
+#include <linux/thread_info.h>
+#include <linux/time.h>
+#include <linux/jiffies.h>
+#include <linux/posix-timers.h>
+#include <linux/cpu.h>
+#include <linux/syscalls.h>
+#include <linux/delay.h>
+#include <linux/tick.h>
+#include <linux/kallsyms.h>
+#include <linux/irq_work.h>
+#include <linux/sched.h>
+#include <linux/sched/sysctl.h>
+#include <linux/slab.h>
+
+#include <asm/uaccess.h>
+#include <asm/unistd.h>
+#include <asm/div64.h>
+#include <asm/timex.h>
+#include <asm/io.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/timer.h>
+
+/*
+ * Called by timer interrupt.
+ */
+void my_timer_handler(void)
+{
+	printk(KERN_NOTICE "\n>>>>>>>>>>>>>>>>>my_timer_handler here<<<<<<<<<<<<<<<<<<\n\n");
+}

mymain.c文件

diff -Naur linux-3.9.4/mykernel/mymain.c linux-3.9.4.new/mykernel/mymain.c
--- linux-3.9.4/mykernel/mymain.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-3.9.4.new/mykernel/mymain.c	2013-06-25 21:44:59.489317439 -0700
@@ -0,0 +1,93 @@
+/*
+ *  linux/mykernel/mymain.c
+ *
+ *  Kernel internal my_start_kernel
+ *
+ *  Copyright (C) 2013  Mengning
+ *
+ */
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
+#include <linux/stackprotector.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/initrd.h>
+#include <linux/bootmem.h>
+#include <linux/acpi.h>
+#include <linux/tty.h>
+#include <linux/percpu.h>
+#include <linux/kmod.h>
+#include <linux/vmalloc.h>
+#include <linux/kernel_stat.h>
+#include <linux/start_kernel.h>
+#include <linux/security.h>
+#include <linux/smp.h>
+#include <linux/profile.h>
+#include <linux/rcupdate.h>
+#include <linux/moduleparam.h>
+#include <linux/kallsyms.h>
+#include <linux/writeback.h>
+#include <linux/cpu.h>
+#include <linux/cpuset.h>
+#include <linux/cgroup.h>
+#include <linux/efi.h>
+#include <linux/tick.h>
+#include <linux/interrupt.h>
+#include <linux/taskstats_kern.h>
+#include <linux/delayacct.h>
+#include <linux/unistd.h>
+#include <linux/rmap.h>
+#include <linux/mempolicy.h>
+#include <linux/key.h>
+#include <linux/buffer_head.h>
+#include <linux/page_cgroup.h>
+#include <linux/debug_locks.h>
+#include <linux/debugobjects.h>
+#include <linux/lockdep.h>
+#include <linux/kmemleak.h>
+#include <linux/pid_namespace.h>
+#include <linux/device.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/idr.h>
+#include <linux/kgdb.h>
+#include <linux/ftrace.h>
+#include <linux/async.h>
+#include <linux/kmemcheck.h>
+#include <linux/sfi.h>
+#include <linux/shmem_fs.h>
+#include <linux/slab.h>
+#include <linux/perf_event.h>
+#include <linux/file.h>
+#include <linux/ptrace.h>
+#include <linux/blkdev.h>
+#include <linux/elevator.h>
+
+#include <asm/io.h>
+#include <asm/bugs.h>
+#include <asm/setup.h>
+#include <asm/sections.h>
+#include <asm/cacheflush.h>
+
+#ifdef CONFIG_X86_LOCAL_APIC
+#include <asm/smp.h>
+#endif
+
+void __init my_start_kernel(void)
+{
+    int i = 0;
+    while(1)
+    {
+        i++;
+        if(i%100000 == 0)
+            printk(KERN_NOTICE "my_start_kernel here  %d \n",i);
+            
+    }
+}

README:

diff -Naur linux-3.9.4/mykernel/README.md linux-3.9.4.new/mykernel/README.md
--- linux-3.9.4/mykernel/README.md	1969-12-31 16:00:00.000000000 -0800
+++ linux-3.9.4.new/mykernel/README.md	2013-06-25 21:52:52.467808733 -0700
@@ -0,0 +1,3 @@
+mykernel
+==========
+It is a platform to write your own OS kernel,its based on Linux Kernel 3.9.4 source code.

这下大概对编译内核的原理有了初步的理解,通过补丁的方式对内核的源代码进行了一点点小的修改,主体部分还是要借助linux-3.9.4的力量啊!

回到问题所在,为什么要先删除mykernel文件夹呢?可能是为了给打补丁铺路吧,如果不事先rm文件夹,就会出现以下画面:
在这里插入图片描述mykernel报错了,具体的原因还是懵懵懂懂,希望后面深入学习了内核,可以回来解决这个问题。

  1. 分析进程启动和切换机制
  • mypcb.h 进程控制块
/*
 *  linux/mykernel/mypcb.h
 *
 *  Kernel internal PCB types
 *
 *  Copyright (C) 2013  Mengning
 *
 */

#define MAX_TASK_NUM        4
#define KERNEL_STACK_SIZE   1024*2 # unsigned long
//define 最大任务数为4;kernel stack大小;
/* CPU-specific state of this task */
struct Thread {//存储ip和sp
    unsigned long		ip;//eip保存
    unsigned long		sp;//esp保存
};

typedef struct PCB{
    int pid;//进程的ID
    //进程的状态
    volatile long state;/* -1 unrunnable, 0 runnable, >0 stopped */
    unsigned long 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);//调度器

  • mymain.c
/*
 *  linux/mykernel/mymain.c
 *
 *  Kernel internal my_start_kernel
 *
 *  Copyright (C) 2013  Mengning
 *
 */
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/tty.h>
#include <linux/vmalloc.h>


#include "mypcb.h"

tPCB task[MAX_TASK_NUM];	//pcb的数组;
tPCB * my_current_task = NULL;	//当前task的指针
volatile int my_need_sched = 0;	//是不是需要调度

void my_process(void);


void __init my_start_kernel(void)
{
    int pid = 0;
    int i;
    /* Initialize process 0*/
    task[pid].pid = pid;//0号进程
    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];///esp 栈顶;
    task[pid].next = &task[pid];//指向自己;当前只有0号进程;
    /*fork more process */
    //fork其他的进程;
    for(i=1;i<MAX_TASK_NUM;i++)
    {
        memcpy(&task[i],&task[0],sizeof(tPCB));//copy0号进程
        task[i].pid = i;
	//*(&task[i].stack[KERNEL_STACK_SIZE-1] - 1) = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-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];
    }
    /* start process 0 by task[0] */
    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 */
    	: 
    	: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)	/* input c or d mean %ecx/%edx*/
	);
	//0号进程正式启动了
} 

int i = 0;

void my_process(void)
{    //所有的进程都是执行这个函数;
    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);
        }     
    }
}
  • myinterrupt.c
/*
 *  linux/mykernel/myinterrupt.c
 *
 *  Kernel internal my_timer_handler
 *
 *  Copyright (C) 2013  Mengning
 *
 */
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/tty.h>
#include <linux/vmalloc.h>

#include "mypcb.h"

extern tPCB task[MAX_TASK_NUM];
extern tPCB * my_current_task;
extern volatile int my_need_sched;
volatile int time_count = 0;//时间计数

/*
 * Called by timer interrupt.
 * it runs in the name of current running process,
 * so it use kernel stack of current running process
 */
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;  	
}

void my_schedule(void)
{
    tPCB * next;
    tPCB * prev;//当前进程

    if(my_current_task == NULL 
        || my_current_task->next == NULL)
    {	//当前进程和next进程为空就要报错的;
    	return;
    }
    printk(KERN_NOTICE ">>>my_schedule<<<\n");
    /* schedule */
    next = my_current_task->next;//当前进程的下一个进程赋给next;
    prev = my_current_task;//当前进程;
    if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
    {        
    	my_current_task = next; 
    	printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);  
    	/* switch to next process */
    	//进程切换的关键代码
    	asm volatile(	
        	"pushl %%ebp\n\t" 	    /* save ebp当前进程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)
    	); 
    }  
    return;	
}

用个手绘图理解一下文件的交叉关系:
在这里插入图片描述

结语

到这里才对编译内核有了一点点自己的理解,以上是自己作为一个Linux内核初学者的所想所悟的笔记,有什么错误希望大家能够在评论区指正哟(^U^)ノ~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值