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

          歡迎光臨! 
          閑聊 QQ:1074961813

          隨筆分類(357)

          我管理的群

          公共blog

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

          參與管理的論壇

          好友的blog

          我的其他blog

          朋友的網(wǎng)站

          搜索

          •  

          最新評(píng)論

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

          .
          #define
           RAMDISK 32 /*這個(gè)定義是我特意加上去的,原代碼中無此定義*/
          #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);
          .

          它完成了內(nèi)存的分頁、分配工作。其中調(diào)用了其他的代碼,且看他是如何工作的:

           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長度的內(nèi)存空間內(nèi)容,已在前面在系統(tǒng)引導(dǎo)代碼/boot/setup.s 中已做了設(shè)置

          具體是:

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

          !第一個(gè)硬盤
          mov ax,#
          0x0000

          mov ds,ax
          lds si,[
          4*0x41! 取中斷向量0x41 的值,也即hd0 參數(shù)表的地址 ds:si
          mov ax,#INITSEG
          mov es,ax
          mov di,#
          0x0080 ! 傳輸?shù)哪康牡刂? 0x9000:0x0080 =
           es:di
          mov cx,#
          0x10 !
           循環(huán)0x10次沒次一個(gè)字節(jié),共傳輸0x10 字節(jié)。
          rep
          movsb  

          !
          第二個(gè)硬盤

          mov ax,#
          0x0000

          mov ds,ax
          lds si,[
          4*0x46! 取中斷向量0x46 的值,也即hd1 參數(shù)表的地址=ds:si
          mov ax,#INITSEG
          mov es,ax
          mov di,#
          0x0090 ! 傳輸?shù)哪康牡刂? 0x9000:0x0090 =
           es:di
          mov cx,#
          0x10

          rep
          movsb

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

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

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

          mov ah,#
          0x88

          int 0x15
          mov [
          2],ax ! 將擴(kuò)展內(nèi)存數(shù)值存在0x90002 處(1 個(gè)字)。

          具體實(shí)現(xiàn)還得參考一下setup.s在此處的上下文,由于這并非該文重點(diǎn)就講到說到這里,其實(shí)大家在這只需要知道,EXT_MEM_K是當(dāng)前物理內(nèi)存的1M以后的擴(kuò)展內(nèi)存就可以了。

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

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

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

          memory_end &= 0xfffff000
          ; /*因?yàn)橐院蟮牟僮鞫际菍?duì)整的4空間當(dāng)一個(gè)整體進(jìn)行的*/

          忽略小于等于4K大小的內(nèi)存,也就是說經(jīng)過這個(gè)設(shè)置,linux內(nèi)存的每一頁大小為4K。

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

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

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

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

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

          接下來根據(jù)memory_end的大小設(shè)置緩沖區(qū)大小,根據(jù)前面情況如果內(nèi)存比較到那么緩沖區(qū)就搞大點(diǎn),小的話就只能省點(diǎn)用了。

          main_memory_start =
           buffer_memory_end;

          將主內(nèi)存起始地址 = 緩沖區(qū)的結(jié)束地址

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

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

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

          /* 返回內(nèi)存虛擬盤ramdisk 所需的內(nèi)存量 */
          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內(nèi)存空間初始化全為'\0'(null)*/

            
          return (length);
          }

          對(duì) blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; 進(jìn)行追源

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

          #define NR_BLK_DEV 7  / 塊設(shè)備的數(shù)量。*/

          extern struct blk_dev_struct blk_dev[NR_BLK_DEV]; /* 塊設(shè)備數(shù)組,每種塊設(shè)備占用一項(xiàng)。*/

          /* 塊設(shè)備結(jié)構(gòu)。*/
          struct blk_dev_struct
          {
            void (*request_fn) (void); /* 請(qǐng)求操作的函數(shù)指針。*/
            struct request *current_request; /* 請(qǐng)求信息結(jié)構(gòu)。*/
          };

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

          那do_fd_request函數(shù)的實(shí)現(xiàn)在什么地方呢?

          它在/blk_dev/Ramdisk.c

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

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

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

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

          /* 如果是寫命令(WRITE),則將請(qǐng)求項(xiàng)中緩沖區(qū)的內(nèi)容復(fù)制到addr 處,長度為len 字節(jié)。*/
            
          if (CURRENT->cmd == WRITE)
              
          {
                (
          void) memcpy (addr, CURRENT->
          buffer, len);
          /* 如果是讀命令(READ),則將addr 開始的內(nèi)容復(fù)制到請(qǐng)求項(xiàng)中緩沖區(qū)中,長度為len 字節(jié)。*/

              }

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

              }

            
          else
              panic (
          "unknown ramdisk-command");
          /* 請(qǐng)求項(xiàng)成功后處理,置更新標(biāo)志。并繼續(xù)處理本設(shè)備的下一請(qǐng)求項(xiàng)。*/

            end_request (
          1);
            
          goto
           repeat;
          }
           

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


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

          #define INIT_REQUEST \
          repeat: \
          if (!CURRENT) \   /* 如果當(dāng)前請(qǐng)求結(jié)構(gòu)指針為null 則返回。*/
          return;
          if (MAJOR (CURRENT->dev) !=
           MAJOR_NR)
            \    
          /* 如果當(dāng)前設(shè)備的主設(shè)備號(hào)不對(duì)則死機(jī)。*/

              panic (DEVICE_NAME 
          ": request list destroyed");/*調(diào)用內(nèi)核的Kernel/printk.c中的printk函數(shù)*/
          if (CURRENT->bh)
            
          {
              
          if (!CURRENT->bh->
          b_lock)
                \    
          /* 如果在進(jìn)行請(qǐng)求操作時(shí)緩沖區(qū)沒鎖定則死機(jī)。*/

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


          #endif

          對(duì)于這個(gè)do_rd_request涉及到對(duì)塊設(shè)備的操作,這里不做詳細(xì)講解。大家只要知道,這個(gè)函數(shù)對(duì)rd_init這個(gè)函數(shù)是對(duì)ramdisk進(jìn)行初始化便可。

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

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

          假設(shè)我們現(xiàn)在的內(nèi)存比較到,擴(kuò)展內(nèi)存超過16M
          經(jīng)過前面的操作我們大概知道內(nèi)存是這樣的


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

          mem_init (main_memory_start, memory_end);最關(guān)鍵的函數(shù)出來了,
                     
          該函數(shù)在mm/memory.c中被實(shí)現(xiàn),實(shí)現(xiàn)主內(nèi)存初始化;

          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; /* 全局變量,存放實(shí)際物理內(nèi)存最高端地址16M。在該版本的linux最多支持16M內(nèi)存,多余的內(nèi)存將被視而不見*/

          接下來的for循環(huán)用到如下定義:

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

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

          /*內(nèi)存映射字節(jié)圖(1 字節(jié)代表1 頁內(nèi)存),每個(gè)頁面對(duì)應(yīng)的字節(jié)用于標(biāo)志頁面當(dāng)前被引用(占用)次數(shù)。*/
          static unsigned char mem_map[PAGING_PAGES] = { 0, };

          #define USED 100  /* 頁面被占用標(biāo)志*/

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

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

          end_mem -= start_mem;  /* 再計(jì)算可分頁處理的內(nèi)存空間。*/

          end_mem >>= 12;  /* 從而計(jì)算出可用于分頁處理的頁面數(shù)。*/

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

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

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

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


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

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


            回復(fù)  更多評(píng)論
            
          # re: linux0.11內(nèi)核main.c中的內(nèi)存初始化 /*非常詳解*/[未登錄] 2008-04-25 08:07 尋覓
          過獎(jiǎng)了 我的筆記很一般   回復(fù)  更多評(píng)論
            
          # re: linux0.11內(nèi)核main.c中的內(nèi)存初始化 /*非常詳解*/ 2008-08-30 08:35 劉金軒
          我是從百度里進(jìn)入你的blog,剛開始看kernel的main.c,寫的很好,又看了看其它文章,覺得我應(yīng)該收藏下來,呵呵,加入書簽以后常來。  回復(fù)  更多評(píng)論
            
          # re: linux0.11內(nèi)核main.c中的內(nèi)存初始化 /*非常詳解*/[未登錄] 2009-04-06 10:33 zhou
          do_fd_request 這里應(yīng)該是 do_rd_request 吧  回復(fù)  更多評(píng)論
            
          主站蜘蛛池模板: 沾化县| 瓦房店市| 宁陕县| 利津县| 津南区| 游戏| 赤壁市| 呼和浩特市| 中方县| 堆龙德庆县| 新兴县| 铜鼓县| 江安县| 清河县| 常熟市| 青神县| 峨眉山市| 新宾| 德昌县| 获嘉县| 威海市| 胶南市| 土默特右旗| 轮台县| 砚山县| 都昌县| 正定县| 乳源| 临桂县| 大厂| 德格县| 寻乌县| 渝北区| 兴隆县| 确山县| 于都县| 孝昌县| 湛江市| 高台县| 平乐县| 永胜县|