2010-03-13 16 views

cevap

2

Öncelikle bunu neden istesin? Modern VM sistemlerinin amacı, uygulama programlayıcısını, fiziksel bellek düzeninin karmaşıklığından çıkarmaktır. Hayatlarını daha kolay hale getirmek için her birinin kendi tek adres alanını açarak.

Bunu yapmak istiyorum olmadıysa neredeyse certanly bir çekirdek modülü kullanmak gerekir. Değişkenin sanal adresini normal şekilde alın, bunu işlem sayfaları tablolarına dizinlemek ve bulduğunuz değeri (çerçevenin fiziksel adresi) okumak için kullanın. Daha sonra, tüm fiziksel adresi almak için sayfa ofseti ekleyin. Sayfalama etkinken bu adresi kullanamayacağınızı unutmayın.

(Eğer bir çekirdek modülü yazmak için ihtiyaç olmaz böylece/proc dosya sisteminden bir VM bölgenin çerçeve adresini almak mümkün olabilir ve şanslı.)

+4

... ile ilgili diğer bilgiler ve Sayfayı belleğe kilitlersiniz, fiziksel adres her an değişebilir. – caf

+1

Bir çekirdek modülü yazmanıza gerek yoktur: diğer örnekler açıkladığı gibi, bu zaten '/ proc/$ pid/pagemap' üzerinden gösterilmektedir. – poolie

+0

Değişkenlerin fiziksel adresini bilmek NUMA mimarisinde ilginç olabilir – horro

-5

(düzenleme: tarafından "Eğer fiziksel adres hangi" ise, düzeyini demek 'RAM modülü benim bit depolanmaz', sonra aşağıdaki cevap uygun değildir.)

