CPU地址空間詳解


(一)地址的概念

1)物理地址:CPU地址總線傳來的地址,由硬件電路控制其具體含義。物理地址中很大一部分是留給內存條中的內存的,但也常被映射到其他存儲器上 (如顯存、BIOS等)。在程序指令中的虛擬地址經過段映射和頁面映射后,就生成了物理地址,這個物理地址被放到CPU的地址線上。
        物理地址空間,一部分給物理RAM(內存)用,一部分給總線用,這是由硬件設計來決定的,因此在32 bits地址線的x86處理器中,物理地址空間是2的32次方,即4GB,但物理RAM一般不能上到4GB,因為還有一部分要給總線用(總線上還掛着別的 許多設備)。在PC機中,一般是把低端物理地址給RAM用,高端物理地址給總線用。
 
2)總線地址:總線的地址線或在地址周期上產生的信號。外設使用的是總線地址,CPU使用的是物理地址。
        物理地址與總線地址之間的關系由系統的設計決定的。在x86平台上,物理地址就是總線地址,這是因為它們共享相同的地址空間——這句話有點難理解,詳見下 面的“獨立編址”。在其他平台上,可能需要轉換/映射。比如:CPU需要訪問物理地址是0xfa000的單元,那么在x86平台上,會產生一個PCI總線 上對0xfa000地址的訪問。因為物理地址和總線地址相同,所以憑眼睛看是不能確定這個地址是用在哪兒的,它或者在內存中,或者是某個卡上的存儲單元, 甚至可能這個地址上沒有對應的存儲器。

3)虛擬地址:現代操作系統普遍采用虛擬內存管理(Virtual Memory Management)機制,這需要MMU(Memory Management Unit)的支持。MMU通常是CPU的一部分,如果處理器沒有MMU,或者有MMU但沒有啟用,CPU執行單元發出的內存地址將直接傳到芯片引腳上,被 內存芯片(物理內存)接收,這稱為物理地址(Physical Address),如果處理器啟用了MMU,CPU執行單元發出的內存地址將被MMU截獲,從CPU到MMU的地址稱為虛擬地址(Virtual Address),而MMU將這個地址翻譯成另一個地址發到CPU芯片的外部地址引腳上,也就是將虛擬地址映射成物理地址。
        Linux中,進程的4GB(虛擬)內存分為用戶空間、內核空間。用戶空間分布為0~3GB(即PAGE_OFFSET,在0X86中它等於 0xC0000000)
,剩下的1G為內核空間。程序員只能使用虛擬地址。系統中每個進程有各自的私有用戶空間(0~3G),這個空間對系統中的其他進程是不可見的。
        CPU發出取指令請求時的地址是當前上下文的虛擬地址,MMU再從頁表中找到這個虛擬地址的物理地址,完成取指。同樣讀取數據的也是虛擬地址,比如mov ax, var. 編譯時var就是一個虛擬地址,也是通過MMU從也表中來找到物理地址,再產生總線時序,完成取數據的。


(二)編址方式
1)外設都是通過讀寫設備上的寄存器來進行的,外設寄存器也稱為“I/O端口”,而IO端口有兩種編址方式:獨立編址和統一編制。

        統一編址:外設接口中的IO寄存器(即IO端口)與主存單元一樣看待,每個端口占用一個存儲單元的地址,將主存的一部分划出來用作IO地址空間,如,在 PDP-11中,把最高的4K主存作為IO設備寄存器地址。端口占用了存儲器的地址空間,使存儲量容量減小。
        統一編址也稱為“I/O內存”方式,外設寄存器位於“內存空間”(很多外設有自己的內存、緩沖區,外設的寄存器和內存統稱“I/O空間”)。
        如,Samsung的S3C2440,是32位ARM處理器,它的4GB地址空間被外設、RAM等瓜分:
0x8000 1000    LED 8*8點陣的地址
0x4800 0000 ~ 0x6000 0000  SFR(特殊暫存器)地址空間
0x3800 1002   鍵盤地址
0x3000 0000 ~ 0x3400 0000  SDRAM空間 
0x2000 0020 ~ 0x2000 002e  IDE
0x1900 0300   CS8900

        獨立編址(單獨編址):IO地址與存儲地址分開獨立編址,I/0端口地址不占用存儲空間的地址范圍,這樣,在系統中就存在了另一種與存儲地址無關的IO地 址,CPU也必須具有專用與輸入輸出操作的IO指令(IN、OUT等)和控制邏輯。獨立編址下,地址總線上過來一個地址,設備不知道是給IO端口的、還是 給存儲器的,於是處理器通過MEMR/MEMW和IOR/IOW兩組控制信號來實現對I/O端口和存儲器的不同尋址。如,intel 80x86就采用單獨編址,CPU內存和I/O是一起編址的,就是說內存一部分的地址和I/O地址是重疊的。
        獨立編址也稱為“I/O端口”方式,外設寄存器位於“I/O(地址)空間”。
        對於x86架構來說,通過IN/OUT指令訪問。PC架構一共有65536個8bit的I/O端口,組成64K個I/O地址空間,編號從 0~0xFFFF,有16位,80x86用低16位地址線A0-A15來尋址。連續兩個8bit的端口可以組成一個16bit的端口,連續4個組成一個 32bit的端口。I/O地址空間和CPU的物理地址空間是兩個不同的概念,例如I/O地址空間為64K,一個32bit的CPU物理地址空間是4G。 如,在Intel 8086+Redhat9.0 下用“more /proc/ioports”可看到:
0000-001f : dma1
0020-003f : pic1
0040-005f : timer
0060-006f : keyboard
0070-007f : rtc
0080-008f : dma page reg
00a0-00bf : pic2
00c0-00df : dma2
00f0-00ff : fpu
0170-0177 : ide1
……

        不過Intel x86平台普通使用了名為內存映射(MMIO)的技術,該技術是PCI規范的一部分,IO設備端口被映射到內存空間,映射后,CPU訪問IO端口就如同訪 問內存一樣。看Intel TA 719文檔給出的x86/x64系統典型內存地址分配表:
系統資源  占用
------------------------------------------------------------------------
BIOS  1M
本地APIC  4K
芯片組保留 2M
IO APIC  4K
PCI設備  256M
PCI Express設備 256M
PCI設備(可選) 256M
顯示幀緩存 16M
TSEG  1M


        對於某一既定的系統,它要么是獨立編址、要么是統一編址,具體采用哪一種則取決於CPU的體系結構。 如,PowerPC、m68k等采用統一編址,而X86等則采用獨立編址,存在IO空間的概念。目前,大多數嵌入式微控制器如ARM、PowerPC等並 不提供I/O空間,僅有內存空間,可直接用地址、指針訪問。但對於Linux內核而言,它可能用於不同的CPU,所以它必須都要考慮這兩種方式,於是它采 用一種新的方法,將基於I/O映射方式的或內存映射方式的I/O端口通稱為“I/O區域”(I/O region),不論你采用哪種方式,都要先申請IO區域:request_resource(),結束時釋放 它:release_resource()。
 

2)對外設的訪問

1、訪問I/O內存的流程是:request_mem_region() -> ioremap() -> ioread8()/iowrite8() -> iounmap() -> release_mem_region() 。
        前面說過,IO內存是統一編址下的概念,對於統一編址,IO地址空間是物理主存的一部分,對於編程而言,我們只能操作虛擬內存,所以,訪問的第一步就是要把設備所處的物理地址映射到虛擬地址,Linux2.6下用ioremap():
        void *ioremap(unsigned long offset, unsigned long size);
然后,我們可以直接通過指針來訪問這些地址,但是也可以用Linux內核的一組函數來讀寫:
ioread8(), iowrite16(), ioread8_rep(), iowrite8_rep()......

2、訪問I/O端口
        訪問IO端口有2種途徑:I/O映射方式(I/O-mapped)、內存映射方式(Memory-mapped)。前一種途徑不映射到內存空間,直接使用 intb()/outb()之類的函數來讀寫IO端口;后一種MMIO是先把IO端口映射到IO內存(“內存空間”),再使用訪問IO內存的函數來訪問 IO端口。
        void ioport_map(unsigned long port, unsigned int count);
