Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> Linux0.11內核--進程調度分析之1.初始化,linux0.11內核

Linux0.11內核--進程調度分析之1.初始化,linux0.11內核

編輯:關於android開發

Linux0.11內核--進程調度分析之1.初始化,linux0.11內核


【版權所有,轉載請注明出處。出處:http://www.cnblogs.com/joey-hua/p/5596746.html 】

首先看main.c裡的初始化函數main函數裡面有個函數是對進程調度的初始化,sched_init()函數,次函數在sched.c中實現:

// 調度程序的初始化子程序。
void sched_init (void)
{
  int i;
  struct desc_struct *p;	// 描述符表結構指針。

  if (sizeof (struct sigaction) != 16)	// sigaction 是存放有關信號狀態的結構。
    panic ("Struct sigaction MUST be 16 bytes");
  // 設置初始任務(任務0)的任務狀態段描述符和局部數據表描述符(include/asm/system.h,65)。
  set_tss_desc (gdt + FIRST_TSS_ENTRY, &(init_task.task.tss));
  set_ldt_desc (gdt + FIRST_LDT_ENTRY, &(init_task.task.ldt));
  // 清任務數組和描述符表項(注意i=1 開始,所以初始任務的描述符還在)。
  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++;
    }
  /* Clear NT, so that we won't have troubles with that later on */
  /* 清除標志寄存器中的位NT,這樣以後就不會有麻煩 */
  // NT 標志用於控制程序的遞歸調用(Nested Task)。當NT 置位時,那麼當前中斷任務執行
  // iret 指令時就會引起任務切換。NT 指出TSS 中的back_link 字段是否有效。
  __asm__ ("pushfl ; andl $0xffffbfff,(%esp) ; popfl");	// 復位NT 標志。
  ltr (0);			// 將任務0 的TSS 加載到任務寄存器tr。
  lldt (0);			// 將局部描述符表加載到局部描述符表寄存器。
  // 注意!!是將GDT 中相應LDT 描述符的選擇符加載到ldtr。只明確加載這一次,以後新任務
  // LDT 的加載,是CPU 根據TSS 中的LDT 項自動加載。
  // 下面代碼用於初始化8253 定時器。
  outb_p (0x36, 0x43);		/* binary, mode 3, LSB/MSB, ch 0 */
  outb_p (LATCH & 0xff, 0x40);	/* LSB */// 定時值低字節。
  outb (LATCH >> 8, 0x40);	/* MSB */// 定時值高字節。
  // 設置時鐘中斷處理程序句柄(設置時鐘中斷門)。
  set_intr_gate (0x20, &timer_interrupt);
  // 修改中斷控制器屏蔽碼,允許時鐘中斷。
  outb (inb_p (0x21) & ~0x01, 0x21);
  // 設置系統調用中斷門。
  set_system_gate (0x80, &system_call);
}

首先初始化任務0的TTS,FIRST_TSS_ENTRY為4,表示在描述符表的索引是4。因為gdt是desc_struct類型為8個字節,剛好是一個描述符的長度,所以這裡的gdt+4可以理解為gdt[4]。剛好對應的是TSS0。

描述符表的內容如下:

0-沒有用nul,1-代碼段cs,2-數據段ds,3-系統段syscall,4-任務狀態段TSS0,5-局部表LTD0,6-任務狀態段TSS1,等。

//// 在全局表中設置任務狀態段/局部表描述符。
// 參數:n - 在全局表中描述符項n 所對應的地址;addr - 狀態段/局部表所在內存的基地址。
// type - 描述符中的標志類型字節。
// %0 - eax(地址addr);%1 - (描述符項n 的地址);%2 - (描述符項n 的地址偏移2 處);
// %3 - (描述符項n 的地址偏移4 處);%4 - (描述符項n 的地址偏移5 處);
// %5 - (描述符項n 的地址偏移6 處);%6 - (描述符項n 的地址偏移7 處);
#define _set_tssldt_desc(n,addr,type) \
__asm__ ( "movw $104,%1\n\t" \	// 將TSS 長度放入描述符長度域(第0-1 字節)。
"movw %%ax,%2\n\t" \		// 將基地址的低字放入描述符第2-3 字節。
  "rorl $16,%%eax\n\t" \	// 將基地址高字移入ax 中。
  "movb %%al,%3\n\t" \		// 將基地址高字中低字節移入描述符第4 字節。
  "movb $" type ",%4\n\t" \	// 將標志類型字節移入描述符的第5 字節。
  "movb $0x00,%5\n\t" \		// 描述符的第6 字節置0。
  "movb %%ah,%6\n\t" \		// 將基地址高字中高字節移入描述符第7 字節。
  "rorl $16,%%eax" \		// eax 清零。
  ::"a" (addr), "m" (*(n)), "m" (*(n + 2)), "m" (*(n + 4)),
  "m" (*(n + 5)), "m" (*(n + 6)), "m" (*(n + 7)))