Bunu yapmak için root yetkileri gerekmez. Bunun yerine ihtiyacınız olan bir hata ayıklayıcıdır. Ve burada (x86_64'lerde Linux sistemini kullanarak) gidin:

Önce oynamak için biraz program gerekiyor. Bu bir global değişkene erişir ve iki kez üst üste basar. Daha sonra bellekte bulduğumuz iki küresel değişkene sahiptir.

 
#include <stdio.h> 

int a, b = 0; 

int main(void) 
{ 
    printf("a: "); 
    if (fscanf("%d", &a) < 1) 
     return 0; 

    printf("a = %d\n", myglobal); 

    printf("b: "); 
    if (fscanf("%d", &b) < 1) 
     return 0; 

    printf("a = %d, b = %d\n", a, b); 

    return 0; 
} 

Adım 1: Programı derlemek ve ondan tüm hata ayıklama bilgilerini şerit, biz de, gerçek hayattaki bir durumda alacağı yok ayıklayıcıya herhangi ipuçları alamadım.

 
$ gcc -s -W -Wall -Os -o ab ab.c 

Adım 2: Programı çalıştırın ve iki numaradan birini girin.

 
$ ./ab 
a: 123 
a = 123 
b: _ 

Adım 3: İşlemi bulun.

 
$ ps aux | grep ab 
roland 21601 0.0 0.0 3648 456 pts/11 S+ 15:17 0:00 ./ab 
roland 21665 0.0 0.0 5132 672 pts/12 S+ 15:18 0:00 grep ab 

Adım 4: İşlemciye bir hata ayıklayıcı ekleyin (21601). o main fonksiyonu ve scanf işlevini çağırarak bazı kodu arasında olduğundan

 
$ gdb 
... 
(gdb) attach 21601 
... 
(gdb) where 
#0 0x00007fdecfdd2970 in read() from /lib/libc.so.6 
#1 0x00007fdecfd80b40 in _IO_file_underflow() from /lib/libc.so.6 
#2 0x00007fdecfd8230e in _IO_default_uflow() from /lib/libc.so.6 
#3 0x00007fdecfd66903 in _IO_vfscanf() from /lib/libc.so.6 
#4 0x00007fdecfd7245c in scanf() from /lib/libc.so.6 
#5 0x0000000000400570 in ??() 
#6 0x00007fdecfd2f1a6 in __libc_start_main() from /lib/libc.so.6 
#7 0x0000000000400459 in ??() 
#8 0x00007fffd827da48 in ??() 
#9 0x000000000000001c in ??() 
#10 0x0000000000000001 in ??() 
#11 0x00007fffd827f9a2 in ??() 
#12 0x0000000000000000 in ??() 

ilginç kare, sayı 5, bu nedenle bizim main işlev olmalıdır.

 
(gdb) frame 5 
... 
(gdb) disassemble $pc $pc+50 
... 
0x0000000000400570 :  test %eax,%eax 
0x0000000000400572 :  jle 0x40058c <[email protected]+372> 
0x0000000000400574 :  mov 0x2003fe(%rip),%edx  # 0x600978 <[email protected]+2098528> 
0x000000000040057a :  mov 0x2003fc(%rip),%esi  # 0x60097c <[email protected]+2098532> 
0x0000000000400580 :  mov $0x40068f,%edi 
0x0000000000400585 :  xor %eax,%eax 
0x0000000000400587 :  callq 0x4003f8 <[email protected]> 
... 

Şimdi fonksiyon printf üç parametre alacak biliyorum ve orada iki sadece dört bayt birbirlerinden uzak şunlardır: ayıklama oturumu devam ediliyor. Bu ikisinin a ve b değişkenlerimiz olduğuna dair iyi bir işaret. Yani a adresi 0x600978 veya 0x60097c'dir. en deneyerek öğrenelim: 0x0000007b (girdiğimiz 123 için onaltılık gösterimi, çünkü) ve b 0x600978 olan

 
(gdb) x/w 0x60097c   
0x60097c <[email protected]+2098532>: 0x0000007b 
(gdb) x/w 0x600978 
0x600978 <[email protected]+2098528>: 0x00000000 

Yani a

, ilk okunduğunda değişken, adres 0x60097c yer almaktadır.

Hata ayıklayıcısında, şimdi a değişkenini değiştirebilir ve sonra programa devam edebiliriz. iki sayıyı girmek istedi programda

 
(gdb) set *(int *)0x60097c = 1234567 
(gdb) continue 

Arka:

 
$ ./ab 
a: 123 
a = 123 
b: 5 
a = 1234567, b = 5 
$ 
+11

Bu fiziksel adresinizi değil, fiziksel adresinizi verir. –

16

olarak kısmen önce cevap normal programlar bunlar sanal bir adres alanı ile çalıştırmak gibi fiziksel adresler hakkında endişelenmenize gerek olmamalıdır tüm kolaylıkları. Ayrıca, her sanal adres fiziksel bir adrese sahip değildir, haritalanmış dosyalara veya değiştirilmiş sayfalara ait olabilir. Ancak, bazen bu haritalamayı, kullanıcı alanında bile görmek ilginç olabilir.

Bu amaçla, Linux çekirdeği, eşlemeyi /proc'daki bir dosya kümesinden kullanıcı sayfasına çıkarır. Dökümantasyon here bulunabilir. Kısa özet:

  1. /proc/$pid/maps böyle eşleştirilmiş dosyalar için gelen dosya olarak birlikte ek bilgilerle sanal adreslerin eşleme listesi sağlar.
  2. /proc/$pid/pagemap, varsa fiziksel adres de dahil olmak üzere, eşlenen her sayfa hakkında daha fazla bilgi sağlar.

This website, bu arabirimi kullanarak tüm çalışan işlemlerin eşlemelerini dökülen bir C programı ve ne yaptığını açıklayan bir C programı sunar.

+0

Testlerle en az örnek: https://stackoverflow.com/questions/17021214/how-to-decode-proc-pid-pagemap-entries-in-linux/45126141#45126141 –

14
#include "stdio.h" 
#include "unistd.h" 
#include "inttypes.h" 

uintptr_t vtop(uintptr_t vaddr) { 
    FILE *pagemap; 
    intptr_t paddr = 0; 
    int offset = (vaddr/sysconf(_SC_PAGESIZE)) * sizeof(uint64_t); 
    uint64_t e; 

    // https://www.kernel.org/doc/Documentation/vm/pagemap.txt 
    if ((pagemap = fopen("/proc/self/pagemap", "r"))) { 
     if (lseek(fileno(pagemap), offset, SEEK_SET) == offset) { 
      if (fread(&e, sizeof(uint64_t), 1, pagemap)) { 
       if (e & (1ULL << 63)) { // page present ? 
        paddr = e & ((1ULL << 54) - 1); // pfn mask 
        paddr = paddr * sysconf(_SC_PAGESIZE); 
        // add offset within page 
        paddr = paddr | (vaddr & (sysconf(_SC_PAGESIZE) - 1)); 
       } 
      } 
     } 
     fclose(pagemap); 
    } 

    return paddr; 
} 
İlgili konular