这一部分来看 main 中调用的 time_init 与 sched_init 函数。
首先是 time_init,其定义在 main.c 第 76 行,CMOS_READ 与 BCD_TO_BIN 在 main.c 的注释里已经提及,该函数在最后调用了 kernel_mktime 来计算从 1970 年 1 月 1 日 0 时至现在的开机时间,kernel_mktime 定义在 kernel/mktime.c 中
mktime.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include <time.h>
#define MINUTE 60 #define HOUR (60*MINUTE) #define DAY (24*HOUR) #define YEAR (365*DAY)
static int month[12] = { 0, DAY*(31), DAY*(31+29), DAY*(31+29+31), DAY*(31+29+31+30), DAY*(31+29+31+30+31), DAY*(31+29+31+30+31+30), DAY*(31+29+31+30+31+30+31), DAY*(31+29+31+30+31+30+31+31), DAY*(31+29+31+30+31+30+31+31+30), DAY*(31+29+31+30+31+30+31+31+30+31), DAY*(31+29+31+30+31+30+31+31+30+31+30) };
long kernel_mktime(struct tm * tm) { long res; int year; if (tm->tm_year < 70 ) tm->tm_year += 100; year = tm->tm_year - 70; res = YEAR*year + DAY*((year+1)/4); res += month[tm->tm_mon]; if (tm->tm_mon>1 && ((year+2)%4)) res -= DAY; res += DAY*(tm->tm_mday-1); res += HOUR*tm->tm_hour; res += MINUTE*tm->tm_min; res += tm->tm_sec; return res; }
|
sched.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| #include <linux/sched.h> #include <linux/kernel.h> #include <linux/sys.h> #include <linux/fdreg.h> #include <asm/system.h> #include <asm/io.h> #include <asm/segment.h>
#include <signal.h>
#define _S(nr) (1<<((nr)-1)) #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
void show_task(int nr,struct task_struct * p) { int i,j = 4096-sizeof(struct task_struct);
printk("%d: pid=%d, state=%d, ",nr,p->pid,p->state); i=0; while (i<j && !((char *)(p+1))[i]) i++; printk("%d (of %d) chars free in kernel stack\n\r",i,j); }
void show_stat(void) { int i;
for (i=0;i<NR_TASKS;i++) if (task[i]) show_task(i,task[i]); }
#define LATCH (1193180/HZ)
extern void mem_use(void);
extern int timer_interrupt(void); extern int system_call(void);
union task_union { struct task_struct task; char stack[PAGE_SIZE]; };
static union task_union init_task = {INIT_TASK,}; long volatile jiffies=0; long startup_time=0; struct task_struct *current = &(init_task.task); struct task_struct *last_task_used_math = NULL;
struct task_struct * task[NR_TASKS] = {&(init_task.task), }; long user_stack [ PAGE_SIZE>>2 ] ;
struct { long * a; short b; } stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
void math_state_restore() { if (last_task_used_math == current) return; __asm__("fwait"); if (last_task_used_math) { __asm__("fnsave %0"::"m" (last_task_used_math->tss.i387)); } last_task_used_math=current; if (current->used_math) { __asm__("frstor %0"::"m" (current->tss.i387)); } else { __asm__("fninit"::); current->used_math=1; } }
|
接下来是进程调度函数,schedule 会选出 counter 值最大的任务作为 next 任务来运行。如果系统中只有 0 号进程在运行时,则切换到 0 号进程,其又执行 pause 来调用 schedule 函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| void schedule(void) { int i,next,c; struct task_struct ** p;
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; }
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) 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); }
|
switch_to 定义在 include/linux/sched.h,_TSS 也定义在该文件中。
ljmp 长跳转的指令格式为:ljmp 16 位段选择子:32 位偏移值,这里定义的 _tmp.a 就是 32 位偏移值,_tmp.b 低 2 字节是 16 位段选择子(高 2 字节不用)。跳转到 TSS 描述符的选择子会导致任务切换到该 TSS 对应的进程,且对于造成任务切换的长跳转 _tmp.a 的值无用,因此这里没有对 _tmp.a 进行赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #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])); \ }
#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
|
继续看 sched.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| int sys_pause(void) { current->state = TASK_INTERRUPTIBLE; schedule(); return 0; }
void sleep_on(struct task_struct **p) { struct task_struct *tmp;
if (!p) return; if (current == &(init_task.task)) panic("task[0] trying to sleep"); tmp = *p; *p = current; current->state = TASK_UNINTERRUPTIBLE; schedule(); if (tmp) tmp->state=0; }
void interruptible_sleep_on(struct task_struct **p) { struct task_struct *tmp;
if (!p) return; if (current == &(init_task.task)) panic("task[0] trying to sleep"); tmp=*p; *p=current; repeat: current->state = TASK_INTERRUPTIBLE; schedule(); if (*p && *p != current) { (**p).state=0; goto repeat; } *p=NULL; if (tmp) tmp->state=0; }
void wake_up(struct task_struct **p) { if (p && *p) { (**p).state=0; *p=NULL; } }
|
从 201 行到 262 行的几个函数用于软驱定时处理,等看到块设备部分再记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
| #define TIME_REQUESTS 64
static struct timer_list { long jiffies; void (*fn)(); struct timer_list * next; } timer_list[TIME_REQUESTS], * next_timer = NULL;
void add_timer(long jiffies, void (*fn)(void)) { struct timer_list * p;
if (!fn) return; cli(); if (jiffies <= 0) (fn)(); else { for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++) if (!p->fn) break; if (p >= timer_list + TIME_REQUESTS) panic("No more time requests free"); p->fn = fn; p->jiffies = jiffies; p->next = next_timer; next_timer = p; while (p->next && p->next->jiffies < p->jiffies) { p->jiffies -= p->next->jiffies; fn = p->fn; p->fn = p->next->fn; p->next->fn = fn; jiffies = p->jiffies; p->jiffies = p->next->jiffies; p->next->jiffies = jiffies; p = p->next; } } sti(); }
void do_timer(long cpl) { extern int beepcount; extern void sysbeepstop(void);
if (beepcount) if (!--beepcount) sysbeepstop();
if (cpl) current->utime++; else current->stime++;
if (next_timer) { next_timer->jiffies--; while (next_timer && next_timer->jiffies <= 0) { void (*fn)(void); fn = next_timer->fn; next_timer->fn = NULL; next_timer = next_timer->next; (fn)(); } } if (current_DOR & 0xf0) do_floppy_timer(); if ((--current->counter)>0) return; current->counter=0; if (!cpl) return; schedule(); }
int sys_alarm(long seconds) { int old = current->alarm;
if (old) old = (old - jiffies) / HZ; current->alarm = (seconds>0)?(jiffies+HZ*seconds):0; return (old); }
int sys_getpid(void) { return current->pid; }
int sys_getppid(void) { return current->father; }
int sys_getuid(void) { return current->uid; }
int sys_geteuid(void) { return current->euid; }
int sys_getgid(void) { return current->gid; }
int sys_getegid(void) { return current->egid; }
int sys_nice(long increment) { if (current->priority-increment>0) current->priority -= increment; return 0; }
|
最后是 main.c 中调用的 sched_init
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| void sched_init(void) { int i; struct desc_struct * p; if (sizeof(struct sigaction) != 16) panic("Struct sigaction MUST be 16 bytes"); set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss)); set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt)); p = gdt+2+FIRST_TSS_ENTRY; for(i=1;i<NR_TASKS;i++) { task[i] = NULL; p->a=p->b=0; p++; p->a=p->b=0; p++; }
__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); ltr(0); lldt(0); outb_p(0x36,0x43); outb_p(LATCH & 0xff , 0x40); outb(LATCH >> 8 , 0x40); set_intr_gate(0x20,&timer_interrupt); outb(inb_p(0x21)&~0x01,0x21); set_system_gate(0x80,&system_call); }
#define _set_tssldt_desc(n,addr,type) \ __asm__ ("movw $104,%1\n\t" \ "movw %%ax,%2\n\t" \ "rorl $16,%%eax\n\t" \ "movb %%al,%3\n\t" \ "movb $" type ",%4\n\t" \ "movb $0x00,%5\n\t" \ "movb %%ah,%6\n\t" \ "rorl $16,%%eax" \ ::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \ "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \ )
#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x89") #define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x82")
|