//// 在全局表中設置任務狀態段描述符。
// n - 是該描述符的指針(向量);addr - 是描述符中的基地址值。任務狀態段描述符的類型是0x89。
#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr, "0x89")
//// 在全局表中設置局部表描述符。
// n - 是該描述符的指針(向量);addr - 是描述符中的基地址值。局部表描述符的類型是0x82。
#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr, "0x82")

因為TSS最小尺寸是104字節,所以第一句是把長度104賦值給TTS0描述符的第0-1字節,描述符的格式如 描述符格式 ;第二句是把ax也就是addr也就是任務聯合的第一個任務的tss地址賦值給*(n+2)處,因為是movw字,所以也就是描述符的第2-3字節處。接下來填充第4字節,然後把類型type填充到第5字節,最後把剩余的字節填充。

初始化任務0的ldt的方法也是類似,好了,這裡初始化完成任務0的TSS和LDT。

sched_init接下來是清空除了任務0的所有任務的數組和對應的描述符,這個好理解。

下面是加載任務0的TSS到任務寄存器tr,加載ldt到局部描述符表寄存器ldtr,sched.h:

/*
* 尋找第1 個TSS 在全局表中的入口。0-沒有用nul,1-代碼段cs,2-數據段ds,3-系統段syscall
* 4-任務狀態段TSS0,5-局部表LTD0,6-任務狀態段TSS1,等。見head.s
*/
// 全局表中第1 個任務狀態段(TSS)描述符的選擇符索引號。
#define FIRST_TSS_ENTRY 4
// 全局表中第1 個局部描述符表(LDT)描述符的選擇符索引號。
#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
// 宏定義,計算在全局表中第n 個任務的TSS 描述符的索引號(選擇符)。
#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
// 宏定義,計算在全局表中第n 個任務的LDT 描述符的索引號。
#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
// 宏定義,加載第n 個任務的任務寄存器tr。
#define ltr(n) __asm__( "ltr %%ax":: "a" (_TSS(n)))
// 宏定義,加載第n 個任務的局部描述符表寄存器ldtr。
#define lldt(n) __asm__( "lldt %%ax":: "a" (_LDT(n)))

LDTR局部描述符寄存器:16位,高13為存放LDT在GDT中的索引值。

所以FIRST_LDT_ENTRY要左移3位,(((unsigned long) n)<<4)不太好理解,因為先要去掉左移的3位,所以實際值是n<<1,也就是2n。最終的值相當於FIRST_LDT_ENTRY+2n。這樣就好理解了,因為每個任務都有兩個描述符項。

這裡要注意:只明確加載這一次,以後新任務LDT 的加載,是CPU 根據TSS 中的LDT 項自動加載。

接下來是初始化定時器,沒什麼好說的。

接下來兩句最關鍵了,進程調度的引發的誘因就是在下面初始化的:

  // 設置時鐘中斷處理程序句柄(設置時鐘中斷門)。
  set_intr_gate (0x20, &timer_interrupt);
  // 修改中斷控制器屏蔽碼,允許時鐘中斷。
  outb (inb_p (0x21) & ~0x01, 0x21);

第一句在系統調用機制分析中有講到,是設置中斷門的,所以這裡就是把system_call.s中的函數timer_interrupt和中斷號0x20關聯起來,下面一句代碼參考 時鐘中斷 開啟了時鐘中斷也就是0x20號中斷,也就是說時鐘每滴答(10ms)一下就會調用timer_interrupt函數。

到這裡,進程調度的初始化就結束了。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved