博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
2.9 初始化进程0
阅读量:3676 次
发布时间:2019-05-21

本文共 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;i
a=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/

你可能感兴趣的文章
qt中调用打印机打印字符
查看>>
labview下UDP通信
查看>>
stm32f4中通过dma采集adc
查看>>
智能家居系统解决方案
查看>>
配置qt + wince + vs2005环境
查看>>
BMP数据转换为RGB565
查看>>
STM32驱动W5100实现udp通信
查看>>
STM32单片机PWM输出测试
查看>>
QML与C++交互 在qml中使用QSqlQueryModel显示数据库数据
查看>>
Linux下基于QT串口编程测试一
查看>>
Ubuntu下BeagleBone的USB串口驱动安装 转
查看>>
STM32的PWM输入模式设置并用DMA接收数据
查看>>
windows下mongodb设置用户名密码 用python连接
查看>>
zigbee学习 示例程序SampleApp中通讯流程
查看>>
QT在linux环境下读取和设置系统时间
查看>>
单片机上使用TEA加密通信
查看>>
空间3点投影定位算法
查看>>
STM32F4的IO设置测试
查看>>
配置qt5 1 for android的环境 转载
查看>>
利用数组实现队列操作
查看>>