实验要求
完成一个简单的时间片轮转多道程序内核代码,代码见视频中或从mykernel找。仔细分析进程的启动和进程的切换机制,总结部分需要阐明自己对“操作系统是如何工作的”理解。
实验步骤
- 使用实验楼的虚拟机打开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报错了,具体的原因还是懵懵懂懂,希望后面深入学习了内核,可以回来解决这个问题。
- 分析进程启动和切换机制
- 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^)ノ~