日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

虚拟地址如何访问到物理地址

發布時間:2023/12/18 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 虚拟地址如何访问到物理地址 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

環境:32bit CPU
一、通過二級頁表映射的方式訪問物理地址

1、取一級頁表的基地址Abase1
2、取虛擬地址的前12bit[31:20]地址O1
3、計算得到新地址Apgd=(Abase1&0xFFFFF000)+O1,此地址是PGD頁表上的地址,取此地址中的數據Abase2
4、取虛擬地址的中間8bit[19:12]地址O2
5、計算得到新地址Apte=(Abase2&0xFFFFFF00)+O2,此地址是PTE頁表上的地址,取此地址中的數據Abase3
6、取虛擬地址的后12bit[11:0]地址O3
7、計算得到新地址Aphy=(Abase3&0xFFFFF000)+O3,此地址就是實實在在的物理地址
以上的計算和查找過程有MMU模塊實現,同時會把映射信息存放在MMU中的TLB中,類似于cache,方便下次快速的查找。
二、疑問
1、一級頁表的基地址存放在什么地方?
2、如何找到存放在物理地址的信息?
三、分析問題
1、操作系統有個專門的結構體負責進程的內存使用情況,task_struct里面的mm_struct

struct task_struct {......int on_rq;int prio, static_prio, normal_prio;unsigned int rt_priority;const struct sched_class *sched_class;struct sched_entity se;struct sched_rt_entity rt;......struct mm_struct *mm, *active_mm;struct mm_struct {......unsigned long mmap_base; /* base of mmap area */unsigned long mmap_legacy_base; /* base of mmap area in bottom-up allocations */unsigned long task_size; /* size of task vm space */unsigned long highest_vm_end; /* highest vma end address */pgd_t * pgd;atomic_t mm_users; /* How many users with user space? */atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */atomic_long_t nr_ptes; /* PTE page table pages */......

其中pgd保存的就是一級頁表的基地址。進程切換時,會把TTBRx寄存器(上圖所示)保存起來,把下一個進程的pgd內容寫到TTBRx寄存器中。
2、寫段程序,找到存放信息的物理地址
2.1、內核態代碼
根據上面所描述的頁表映射方式,查找到虛擬地址對應的物理地址
需要在內核態下面運行,內核態下可以獲取當前進程的PCB task_struct進而獲取mm_struct內存管理模塊

#include <linux/init_task.h> unsigned int get_phy_addr(struct mm_struct *mm, unsigned long addr) {pgd_t *pgd = NULL;pud_t *pud = NULL;pmd_t *pmd = NULL;pte_t *pte = NULL;unsigned int phy_addr = 0;printk("pgd_base = %p\n",mm->pgd);pgd = pgd_offset(mm, addr); //一級頁表項的地址printk("vaddr=[0x%08x] *pgd=%08x\n", addr, pgd_val(*pgd));if (pgd_none(*pgd))goto out;if (pgd_bad(*pgd)) {printk("pgd bad\n");goto out;}pud = pud_offset(pgd, addr);printk("pud=0x%08x,*pud=%08x\n", pud, pud_val(*pud));if (pud_none(*pud))goto out;if (pud_bad(*pud)) {printk("pud bad");goto out;}pmd = pmd_offset(pud, addr);printk("pmd=0x%08x,*pmd=%08x\n", pmd, pmd_val(*pmd));if (pmd_none(*pmd))goto out;if (pmd_bad(*pmd)) {printk("(bad)");goto out;}//二級映射,pmd等于pgd。函數中實現兩個功能:1、根據pgd地址查找二級頁表基地址;2、二級頁表基地址和addr算出二級頁表地址pte = pte_offset_map(pmd, addr); printk("pte=0x%08x, *pte=%08x\n", pte, pte_val(*pte));if(!pte_none(*pte) && pte_present(*pte)) {phy_addr = (pte_val(*pte) & 0xFFFFF000) | (addr & 0xFFF);printk("phy_addr is 0x%x\n", phy_addr);}else{printk("pte is not present\n") ;}pte_unmap(pte);return phy_addr; out:return -1; }

2.2、/dev/mem節點說明
知道虛擬地址對應的物理地址,我們就可以借助/dev/mem節點訪問對應的物理地址查看是否是虛擬地址存放的數據。
/dev/mem是系統物理內存的映像文件,這里的物理內存是指我們插在內存槽上的內存條嗎?當然是,但物理內存不單單指內存條。
物理內存嚴格來講應該是指 物理地址空間 ,內存條只是映射到這個地址空間的一部分,其余的還有各種PCI設備,IO端口等。
我們可以從/proc/iomem中看到這個映射:

cat /proc/iomem


其中 內存分配的物理地址是0x80000000~0x83FFFFFF 64M
事實上,它就是一個活著的Linux系統實時映像,所有的進程task_struct結構體,sock結構體,sk_buff結構體,進程數據等等都在里面的某個位置:

如果能定位它們在/dev/mem里的位置,我們就能得到系統中這些數據結構的實時值,所謂的調試工具所做的也不過如此。其實我們在調試內核轉儲文件的時候,vmcore也是一個物理內存映像,和/dev/mem不同的是,它是一具尸體。
無論是活體,還是尸體,均五臟俱全,分析它們的手段是一致。和靜態分析vmcore不同的是,/dev/mem是一個動態的內存映像,有時候借助它可以做一些正經的事情。
2.3、應用層獲取/dev/mem的映射

#include <stdio.h> #include <unistd.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h>int main(int argc, char *argv[]) {int i = 0, fd = 0;unsigned int addr = 0, content = 0;unsigned int page_size = getpagesize();unsigned int taddr = 0;unsigned int *map_base = NULL;unsigned int offset_in_page = 0;printf("page_size=0x%x\n", page_size);fd = open("/dev/mem", O_RDWR|O_SYNC);if (fd == -1) {printf("open /dev/mem error.\n");return -1;}taddr = strtoll(argv[1], NULL, 16);offset_in_page = taddr & (page_size - 1);//map_base = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, taddr & (~(unsigned int)(page_size - 1)));map_base = mmap(NULL, page_size, PROT_READ, MAP_PRIVATE, fd, taddr & (~(unsigned int)(page_size - 1)));if (-1 == (int)map_base) {printf("mmap failed!, errno=%d\n", errno);close(fd);return -1;}printf("map_base=0x%x\n", map_base);addr = (unsigned int)(((unsigned char *)map_base) + offset_in_page);printf("addressx:0x%x\n", addr);printf("content 0x%x\n\n", *(unsigned int *)(addr));//*(unsigned int *)(addr) = 0x6f6a6944;close(fd);munmap(map_base, page_size);return 1; }

2.4、測試程序

測試程序: int main(int argc, char** argv) {int fd = -1;struct param_s param;char *buf = "Chinaxxxxx.\n";memset(&param, 0x00, sizeof(struct ty_motor_param_s));fd = open("/dev/motor", O_RDWR);if (fd < 0) {printf("open error.\n");return 0;}printf("buf=0x%x", buf);param.addr = buf;ioctl(fd, IOCTL_SET, &param);close(fd);sleep(10);printf("buf=0x%x", buf);return 0; }

2.5、實驗開始
2.5.1、運行2.4程序,通過ioctl運行內核代碼,執行2.3程序get_phy_addr函數,獲取物理地址。
2.5.2、通過傳入獲取到的物理地址,運行2.2程序,打印物理地址的信息。

其中0x83e6573c是獲取的物理地址
ARM處理器小端存儲數據,因此0x43 0x68 0x69 0x6e 剛好對應著Chinaxxxxx中的Chin的Ascii碼值
2.6、擴展
2.6.1、既然知道物理地址了,是不是可以修改物理地址里面的內容?
2.6.2、打開2.3代碼的注釋 *(unsigned int *)(addr) = 0x6f6a6944;
2.6.3、再操作2.5中的步驟,發現原先的Chinaxxxxx被修改了

3、遇到的問題
3.1.1、2.3中的代碼mmap一直返回-1,錯誤碼一直是1(errno 1 Operation not permitted)
3.1.2、解決方法
在.config文件中設置CONFIG_STRICT_DEVMEM is not set

正如上面描述的,通過獲取物理地址修改物理地址里面的數據,這對系統來說就毫無安全可言。因此內核通過CONFIG_STRICT_DEVMEM配置項禁止內存空間實現映射。

總結

以上是生活随笔為你收集整理的虚拟地址如何访问到物理地址的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。