本文共 6763 字,大约阅读时间需要 22 分钟。
2.9 初始化进程0
进程0是Linux操作系统中运行的第一个进程,也是Linux操作系统父子进程创建机制的第一个父进程。下面讲解的内容对进程0能够在主机中正常运算的影响最为重要和深远,主要包含如下三方面的内容。
1)系统先初始化进程0。进程0管理结构task_struct的母本(init_task = {INIT_TASK,})已经在代码设计阶段事先设计好了,但这并不代表进程0已经可用了,还要将进程0的task_struct中的LDT、TSS与GDT相挂接,并对GDT、task[64]以及与进程调度相关的寄存器进行初始化设置。
2)Linux 0.11作为一个现代操作系统,其最重要的标志就是能够支持多进程轮流执行,这要求进程具备参与多进程轮询的能力。系统这里对时钟中断进行设置,以便在进程0运行后,为进程0以及后续由它直接、间接创建出来的进程能够参与轮转奠定基础。
3)进程0要具备处理系统调用的能力。每个进程在运算时都可能需要与内核进行交互,而交互的端口就是系统调用程序。系统通过函数set_system_gate将system_call与IDT相挂接,这样进程0就具备了处理系统调用的能力了。这个system_call就是系统调用的总入口。
进程0只有具备了以上三种能力才能保证将来在主机中正常地运行,并将这些能力遗传给后续建立的进程。
这三点的实现都是在sched_init()函数中实现的,具体代码如下:
//代码路径:init/main.c: void main(void) { … sched_init(); … } //代码路径:kernel/sched.c: … #define LATCH (1193180/HZ) //每个时间片的振荡次数 … union task_union { // task_struct与内核栈的共用体 struct task_struct task; char stack[PAGE_SIZE]; // PAGE_SIZE是4 KB }; static union task_union init_task= {INIT_TASK,};//进程0的task_struct … //初始化进程槽task[NR_TASKS]的第一项为进程0,即task[0]为进程0占用 struct task_struct * task[NR_TASKS]= {&(init_task.task), }; … 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));//设置TSS0 set_ldt_desc(gdt?+?FIRST_LDT_ENTRY,&(init_task.task.ldt));//设置LDT0 p= gdt?+?2+FIRST_TSS_ENTRY; //从GDT的6项,即TSS1开始向上全部清零,并且将进程槽从 for(i=1;ia=p->b=0; p++; p->a=p->b=0; p++; } /* Clear NT, so that we won't have troubles with that later on */ __asm__("pushfl;andl $0xffffbfff,(%esp);popfl"); ltr(0); //重要!将TSS挂接到TR寄存器 lldt(0); //重要!将LDT挂接到LDTR寄存器 outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 *///设置定时器 outb_p(LATCH & 0xff , 0x40); /* LSB */ //每10毫秒一次时钟中断 outb(LATCH >> 8 , 0x40); /* MSB */ set_intr_gate(0x20,&timer_interrupt); //重要!设置时钟中断,进程调度的基础 outb(inb_p(0x21)&~0x01,0x21); //允许时钟中断 set_system_gate(0x80,&system_call); //重要!设置系统调用总入口 } //代码路径:include\linux\sched.h:// //嵌入汇编参看trap_init的注释 … #define FIRST_TSS_ENTRY 4 //参看图2-15中GDT的4项,即TSS0入口 #define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY?+?1)//同上,5项即LDT0入口 #define _TSS(n) ((((unsigned long) n)<<4)?+?(FIRST_TSS_ENTRY<<3)) #define _LDT(n) ((((unsigned long) n)<<4)?+?(FIRST_LDT_ENTRY<<3)) #define ltr(n) __asm__("ltr %%ax"::"a" (_TSS(n))) #define lldt(n) __asm__("lldt %%ax"::"a" (_LDT(n))) … //代码路径:include\asm\system.h: … #define set_intr_gate(n,addr) \ _set_gate(&idt[n],14,0,addr) #define set_trap_gate(n,addr) \ _set_gate(&idt[n],15,0,addr) #define set_system_gate(n,addr) \ _set_gate(&idt[n],15,3,addr) … #define _set_tssldt_desc(n,addr,type) \ //嵌入汇编参看trap_init的注释 __asm__ ("movw $104,%1\n\t" \ //将104,即1101000存入描述符的第1、2字节 "movw %%ax,%2\n\t" \ //将tss或ldt基地址的低16位存入描述符的第 //3、4字节 "rorl $16,%%eax\n\t" \ //循环右移16位,即高、低字互换 "movb %%al,%3\n\t" \ //将互换完的第1字节,即地址的第3字节存入第5字节 "movb $" type ",%4\n\t" \ //将0x89或0x82存入第6字节 "movb $0x00,%5\n\t" \ //将0x00存入第7字节 "movb %%ah,%6\n\t" \ //将互换完的第2字节,即地址的第4字节存入第8字节 "rorl $16,%%eax" \ //复原eax ::"a" (addr), "m" (*(n)), "m" (*(n?+?2)), "m" (*(n?+?4)), \ "m" (*(n?+?5)), "m" (*(n?+?6)), "m" (*(n?+?7)) \ //"m" (*(n))是gdt第n项描述符的地址开始的内存单元 //"m" (*(n?+?2)) 是gdt第n项描述符的地址向上第3字节开始的内存单元 //其余依此类推 ) //n:gdt的项值,addr:tss或ldt的地址,0x89对应tss,0x82对应ldt #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") //代码路径:include/linux/sched.h: … struct tss_struct { long back_link; /* 16 high bits zero */ long esp0; long ss0; /* 16 high bits zero */ long esp1; long ss1; /* 16 high bits zero */ long esp2; long ss2; /* 16 high bits zero */ long cr3; long eip; long eflags; long eax,ecx,edx,ebx; long esp; long ebp; long esi; long edi; long es; /* 16 high bits zero */ long cs; /* 16 high bits zero */ long ss; /* 16 high bits zero */ long ds; /* 16 high bits zero */ long fs; /* 16 high bits zero */ long gs; /* 16 high bits zero */ long ldt; /* 16 high bits zero */ long trace_bitmap; /* bits: trace 0, bitmap 16-31 */ struct i387_struct i387; }; struct task_struct { /* these are hardcoded - don't touch */ long state; /* -1 unrunnable, 0 runnable, >0 stopped */ long counter; long priority; long signal; struct sigaction sigaction[32]; long blocked; /* bitmap of masked signals */ /* various fields */ int exit_code; unsigned long start_code,end_code,end_data,brk,start_stack; long pid,father,pgrp,session,leader; unsigned short uid,euid,suid; unsigned short gid,egid,sgid; long alarm; long utime,stime,cutime,cstime,start_time; unsigned short used_math; /* file system info */ int tty; /* -1 if no tty, so it must be signed */ unsigned short umask; struct m_inode * pwd; struct m_inode * root; struct m_inode * executable; unsigned long close_on_exec; struct file * filp[NR_OPEN]; /* ldt for this task 0 - zero 1 - cs 2 - ds&ss */ struct desc_struct ldt[3]; /* tss for this task */ struct tss_struct tss; }; /*进程0的task_struct * INIT_TASK is used to set up the first task table, touch at * your own risk!. Base=0, limit=0x9ffff (=640kB) */ #define INIT_TASK \ /* state etc */ { 0,15,15, \ //就绪态,15个时间片 /* signals */ 0,{ {},},0, \ /* ec,brk... */ 0,0,0,0,0,0, \ /* pid etc.. */ 0,-1,0,0,0, \ //进程号0 /* uid etc */ 0,0,0,0,0,0, \ /* alarm */ 0,0,0,0,0,0, \ /* math */ 0, \ /* fs info */ -1,0022,NULL,NULL,NULL,0, \ /* filp */ {NULL,}, \ { \ {0,0}, \ /* ldt */ {0x9f,0xc0fa00}, \ {0x9f,0xc0f200}, \ }, \ /*tss*/ {0,PAGE_SIZE?+?(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\ 0,0,0,0,0,0,0,0, \ //eflags的值,决定了cli这类指令只能在0特权级使用 0,0,0x17,0x17,0x17,0x17,0x17,0x17, \ _LDT(0),0x80000000, \ {} \ }, \ }
转载地址:http://dyxbn.baihongyu.com/