通過這個函數,可以把port開始的count個連續的IO端口映射為一段“內存空間”,然后就可以在其返回的地址是像訪問IO內存一樣訪問這些IO端口。

 

 

 

Linux下的IO端口和IO內存

 

CPU對外設端口物理地址的編址方式有兩種:一種是IO映射方式,另一種是內存映射方式。 
 Linux將基於IO映射方式的和內存映射方式的IO端口統稱為IO區域(IO region)。

  IO region仍然是一種IO資源,因此它仍然可以用resource結構類型來描述。

  Linux管理IO region:

  1) request_region()

  把一個給定區間的IO端口分配給一個IO設備。

  2) check_region()

  檢查一個給定區間的IO端口是否空閑,或者其中一些是否已經分配給某個IO設備。

  3) release_region()

  釋放以前分配給一個IO設備的給定區間的IO端口。

  Linux中可以通過以下輔助函數來訪問IO端口:

  inb(),inw(),inl(),outb(),outw(),outl()

  “b”“w”“l”分別代表8位,16位,32位。

 對IO內存資源的訪問

  1) request_mem_region()

  請求分配指定的IO內存資源。

  2) check_mem_region()

  檢查指定的IO內存資源是否已被占用。

  3) release_mem_region()

  釋放指定的IO內存資源。

  其中傳給函數的start address參數是內存區的物理地址(以上函數參數表已省略)。

  驅動開發人員可以將內存映射方式的IO端口和外設內存統一看作是IO內存資源。

  ioremap()用來將IO資源的物理地址映射到內核虛地址空間(3GB - 4GB)中,參數addr是指向內核虛地址的指針。

  Linux中可以通過以下輔助函數來訪問IO內存資源:

  readb(),readw(),readl(),writeb(),writew(),writel()。

  Linux在kernel/resource.c文件中定義了全局變量ioport_resource和iomem_resource,來分別描述基於IO映射方式的整個IO端口空間和基於內存映射方式的IO內存資源空間(包括IO端口和外設內存)。

1)關於IO與內存空間:
    在X86處理器中存在着I/O空間的概念,I/O空間是相對於內存空間而言的,它通過特定的指令in、out來訪問。端口號標識了外設的寄存器地址。Intel語法的in、out指令格式為:
    IN 累加器, {端口號│DX}
    OUT {端口號│DX},累加器
    目前,大多數嵌入式微控制器如ARM、PowerPC等中並不提供I/O空間,而僅存在內存空間。內存空間可以直接通過地址、指針來訪問,程序和程序運行中使用的變量和其他數據都存在於內存空間中。 
    即便是在X86處理器中,雖然提供了I/O空間,如果由我們自己設計電路板,外設仍然可以只掛接在內存空間。此時,CPU可以像訪問一個內存單元那樣訪問外設I/O端口,而不需要設立專門的I/O指令。因此,內存空間是必須的,而I/O空間是可選的。

(2)inb和outb:

在Linux設備驅動中,宜使用Linux內核提供的函數來訪問定位於I/O空間的端口,這些函數包括:
· 讀寫字節端口(8位寬)
unsigned inb(unsigned port); 
void outb(unsigned char byte, unsigned port); 
· 讀寫字端口(16位寬)
unsigned inw(unsigned port); 
void outw(unsigned short word, unsigned port); 
· 讀寫長字端口(32位寬)
unsigned inl(unsigned port); 
void outl(unsigned longword, unsigned port); 
· 讀寫一串字節
void insb(unsigned port, void *addr, unsigned long count); 
void outsb(unsigned port, void *addr, unsigned long count);
· insb()從端口port開始讀count個字節端口,並將讀取結果寫入addr指向的內存;outsb()將addr指向的內存的count個字節連續地寫入port開始的端口。
· 讀寫一串字
void insw(unsigned port, void *addr, unsigned long count); 
void outsw(unsigned port, void *addr, unsigned long count); 
· 讀寫一串長字
void insl(unsigned port, void *addr, unsigned long count); 
void outsl(unsigned port, void *addr, unsigned long count); 
上述各函數中I/O端口號port的類型高度依賴於具體的硬件平台,因此,只是寫出了unsigned。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM