<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 303  文章 - 883  trackbacks - 0
    <2008年2月>
    272829303112
    3456789
    10111213141516
    17181920212223
    2425262728291
    2345678

    歡迎光臨! 
    閑聊 QQ:1074961813

    隨筆分類(357)

    我管理的群

    公共blog

    • n維空間
    • Email : java3d@126.com 群 : 12999758

    參與管理的論壇

    好友的blog

    我的其他blog

    朋友的網站

    搜索

    •  

    最新評論

    自從進入安全模式之后,CPU的尋址能力從1M一下子擴展到4G,物理地址=段基址(CS)*16+偏移地址(IP)的日子一去不復返了;可以想象,從這個時候的內存的初始化也就成為一個關鍵步驟。那么、內核究竟是怎么做的呢?
    下面的代碼就是這個時候內核代碼,

    .
    #define
     RAMDISK 32 /*這個定義是我特意加上去的,原代碼中無此定義*/
    #define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)
    #define DRIVE_INFO (*(struct drive_info *)0x90080)
    .
    void main (void)        
    {                
      ROOT_DEV 
    =
     ORIG_ROOT_DEV;
      drive_info 
    =
     DRIVE_INFO;
      memory_end 
    = (1 << 20+ (EXT_MEM_K << 10
    );
      memory_end 
    &= 0xfffff000
    ;
      
    if (memory_end > 16 * 1024 * 1024
    )       
           memory_end 
    = 16 * 1024 * 1024
    ;

      
    if (memory_end > 12 * 1024 * 1024
    )
          buffer_memory_end 
    = 4 * 1024 * 1024
    ;
      
    else if (memory_end > 6 * 1024 * 1024
    )
          buffer_memory_end 
    = 2 * 1024 * 1024
    ;
      
    else

        buffer_memory_end 
    = 1 * 1024 * 1024;
      main_memory_start 
    =
     buffer_memory_end;
      
      #ifdef RAMDISK
      main_memory_start 
    += rd_init(main_memory_start, RAMDISK * 1024
    );
      
    #endif

       mem_init (main_memory_start, memory_end);
    .

    它完成了內存的分頁、分配工作。其中調用了其他的代碼,且看他是如何工作的:

     ROOT_DEV = ORIG_ROOT_DEV; /*   #define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)    */
     
    drive_info = DRIVE_INFO; /*    (*(struct drive_info *)0x90080)      */
     
    他們的意思非常想近
    0x901FC0x90080 所在地址開始長度unsigned shortstruct drive_info長度的內存空間內容,已在前面在系統引導代碼/boot/setup.s 中已做了設置

    具體是:

    0x90080地址開始存放的是兩個硬盤參數表,0x90080~0x9008f放了第一個硬盤的參數;0x90090~0x9009f存放了第二個硬盤的參數表,具體代碼

    !第一個硬盤
    mov ax,#
    0x0000

    mov ds,ax
    lds si,[
    4*0x41! 取中斷向量0x41 的值,也即hd0 參數表的地址 ds:si
    mov ax,#INITSEG
    mov es,ax
    mov di,#
    0x0080 ! 傳輸的目的地址: 0x9000:0x0080 =
     es:di
    mov cx,#
    0x10 !
     循環0x10次沒次一個字節,共傳輸0x10 字節。
    rep
    movsb  

    !
    第二個硬盤

    mov ax,#
    0x0000

    mov ds,ax
    lds si,[
    4*0x46! 取中斷向量0x46 的值,也即hd1 參數表的地址=ds:si
    mov ax,#INITSEG
    mov es,ax
    mov di,#
    0x0090 ! 傳輸的目的地址: 0x9000:0x0090 =
     es:di
    mov cx,#
    0x10

    rep
    movsb

    #define EXT_MEM_K (*(unsigned short *)0x90002) 

    其中0x90002的內容也是在Setup.s設置好的
    ! Get memory size (extended mem, kB) ! 下面3 句取擴展內存的大小值(KB)。
    ! 是調用中斷0x15,功能號ah = 0x88

    ! 返回:ax = 從0x100000(1M)處開始的擴展內存大小(KB)。
    ! 若出錯則CF 置位,ax =
     出錯碼。

    mov ah,#
    0x88

    int 0x15
    mov [
    2],ax ! 將擴展內存數值存在0x90002 處(1 個字)。

    具體實現還得參考一下setup.s在此處的上下文,由于這并非該文重點就講到說到這里,其實大家在這只需要知道,EXT_MEM_K是當前物理內存的1M以后的擴展內存就可以了。

    (如果你對(*(unsigned short *)0x90002) 不了解什么意思 那么請參考前一篇指針文章。)

    memory_end = (1 << 20+ (EXT_MEM_K << 10
    ); /*后面會看到內核的將占用1M的空間*/

    可以知道 memory_end = 1024(K) + EXT_MEM_K(K)   /*注意單位*/

    memory_end &= 0xfffff000
    ; /*因為以后的操作都是對整的4空間當一個整體進行的*/

    忽略小于等于4K大小的內存,也就是說經過這個設置,linux內存的每一頁大小為4K。

    也許有人搞不清楚4K怎么算得?

    上面將memory_end(以二進制方式)底上的12位(0xfff)全設0,2的12次方bits恰好是4K。

    if (memory_end > 16 * 1024 * 1024
    )       
           memory_end 
    = 16 * 1024 * 1024;
    這里設置內存大大小最終不超過16M

    也許有的人會怎么想:不是說總內存可以用到4G嗎?這里怎么~~~,且看下面

       if (memory_end > 12 * 1024 * 1024) /*如果內存>12Mb,則設置緩沖區末端=4Mb*/
        buffer_memory_end = 4 * 1024 * 1024;
      else if (memory_end > 6 * 1024 * 1024) /* 否則如果內存>6Mb,則設置緩沖區末端=2Mb*/
        buffer_memory_end = 2 * 1024 * 1024;
      else
        buffer_memory_end = 1 * 1024 * 1024; /* 緩沖區地址不小于1M*/

    接下來根據memory_end的大小設置緩沖區大小,根據前面情況如果內存比較到那么緩沖區就搞大點,小的話就只能省點用了。

    main_memory_start =
     buffer_memory_end;

    將主內存起始地址 = 緩沖區的結束地址

    #ifdef RAMDISK   /* 如果定義了虛擬盤,則主內存將減少。*/
      main_memory_start += rd_init (main_memory_start, RAMDISK * 1024);
    #endif

    這個時候還要看看有沒有分配虛擬盤,我在最前面的代碼加了一個define在這里得到發揮作用了。

    前面對 rd_init這個函數做了如下定義 extern long rd_init (long mem_start, int length);
    它的作用是:虛擬盤初始化,該函數在“kernel/blk_drv/ramdisk.c”中實現

    /* 返回內存虛擬盤ramdisk 所需的內存量 */
    long
    rd_init (
    long mem_start, int length)
    {
      
    int
     i;
      
    char *
    cp;

      blk_dev[MAJOR_NR].request_fn 
    =
     DEVICE_REQUEST;    
      rd_start 
    = (char *
    ) mem_start;
      rd_length 
    =
     length;
      cp 
    =
     rd_start;
      
    for (i = 0; i < length; i++
    )
        
    *cp++ = '\0';/*ramdisk內存空間初始化全為'\0'(null)*/

      
    return (length);
    }

    對 blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; 進行追源

    可以在該目錄下的 blk.h 中找到如下信息

    #define NR_BLK_DEV 7  / 塊設備的數量。*/

    extern struct blk_dev_struct blk_dev[NR_BLK_DEV]; /* 塊設備數組,每種塊設備占用一項。*/

    /* 塊設備結構。*/
    struct blk_dev_struct
    {
      void (*request_fn) (void); /* 請求操作的函數指針。*/
      struct request *current_request; /* 請求信息結構。*/
    };

    那么這個地方的函數指針指向誰呢?
       
    #define DEVICE_REQUEST do_fd_request /* 設備請求函數do_fd_request()。*/

    那do_fd_request函數的實現在什么地方呢?

    它在/blk_dev/Ramdisk.c

    /* 執行虛擬盤(ramdisk)讀寫操作。程序結構與do_hd_request()類似(kernel/blk_drv/hd.c,294)。*/
    void
    do_rd_request (
    void)
    {
      
    int
     len;
      
    char *
    addr;

      INIT_REQUEST;            
    /* 檢測請求的合法性(參見kernel/blk_drv/blk.h,127)。*/

    /* 下面語句取得ramdisk 的起始扇區對應的內存起始位置和內存長度。*/
    /* 其中sector << 9 表示sector * 512,CURRENT 定義為(blk_dev[MAJOR_NR].current_request)。*/
      addr 
    = rd_start + (CURRENT->sector << 9);
      len 
    = CURRENT->nr_sectors << 9
    ;
    /* 如果子設備號不為1 或者對應內存起始位置>虛擬盤末尾,則結束該請求,并跳轉到repeat 處*/

    /* (定義在28 行的INIT_REQUEST 內開始處)。*/
      
    if ((MINOR (CURRENT->dev) != 1|| (addr + len > rd_start + rd_length))
        
    {
          end_request (
    0
    );
          
    goto
     repeat;
        }

    /* 如果是寫命令(WRITE),則將請求項中緩沖區的內容復制到addr 處,長度為len 字節。*/
      
    if (CURRENT->cmd == WRITE)
        
    {
          (
    void) memcpy (addr, CURRENT->
    buffer, len);
    /* 如果是讀命令(READ),則將addr 開始的內容復制到請求項中緩沖區中,長度為len 字節。*/

        }

      
    else if (CURRENT->cmd == READ)
        
    {
          (
    void) memcpy (CURRENT->
    buffer, addr, len);
    /* 否則顯示命令不存在,死機。*/

        }

      
    else
        panic (
    "unknown ramdisk-command");
    /* 請求項成功后處理,置更新標志。并繼續處理本設備的下一請求項。*/

      end_request (
    1);
      
    goto
     repeat;
    }
     

    (其中sector << 9 表示sector * 512這個問題在我前前一篇文章已做了詳細的講解)


    代碼中的INIT_REQUEST在blk_dev/blk.h中被 定義初始化請求宏。
                                                                             

    #define INIT_REQUEST \
    repeat: \
    if (!CURRENT) \   /* 如果當前請求結構指針為null 則返回。*/
    return;
    if (MAJOR (CURRENT->dev) !=
     MAJOR_NR)
      \    
    /* 如果當前設備的主設備號不對則死機。*/

        panic (DEVICE_NAME 
    ": request list destroyed");/*調用內核的Kernel/printk.c中的printk函數*/
    if (CURRENT->bh)
      
    {
        
    if (!CURRENT->bh->
    b_lock)
          \    
    /* 如果在進行請求操作時緩沖區沒鎖定則死機。*/

     panic (DEVICE_NAME 
    ": block not locked");
      }


    #endif

    對于這個do_rd_request涉及到對塊設備的操作,這里不做詳細講解。大家只要知道,這個函數對rd_init這個函數是對ramdisk進行初始化便可。

    終于快完了,也許你會感覺看得很累,哎!我寫得更累

    mem_init (main_memory_start, memory_end);
    終于到內存初始化了,嘿嘿

    假設我們現在的內存比較到,擴展內存超過16M
    經過前面的操作我們大概知道內存是這樣的


     ______  16M(在該版本的linux最多支持16M內存,多余的內存將被視而不見)
    |  .   |     前面的問題在這里得到解釋 
    |  .   | 
    |主內存|
    |———|
    4M+32K(前面我定義了32)
    |虛擬盤|
    |———|
    4M
    |高速緩|
    |沖區  |
    |———|
    1M(其實內核大小之后640K)
    |內核  |
     ——— 
    0M

    mem_init (main_memory_start, memory_end);最關鍵的函數出來了,
               
    該函數在mm/memory.c中被實現,實現主內存初始化;

    void
    mem_init (
    long start_mem, long end_mem)
    {
      
    int
     i;

      HIGH_MEMORY 
    =
     end_mem;
      
    for (i = 0; i < PAGING_PAGES; i++
    )
        mem_map[i] 
    =
     USED;
      i 
    =
     MAP_NR (start_mem);
      end_mem 
    -=
     start_mem;    
      end_mem 
    >>= 12
    ;
      
    while (end_mem-- > 0
    )
        mem_map[i
    ++= 0
    ;
    }

    static long HIGH_MEMORY = 0; /* 全局變量,存放實際物理內存最高端地址16M。在該版本的linux最多支持16M內存,多余的內存將被視而不見*/

    接下來的for循環用到如下定義:

    #define PAGING_MEMORY (15*1024*1024) /*除內核占用的那1M內存,其他的內存都會被分頁,中共為15MB(前面已假設內存大于16M)。即,被分頁的內存最多15M(這里的15M指的是緩沖區+主內存)*/

    #define PAGING_PAGES (PAGING_MEMORY>>12) /* 偏移之后恰好為分頁后的物理內存頁數*/

    /*內存映射字節圖(1 字節代表1 頁內存),每個頁面對應的字節用于標志頁面當前被引用(占用)次數。*/
    static unsigned char mem_map[PAGING_PAGES] = { 0, };

    #define USED 100  /* 頁面被占用標志*/

    #define LOW_MEM 0x100000 /* 低端內核內存空間(1MB)。*/

    #define MAP_NR(addr) (((addr)-LOW_MEM)>>12) /* 這里定義了一個函數,指定內存地址映射為頁號 i。*/

    end_mem -= start_mem;  /* 再計算可分頁處理的內存空間。*/

    end_mem >>= 12;  /* 從而計算出可用于分頁處理的頁面數。*/

      while (end_mem-- > 0)  /* 最后將這些可用頁面對應的頁面映射數組清零。*/
        mem_map[i++] = 0;

    到這里內存初始化真正完成,讓我們回顧一個整個過程吧!

    設置緩沖區地址(不小與1M)==> 如果定義了ramdisk那么設置它,并將該部分內存的值全搞成'\0' ==>將主內存設置在它后面,然后對除內核(也許緩沖區在此內存區域中)占用的那空間進行分頁(每頁4K),再對內存內存映射字節圖進行初始化。

    也許你現在在想,我該找出去散下心,我快瘋了!!! 呵呵 慢慢來


    地震讓大伙知道:居安思危,才是生存之道。
    posted on 2008-02-19 00:28 小尋 閱讀(2669) 評論(5)  編輯  收藏 所屬分類: kernel

    FeedBack:
    # re: linux0.11內核main.c中的內存初始化 /*非常詳解*/ 2008-02-19 10:24 幻想~@@~
    Memory.c有一個定義
    老趙書中說
    #define PAGING_MEMORY (15*1024*1024) /*分頁內存15M,主內存最多15M*/
     
    個人覺得,他說主內存15M 好象說錯了
    我做了如下解釋:
    #define PAGING_MEMORY (15*1024*1024) /*除內核占用的那1M內存,其他的內存都會被分頁,中共為15MB(前面已假設內存大于16M)。即,被分頁的內存最多15M(這里的15M指的是緩沖區+主內存)
    主內存最多應該是:16M - 4M = 12M
      回復  更多評論
      
    # re: linux0.11內核main.c中的內存初始化 /*非常詳解*/ 2008-04-24 16:27 micro
    linux 0.11 剛好看到這 看了樓主的工整筆記 的覺得自己很慚愧啊..


      回復  更多評論
      
    # re: linux0.11內核main.c中的內存初始化 /*非常詳解*/[未登錄] 2008-04-25 08:07 尋覓
    過獎了 我的筆記很一般   回復  更多評論
      
    # re: linux0.11內核main.c中的內存初始化 /*非常詳解*/ 2008-08-30 08:35 劉金軒
    我是從百度里進入你的blog,剛開始看kernel的main.c,寫的很好,又看了看其它文章,覺得我應該收藏下來,呵呵,加入書簽以后常來。  回復  更多評論
      
    # re: linux0.11內核main.c中的內存初始化 /*非常詳解*/[未登錄] 2009-04-06 10:33 zhou
    do_fd_request 這里應該是 do_rd_request 吧  回復  更多評論
      
    主站蜘蛛池模板: 亚洲高清偷拍一区二区三区| 亚洲香蕉免费有线视频| 97超高清在线观看免费视频| 亚洲狠狠久久综合一区77777| 日韩版码免费福利视频| MM1313亚洲精品无码久久| 亚洲人成色777777在线观看| 国产免费看JIZZ视频| 又大又硬又粗又黄的视频免费看 | 女人被男人躁的女爽免费视频| 日韩精品免费一线在线观看| 久久精品国产精品亚洲毛片| 国产一级高清免费观看| 国产精品免费高清在线观看| 人人狠狠综合久久亚洲| 久久精品国产亚洲av麻豆色欲| 国产一级淫片a免费播放口之| 久久久久久影院久久久久免费精品国产小说 | 亚洲国产精品成人| 亚洲免费闲人蜜桃| 好吊色永久免费视频大全| 亚洲va在线va天堂va手机| 亚洲色成人中文字幕网站| 精品剧情v国产在免费线观看| 日韩av无码久久精品免费| 成年大片免费高清在线看黄| 亚洲va在线va天堂va手机| 亚洲av永久无码精品秋霞电影影院| 国产片免费在线观看| 国产成人精品免费视| 国产一精品一av一免费爽爽| 美女视频黄频a免费观看| 亚洲av乱码一区二区三区香蕉| 国产成人无码综合亚洲日韩 | 亚洲AV无码国产精品色午友在线 | 老司机亚洲精品影院| 国产成人精品久久亚洲高清不卡 | 大学生一级特黄的免费大片视频| 免费视频精品一区二区三区| 一级A毛片免费观看久久精品| 亚洲精品国产首次亮相|