精华内容
下载资源
问答
  • Linux读写物理内存

    千次阅读 2012-07-31 16:42:59
    通常只有root用户对其有读写权限。因此只有root用户能进行这些操作。 如果要打开设备文件/dev/mem,需要系统调用open()函数,作用是打开一个文件或设备,其函数原型为: int open(const char *path, int flags); ...

    一、基础知识

    1.打开设备文件:
        mem是一个字符设备文件,是计算机主存的一个映像。通常只有root用户对其有读写权限。因此只有root用户能进行这些操作。
        如果要打开设备文件/dev/mem,需要系统调用open()函数,作用是打开一个文件或设备,其函数原型为:
        int open(const char *path, int flags);
        返回值:如果操作成功则返回一个文件描述符,否则返回-1
        形  参:
                path      被打开文件的路径即文件名描述。
                flags     文件的访问模式描述,可常用的选项见下:
                            O_RDONLY        只读方式
                            O_WRONLY        只写方式
                            O_RDWR          可读写方式
        说  明:此函数用于打开文件或者设备
        头文件:#include <fcntl.h> #include <stat.h>  定义在/usr/include/fcntl.h中
    2.读取内存映像:
        内存映像其实在内存中创建一个与外存中文件完全相同的映像。用户可以将整个文件映射到内存中,也可以将文件的一部分映射到内存中。
        使用操作内存的方法对文件进行操作。系统会将内存映像文件所做的改动反映到真实文件中去。
        在内存映像I/O的实现过程中需要用到一些系统调用:
        首先是创建内存映像文件的系统调用mmap()函数,其函数原型为:
        void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
        返回值:成功时,返回值为指向内存映像起始地址的指针,当调用失败时,返回值为-1
        形  参:
                start      一个void指针,表示希望将文件映射到此指针指向的位置,通常为NULL。
                length     定义内存映像文件所占用的内存空间大小,以字节计。
                prot       内存映像文件的安全属性,注意和open函数中的flags属性保持一致。它的可使用的选项如下:
                              Flags                          含义
                            PROT_EXEC       被映像内存可能含义机器码,可被执行
                            PROT_NONE       映像内存不允许访问
                            PROT_READ       映像内存可读
                            PROT_WRITE      映像内存可写
                flags      内存映像的标志,选项如下:
                              Flags                                   含义
                            MAP_FIXED       指定映射起始地址,如果由start和len指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。
                                            如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。
                            MAP_SHARED      与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。
                                            直到msync()或者munmap()被调用,文件实际上不会被更新。 
                            MAP_PRIVATE     建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。
                                            这个标志和以上标志是互斥的,只能使用其中一个。 
                            MAP_NORESERVE   不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。
                                            当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。
                            MAP_LOCKED      锁定映射区的页面,从而防止页面被交换出内存。 
                            MAP_GROWSDOWN   用于堆栈,告诉内核VM系统,映射区可以向下扩展。
                            MAP_ANONYMOUS   匿名映射,映射区不与任何文件关联。
                            MAP_ANON        MAP_ANONYMOUS的别称,不再被使用。 
                            MAP_FILE        兼容标志,被忽略。 
                            MAP_32BIT       将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。
                                            当前这个标志只在x86-64平台上得到支持。
                            MAP_POPULATE    为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。 
                            MAP_NONBLOCK    仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。
                fd         要映射的文件的描述符。
                offset     所映射的数据内容 距离文件头的偏移量。
        说  明:此函数用于将一个文件或它的一部分映射到内存中。
        头文件:#include <sys/mman.h> #include <sys/types.h>  函数定义在/usr/include/sys/mman.h
    3.撤销内存映像的修改
        另外我们使用完内存映像文件后,要用系统调用函数munmap()函数来撤销,其函数原型为:
        int munmap(void *start, size_t length);
        返回值:成功时,返回值为0;调用失败时返回值为 -1,并将errno设置为相应值。
        形  参:
                start      要撤销的内存映像文件的起始地址。
                length     要撤销的内存映像文件的大小。
        说  明:当进程结束或利用exec相关函数来执行其他程序时,映射内存会自动解除,但关闭对应的文件描述词时不会解除映射。
        头文件:#include <sys/mman.h>
    4.将内存映像的改动保存到外存中
        最后,如果我们要将内存映像的改动保存到外存中,还需要系统调用msync()函数,其函数原型为:
        int msync(const void *start,size_t length,int flags);
        返回值:成功时,返回值为0;调用失败时返回值为 -1,并将errno设置为相应值。
        形  参:
                start      要保存到外存的那些源文件的起始地址。
                length     表示内存映像文件的大小。
                flags      设置了函数的相应操作,其具体选项如下:
                              Flags                      含义
                            MS_ASYNC           调用一个写操作并返回
                            MS_INVALIDATE      映像到相同文件的内存映像数据更新
                            MS_SYNC            完成写操作后函数返回
        说  明:进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执行该操作。
                可以通过调用msync()函数来实现磁盘文件内容与共享内存区中的内容一致,即同步操作。
        头文件:#include <sys/mman.h>
    5.通过/dev/mem设备文件和mmap系统调用,可以将线性地址描述的物理内存映射到进程的地址空间,然后就可以直接访问这段内存了。
    

    二、一个例子

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/mman.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    int main (int args, char* arg[])
    {
        int i;
        int fd;
        char* mem;
        char *buff = "HELLO";
        //open /dev/mem with read and write mode
        if((fd = open ("/dev/mem", O_RDWR)) < 0)
        {
            perror ("open error");
            return -1;
        }
    
        //map physical memory 0-10 bytes
        mem = mmap (0, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (mem == MAP_FAILED)
        {
            perror ("mmap error:");
            return 1;
        }
    
        //Read old value
        for (i = 0; i < 5; i++)
        {
            printf("\nold mem[%d]:%d", i, mem[i]);
        }
    
        printf(The value is 0x%x\n, *((int *)mem));
    
        //write memory
        memcpy(mem, buff, 5);
        //Read new value
        for (i = 0; i<5 ; i++)
        {
            printf("\nnew mem[%d]:%c", i, mem[i]);
        }
    
        printf("\n");
        munmap (mem, 10); //destroy map memory
        close (fd);   //close file
        return 0;
    }
    展开全文
  • Linux读写物理内存的实践环节

    千次阅读 2016-02-19 08:59:14
    通常只有root用户对其有读写权限。因此只有root用户能进行这些操作。 如果要打开设备文件/dev/mem,需要系统调用open()函数,作用是打开一个文件或设备,其函数原型为: int open(const char *path, int flags); ...

    一、基础知识

    1.打开设备文件:
        mem是一个字符设备文件,是计算机主存的一个映像。通常只有root用户对其有读写权限。因此只有root用户能进行这些操作。
        如果要打开设备文件/dev/mem,需要系统调用open()函数,作用是打开一个文件或设备,其函数原型为:
        int open(const char *path, int flags);
        返回值:如果操作成功则返回一个文件描述符,否则返回-1
        形  参:
                path      被打开文件的路径即文件名描述。
                flags     文件的访问模式描述,可常用的选项见下:
                            O_RDONLY        只读方式
                            O_WRONLY        只写方式
                            O_RDWR          可读写方式
        说  明:此函数用于打开文件或者设备
        头文件:#include <fcntl.h> #include <stat.h>  定义在/usr/include/fcntl.h中
    2.读取内存映像:
        内存映像其实在内存中创建一个与外存中文件完全相同的映像。用户可以将整个文件映射到内存中,也可以将文件的一部分映射到内存中。
        使用操作内存的方法对文件进行操作。系统会将内存映像文件所做的改动反映到真实文件中去。
        在内存映像I/O的实现过程中需要用到一些系统调用:
        首先是创建内存映像文件的系统调用mmap()函数,其函数原型为:
        void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
        返回值:成功时,返回值为指向内存映像起始地址的指针,当调用失败时,返回值为-1
        形  参:
                start      一个void指针,表示希望将文件映射到此指针指向的位置,通常为NULL。
                length     定义内存映像文件所占用的内存空间大小,以字节计。
                prot       内存映像文件的安全属性,注意和open函数中的flags属性保持一致。它的可使用的选项如下:
                              Flags                          含义
                            PROT_EXEC       被映像内存可能含义机器码,可被执行
                            PROT_NONE       映像内存不允许访问
                            PROT_READ       映像内存可读
                            PROT_WRITE      映像内存可写
                flags      内存映像的标志,选项如下:
                              Flags                                   含义
                            MAP_FIXED       指定映射起始地址,如果由start和len指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。
                                            如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。
                            MAP_SHARED      与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。
                                            直到msync()或者munmap()被调用,文件实际上不会被更新。 
                            MAP_PRIVATE     建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。
                                            这个标志和以上标志是互斥的,只能使用其中一个。 
                            MAP_NORESERVE   不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。
                                            当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。
                            MAP_LOCKED      锁定映射区的页面,从而防止页面被交换出内存。 
                            MAP_GROWSDOWN   用于堆栈,告诉内核VM系统,映射区可以向下扩展。
                            MAP_ANONYMOUS   匿名映射,映射区不与任何文件关联。
                            MAP_ANON        MAP_ANONYMOUS的别称,不再被使用。 
                            MAP_FILE        兼容标志,被忽略。 
                            MAP_32BIT       将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。
                                            当前这个标志只在x86-64平台上得到支持。
                            MAP_POPULATE    为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。 
                            MAP_NONBLOCK    仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。
                fd         要映射的文件的描述符。
                offset     所映射的数据内容 距离文件头的偏移量。
        说  明:此函数用于将一个文件或它的一部分映射到内存中。
        头文件:#include <sys/mman.h> #include <sys/types.h>  函数定义在/usr/include/sys/mman.h
    3.撤销内存映像的修改
        另外我们使用完内存映像文件后,要用系统调用函数munmap()函数来撤销,其函数原型为:
        int munmap(void *start, size_t length);
        返回值:成功时,返回值为0;调用失败时返回值为 -1,并将errno设置为相应值。
        形  参:
                start      要撤销的内存映像文件的起始地址。
                length     要撤销的内存映像文件的大小。
        说  明:当进程结束或利用exec相关函数来执行其他程序时,映射内存会自动解除,但关闭对应的文件描述词时不会解除映射。
        头文件:#include <sys/mman.h>
    4.将内存映像的改动保存到外存中
        最后,如果我们要将内存映像的改动保存到外存中,还需要系统调用msync()函数,其函数原型为:
        int msync(const void *start,size_t length,int flags);
        返回值:成功时,返回值为0;调用失败时返回值为 -1,并将errno设置为相应值。
        形  参:
                start      要保存到外存的那些源文件的起始地址。
                length     表示内存映像文件的大小。
                flags      设置了函数的相应操作,其具体选项如下:
                              Flags                      含义
                            MS_ASYNC           调用一个写操作并返回
                            MS_INVALIDATE      映像到相同文件的内存映像数据更新
                            MS_SYNC            完成写操作后函数返回
        说  明:进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执行该操作。
                可以通过调用msync()函数来实现磁盘文件内容与共享内存区中的内容一致,即同步操作。
        头文件:#include <sys/mman.h>
    5.通过/dev/mem设备文件和mmap系统调用,可以将线性地址描述的物理内存映射到进程的地址空间,然后就可以直接访问这段内存了。
    

    二、一个例子

    1. #include <stdio.h>  
    2. #include <unistd.h>  
    3. #include <sys/mman.h>  
    4. #include <sys/types.h>  
    5. #include <sys/stat.h>  
    6. #include <fcntl.h>  
    7. int main (int args, char* arg[])  
    8. {  
    9.     int i;  
    10.     int fd;  
    11.     char* mem;  
    12.     char *buff = "HELLO";  
    13.     //open /dev/mem with read and write mode  
    14.     if((fd = open ("/dev/mem", O_RDWR)) < 0)  
    15.     {  
    16.         perror ("open error");  
    17.         return -1;  
    18.     }  
    19.   
    20.     //map physical memory 0-10 bytes  
    21.     mem = mmap (0, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);  
    22.     if (mem == MAP_FAILED)  
    23.     {  
    24.         perror ("mmap error:");  
    25.         return 1;  
    26.     }  
    27.   
    28.     //Read old value  
    29.     for (i = 0; i < 5; i++)  
    30.     {  
    31.         printf("\nold mem[%d]:%d", i, mem[i]);  
    32.     }  
    33.   
    34.     printf(The value is 0x%x\n, *((int *)mem));  
    35.   
    36.     //write memory  
    37.     memcpy(mem, buff, 5);  
    38.     //Read new value  
    39.     for (i = 0; i<5 ; i++)  
    40.     {  
    41.         printf("\nnew mem[%d]:%c", i, mem[i]);  
    42.     }  
    43.   
    44.     printf("\n");  
    45.     munmap (mem, 10); //destroy map memory  
    46.     close (fd);   //close file  
    47.     return 0;  

    //


    另一个例子:

    使用 hexedit /dev/mem 可以显示所有物理内存中的信息。 运用mmap将/dev/mem map出来,然后直接对其读写可以实现用户空间的内核操作。

    以下是我写的一个sample


    1. #include<stdio.h>  
    2. #include<unistd.h>  
    3. #include<sys/mman.h>  
    4. #include<sys/types.h>  
    5. #include<sys/stat.h>  
    6. #include<fcntl.h>  
    7.   
    8. int main()  
    9. {  
    10.     unsigned char * map_base;  
    11.     FILE *f;  
    12.     int n, fd;  
    13.   
    14.     fd = open("/dev/mem", O_RDWR|O_SYNC);  
    15.     if (fd == -1)  
    16.     {  
    17.         return (-1);  
    18.     }  
    19.   
    20.     map_base = mmap(NULL, 0xff, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0x20000);  
    21.   
    22.     if (map_base == 0)  
    23.     {  
    24.         printf("NULL pointer!\n");  
    25.     }  
    26.     else  
    27.     {  
    28.         printf("Successfull!\n");  
    29.     }  
    30.   
    31.     unsigned long addr;  
    32.     unsigned char content;  
    33.   
    34.     int i = 0;  
    35.     for (;i < 0xff; ++i)  
    36.     {  
    37.         addr = (unsigned long)(map_base + i);  
    38.         content = map_base[i];  
    39.         printf("address: 0x%lx   content 0x%x\t\t", addr, (unsigned int)content);  
    40.   
    41.         map_base[i] = (unsigned char)i;  
    42.         content = map_base[i];  
    43.         printf("updated address: 0x%lx   content 0x%x\n", addr, (unsigned int)content);  
    44.     }  
    45.   
    46.     close(fd);  
    47.   
    48.     munmap(map_base, 0xff);  
    49.   
    50.     return (1);  
    51. }  


    上面的例子将起始地址0x20000(物理地址), 长度为0xff映射出来。 然后就可以像普通数组一样操作内存。

    下面是输出结果

    address: 0x7f3f95391000   content 0x0           updated address: 0x7f3f95391000   content 0x0
    address: 0x7f3f95391001   content 0x0           updated address: 0x7f3f95391001   content 0x1
    address: 0x7f3f95391002   content 0x0           updated address: 0x7f3f95391002   content 0x2
    address: 0x7f3f95391003   content 0x0           updated address: 0x7f3f95391003   content 0x3
    address: 0x7f3f95391004   content 0x0           updated address: 0x7f3f95391004   content 0x4
    。。。

    我的测试机器是64位机。 该例子将物理地址0x20000映射到了虚拟地址0x7f3f95391000。

    首先将当前地址下的内容输出, 然后写入新值。

    可以通过 hexedit /dev/mem 验证新值已经写入。


    如果想在用户态处理kernel分配的地址可以这么做。 首先用virt_addr = get_free_pages(GFP_KERNEL, order)分配内存,通过phy_addr = __pa(virt_addr)得到物理地址,然后在用户态将/dev/mem用mmap 映射出来, offset就是phy_addr, length设为 2^order。 此时就可以在用户态读写内核分配的内存了。

    注:该操作需要有root权限。linux


    根据以上提供的参考问下,做如下实验

    查看某一个进程的内存地址空间,并用hexedit显示出来。

    a. 使用top命令查看某个进程的运行情况。

    $ top -p 进程号

    信息解释:

      PID:进程的ID
      USER:进程所有者
      PR:进程的优先级别,越小越优先被执行
      NInice:值
      VIRT:进程占用的虚拟内存
      RES:进程占用的物理内存
      SHR:进程使用的共享内存
      S:进程的状态。S表示休眠,R表示正在运行,Z表示僵死状态,N表示该进程优先值为负数
      %CPU:进程占用CPU的使用率
      %MEM:进程使用的物理内存和总内存的百分比
      TIME+:该进程启动后占用的总的CPU时间,即占用CPU使用时间的累加值。
      COMMAND:进程启动命令名称

    b. 查看某一个进程的内存各种地址空间的范围。

    有如下方法可以参考:

    ------>查看RAM使用情况最简单的方法是通过 /proc/meminfo。这个动态更新的虚拟文件实际上是许多其他内存相关工具(如:free / ps / top)等的组合显示。/proc/meminfo列出了所有你想了解的内存的使用情况。

    ------>进程的内存使用信息也可以通过 /proc/?/statm 和 /proc/?/status 来查看。

    ------>memstat是一个有效识别 executable(s), process(es) and shared libraries使用虚拟内存情况的命令。给定一个进程ID,memstat可以列出这个进程相关的可执行文件、数据和共享库。(需要安装)

    ------>Linux 程序的内存地址空间所映射的文件列表存在该程序所对应的maps文件中,maps文件在/proc目录下。
    例如程序的进程ID为123,其maps文件的地址是/proc/123/maps。

    ------>参考linux源码中的Document\filesystems\proc.txt  (proc文件的系统的帮助文档)

    ------>Linux:pmap命令,查看进程占用的内存及使用地址空间


    关于MAPS文件的解释:

    几乎看什么内核漏洞分析之类的文章,第一眼都是被 cat /proc/{pid}/maps
    产生的一堆奇怪的信息唬住了,都不敢往下看
    居然没有搜到关于它们的详细介绍,旁边也没有表头什么的解释信息
    那就我来整理一下吧,方便后人。
     
    ls /proc 能看到一些数字命名的文件夹,这个就不多说了,什么pid、fd的知识还是容易弄到的
    拿init开刀,


    [root@localhost proc]# cat /proc/1/statm
    487 185 133 31 0 67 0

    很简单地返回7组数字,每一个的单位都是一页 (常见的是4KB)
    分别是

    size:任务虚拟地址空间大小
    Resident:正在使用的物理内存大小
    Shared:共享页数
    Trs:程序所拥有的可执行虚拟内存大小
    Lrs:被映像倒任务的虚拟内存空间的库的大小
    Drs:程序数据段和用户态的栈的大小
    dt:脏页数量

    接下来看看maps
    [root@localhost proc]# cat /proc/1/maps
    00110000-00111000 r-xp 00110000 00:00 0          [vdso]
    0032b000-00347000 r-xp 00000000 fd:00 852733     /lib/ld-2.8.so
    00347000-00348000 r--p 0001c000 fd:00 852733     /lib/ld-2.8.so
    00348000-00349000 rw-p 0001d000 fd:00 852733     /lib/ld-2.8.so
    0034b000-004ae000 r-xp 00000000 fd:00 852734     /lib/libc-2.8.so
    004ae000-004b0000 r--p 00163000 fd:00 852734     /lib/libc-2.8.so
    004b0000-004b1000 rw-p 00165000 fd:00 852734     /lib/libc-2.8.so
    004b1000-004b4000 rw-p 004b1000 00:00 0
    08048000-08067000 r-xp 00000000 fd:00 843075     /sbin/init
    08067000-08068000 rw-p 0001e000 fd:00 843075     /sbin/init
    08b42000-08b6a000 rw-p 08b42000 00:00 0          [heap]
    b8046000-b8048000 rw-p b8046000 00:00 0
    bfb4e000-bfb63000 rw-p bffeb000 00:00 0          [stack]


    第一列代表内存段的虚拟地址
    第二列代表执行权限,r,w,x不必说,p=私有 s=共享。不用说,heap和stack段不应该有x,否则就容易被xx,不过这个跟具体的版本有关
    第三列代表在进程地址里的偏移量
    第四列映射文件的主设备号和次设备号,通过 cat /proc/devices,得知fd是253 device-mapper
    第五列映像文件的节点号,即inode
    第六列是映像文件的路径
    以前我很奇怪怎么会有两个相同的文件路径,原来
    08048000-08067000 r-xp 00000000 fd:00 843075     /sbin/init
    08067000-08068000 rw-p 0001e000 fd:00 843075     /sbin/init
    一个是只读的,是代码段,一个是读写的,是数据段
    至于为什么共享库分成了三个
    0034b000-004ae000 r-xp 00000000 fd:00 852734     /lib/libc-2.8.so
    004ae000-004b0000 r--p 00163000 fd:00 852734     /lib/libc-2.8.so
    004b0000-004b1000 rw-p 00165000 fd:00 852734     /lib/libc-2.8.so
    其中的004ae000-004b0000 r--p 00163000 fd:00 852734     /lib/libc-2.8.so
    还是不能理解

    最后看看神秘的memmap
    可惜不是所有的版本都有这个文件,如同我这里的情况
    只有口头说一说了,memmap需要和maps配合看
    显示出来的每一行代表maps里的一个段
    每一行的每一个数字,代表那个段里的一个页,段有多大,一行就有对应的多少个数字
    如果这个数字是0,代表系统为其分配了虚拟内存,但是没有分配物理内存
    如果有数字,则代表系统为其分配了物理内存,以及有多少个进程引用了这个页
    当然,不会有负数出现。

    c.用hexedit工具来读取上面某个进程的内存地址空间,验证是否一致。

       
    hexedit读取的是内存物理地址的信息,但是上面的内容都是基于线性地址的信息,这里需要一个转换的过程。从linux中的命令没有找到合适的方法

    来实现这个转换的需求。待解决。。。。。。。。。。。。。。。




    参考文献:

    http://blog.csdn.net/zsf8701/article/details/7814988

    http://www.linuxidc.com/Linux/2011-09/42031.htm

    http://blog.csdn.net/mldxs/article/details/16993315

    展开全文
  • //拿到物理地址对应的内存结构实例 page,这是Linux物理内存管理结构,每个page结构实例表示一块物理内存 //kmap要的是page结构的虚拟地址 unsigned long vptr=kmap(_pageaddr); //将该物理地址映射到内核的虚拟...

    研究Linux内存管理过程中,走了不少弯路,内存管理在Linux内核中是很重要,且很复杂的一部分,这里记录我对Linux内存管理结构的了解。

    首先对涉及内存的几个术语解释:

    虚拟地址空间:

            这是系统视图的地址空间,也是我们使用内存主要用到的地址空间

    物理地址空间:

            CPU视图的地址空间,MMU会将虚拟地址转换为物理地址来访问内存。

    MMU:

             内存管理单元,主要负责对内存管理(地址转换、访问控制等)

     

    Linux内存映射原理:

         首先我们只能使用虚拟地址,但访问内存只能通过物理地址,所以虚拟地址首先会通过MMU转换为物理地址,然后经过总线就可以寻址到内存了,核心是 虚拟地址转 物理地址。

    Linux使用4级页表来转换物理地址,每级页表存储虚拟内存地址的部分偏移,这里可以理解为索引吧,以下为4级页表解释

    PGD 全局页目录

    PUD 上层页面目录

    PMD 中间页目录

    PTE 页表项(页表项即为物理地址了)

    下图说明了一个虚拟地址,被拆分了以下部分,每个部分对应于页表的偏移,当我们访问内存时,就是根据这些信息来获取内存的物理地址的。

     

     

     

    当我们在程序内部使用变量内容时,如下:

    int main(int argc,char *argv[])
    {
        int *ptr=(int*)malloc(sizeof(unsigned int));
        *ptr=1992;
        while(1)
        {
            printf("pid %ld vaddr %ld value %d\n",getpid(),ptr,*ptr);
            sleep(2);
        }
        return 0;
    }

    这里指针变量存储了该内存对应的虚拟地址,当我们访问时该内存是,首先根据当前进程的实例(task_struct->mm),获取到进程的pgd,然后依次获取 pud、pmd、pte。

    虚拟地址和物理地址之间的关联即通过页表的方式进行映射。

    在内核中我们可以通过编写内核驱动的方式来访问任意进程的内存(内核中没有任何限制),内核读写任意进程内存核心实现代码如下:

    static int vaddrtopaddr(struct task_struct *p,unsigned long vaddr)
    {
    	struct mm_struct *mm=p->mm;
    	pgd_t *pgd;
        pud_t *pud;
        pmd_t *pmd;
        pte_t *pte;
    	unsigned long kks;
        unsigned long paddr = 0;
        unsigned long page_addr = 0;
        unsigned long page_offset = 0;
        
    	pgd =pgd_offset(mm,vaddr);
        if (pgd_none(*pgd)) {
            printk("not mapped in pgd\n");
            return -1;
        }
     
        pud = pud_offset((p4d_t*)pgd, vaddr);
        printk("pud_val = 0x%lx\n", pud_val(*pud));
        if (pud_none(*pud)) {
            printk("not mapped in pud\n");
            return -1;
        }
     
        pmd = pmd_offset(pud, vaddr);
        if (pmd_none(*pmd)) {
            printk("not mapped in pmd\n");
            return -1;
        }
     
        pte = pte_offset_kernel(pmd, vaddr);
        if (pte_none(*pte)) {
            printk("not mapped in pte\n");
            return -1;
        }
    	//页框地址 页的物理地址
    	page_addr = pte_val(*pte) & PAGE_MASK;
        page_offset = vaddr & ~PAGE_MASK; //虚拟地址在物理地址中的偏移
    
    	paddr = page_addr | page_offset; //内存的物理地址
    	unsigned long _pageaddr=pte_page(*pte); //拿到物理地址对应的内存结构实例 page,这是Linux的物理内存管理结构,每个page结构实例表示一块物理内存
    
        //kmap要的是page结构的虚拟地址
        unsigned long vptr=kmap(_pageaddr); //将该物理地址映射到内核的虚拟地址空间中,这样才能访问该物理内存。
    	if(vptr<=0)
    	{
    		printk("vptr is null");
    	}
    	else
    	{
    		printk("vptr 0x%lx vaddr value 0x%lx\n",vptr,vptr+page_offset);
    		unsigned int *vint=(unsigned int*)(vptr+page_offset);
    		printk("int val %d\n",*vint);
    		*vint=2021;
    	}
    	kunmap(page_addr);
    	return 1; 
    
    }
    

    完整代码 Git

    展开全文
  • Linux物理内存和虚拟内存交换机制

    千次阅读 2016-03-17 22:29:39
    Linux物理内存和虚拟内存交换机制   Vmstat是Virtual Memory Statistics虚拟内存统计缩写: 物理内存是计算机内存的大小,从物理内存读写数据比硬盘中读写数据要快很多,而内存是有限的,所以就有了物理内存和...
    Vmstat是Virtual Memory Statistics虚拟内存统计缩写:
    物理内存是计算机内存的大小,从物理内存中读写数据比硬盘中读写数据要快很多,而内存是有限的,所以就有了物理内存和虚拟内存的概念。
    物理内存就是硬件的内存,是真正的内存。
    虚拟内存是为了满足物理内存不足采用的策略,利用磁盘空间虚拟出一块逻辑内存,用作虚拟内存的空间也就是交换分区。
    作为物理内存的扩展,Linux会在物理内存不足时,使用交换分区的逻辑内存,内核会把暂时不用的内存块信息写到交换空间,这样物理内存就得到了释放,这块儿内存就可以用于其他目的,而需要用到这些内容的时候,这些信息就会被重新从交换分区读入物理内存。
    Linux的内存管理采用的是分页存取机制,为了保证物理内存得到充分的利用,内核会在适当的时间把物理内存中不经常使用的数据块儿自动交换到虚拟内存中,而将充分使用的信息保留到物理内存中。
    相关阅读:
    Linux物理内存探测 http://www.linuxidc.com/Linux/2012-01/50762.htm
    Linux物理内存描述 http://www.linuxidc.com/Linux/2012-01/50761.htm
    Linux物理内存管理区初始化 http://www.linuxidc.com/Linux/2012-01/50757.htm
    Linux虚拟内存优化的细节学习 http://www.linuxidc.com/Linux/2007-01/1759.htm

    Linux环境下虚拟内存的建立 http://www.linuxidc.com/Linux/2008-07/14107.htm


    需要主机的内存运行机制特点:

    1:Linux系统会不时的进行页面交换操作,以保持尽可能多的空闲物理内存,即使并没有什么需要内存,Linux也会交换出暂时不用的内存页面,这样的可以避免等待交换所需的时间。
    2:Linux进行页面交换是有条件的,不是所有页面在不用时都交换到物理内存,Linux内核根据最近经常使用的算法,仅仅将一些不经常使用的页面文件交换到虚拟内存中。有时会看到这样的现象:Linux物理内存还有很多,但是交换空间也用了很多,这很正常。比如,有一个占用很大内存的进程运行的时候,需要耗费很多内存资源,此时有一些不常用页面文件被交换到虚拟内存中,但后来这个占用很多虚拟内存的进程结束并释放了物理内存之后,刚才被交换出去的页面文件并不会自动的交换进物理内存,除非有这个必要。
    最后交换空间的页面在使用的时候,首先要被交换到物理内存中,如果没有足够的物理内存,他们又会马上被交换出去,这样,虚拟内存中没有足够空间来存储这些交换页面,导致Linux出现服务异常这些情况。
    虚拟内存原理:
    在内存中运行的每个进程都需要使用到内存,但是不是每个进行都需要每时每刻都使用系统分配的内存空间。但系统所需内存超过实际的物理内存,内核会释放某些进程所占用,但是未使用的部分或者所有物理内存,把这些资料存储在磁盘上,直到下一次调用,并将释放出来的内存提供给有需要的内存使用。
    Linux内存管理中,通过调页paging和交换swapping来完成上述的内存调度。调页算法是吧内存中最近不常使用的页面换到磁盘中,把活动页面保留在内存中供进程使用。交换技术是吧整个进程,而不是部分页面全部交换到磁盘中。
    分页写入磁盘的过程为:page-out,分页从硬盘重新回到内存的过程被称为page-in,内核需要一个分页的时候,发现此分页不再内存中,,就会发生page fault.



    Vmstat字段说明:

    Procs:进程,r:运行队列中进程数量,b:等待io的进程数量,
    Memory内存:swpd:使用虚拟内存大小,free:可用内存大小,buff:用作缓冲的内存大小,cache:用作缓存的内存大小
    Swap:si:每秒从交换区写到内存的大小,so:每秒写入交换区的大小。
    IO:(现在Linux下块的大小为1024bytes)bi:每秒读取的块数,bo:每秒写入的块数。
    System:in:每秒中断数,包括时钟中断。Cs:每秒上下文切换。
    CPU:us:用户进程执行时间,sy,系统时间,idle空闲时间,wa,等待io时间。


    查看内存使用的详细信息:vmstat –s



    查看磁盘的读写命令:vmstat–d



    查看磁盘的读写命令:vmstat–p /dev/sda1


    Reads:来自这个分区的读得信息,read sectors:来自这个分区的读扇区的次数,writes来自这个分区的写的次数,requestedwrites:来自这个分区的写请求次数


    转自:http://www.linuxidc.com/Linux/2013-07/87912.htm

    展开全文
  • 我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概念 Linux物理内存和虚拟内存 物理内存:系统...
  • linux物理内存

    千次阅读 2008-09-23 15:55:00
    linux系统对于物理内存管理主要有下面3次探测的过程: 1:第一次探测 Arch/i386/boot/setup.S在这里使用了三种方法,通过调用0x15 中断:E820H 得到memory map。E801H 得到大小。88H 0-64M. 2:第二次探测:start_...
  • 我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概念。 物理内存就是系统硬件提供的内存大小,...
  • Vmstat是Virtual Memory Statistics虚拟内存统计缩写:物理内存是计算机内存的大小,从物理内存读写数据比硬盘中读写数据要快很多,而内存是有限的,所以就有了物理内存和虚拟内存的概念。物理内存就是硬件的内存...
  • linux 物理内存读写

    千次阅读 2014-01-19 22:26:23
    #include #include #include #include #include #include main() {  int i, j;  int words = 10;  int devmem;  off_t PageOffset, PageAddress;... unsigned long *hw_ad
  • Linux 物理内存直接读写工具devmem

    千次阅读 2019-03-03 16:48:47
    目录 概述 用法 dev/mem介绍 devmem的源码分析 ...在Linux开发中着实用到的调试工具并不是很多。...基本原理通过设备文件/dev/mem 实现对物理内存读写。 用法 Usage: devmem ADDRESS [WIDTH [VALUE...
  • Linux下直接读写物理地址内存

    千次阅读 2019-11-05 10:01:47
    虚拟 转 物理地址 virt_to_phys( *addr );物理 转 虚拟地址 phys_to_virt( *addr );--------------------...
  •  我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概念。  物理内存就是系统硬件提供的内存...
  • 物理内存是计算机内存的大小,从物理内存读写数据比硬盘中读写数据要快很多,而内存是有限的,所以就有了物理内存和虚拟内存的概念。 物理内存就是硬件的内存,是真正的内存。 虚拟内存是为了满足物理内存不足...
  • 由于开发需要,需要通过memory传输数据,所以使用devmem 方式读写数据,操作linux 内存数据。devmem的方式是提供给驱动开发人员,在应用层能够侦测内存地址中的数据变化,以此来检测驱动中对内存或者相关配置的正确...
  • 要理解虚拟内存首先要明白为什么需要虚拟内存,物理内存就是我们通常所说的内存条。我们知道,相比于从硬盘中读写数据,直接读写内存的速度要快很多,但是内存的大小要比硬盘小很多。 要知道每个进程都有自己独立的...
  • Linux 虚拟内存和物理内存的管理

    千次阅读 2016-07-16 10:55:57
    Linux 虚拟内存和物理内存的理解 关于Linux 虚拟内存和物理内存的理解。 首先,让我们看下虚拟内存: 第一层理解 1. 每个进程都有自己独立的4G内存空间,各个进程的内存空间具有类似的结构 2. 一个新进程建立的时候...
  • 关于Linux 虚拟内存和物理内存的理解。 首先,让我们看下虚拟内存: 第一层理解 1. 每个进程都有自己独立的4G内存空间,各个进程的内存空间具有类似的结构 2. 一个新进程建立的时候,将会建立起自己的内存空间...
  • documentlinux内存机制CPU内存虚拟内存硬盘物理内存内存和虚拟内存跟 Windows 完全不同的 Linux 内存机制Swap配置对性能的影响 linux内存机制 Linux支持虚拟内存(Virtual Mmemory),虚拟内存是指使用磁盘当作RAM的...
  • 我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概念。 物理内存就是系统硬件提供的内存大小,...
  • linux内核虚拟内存和物理内存的映射

    千次阅读 2018-08-24 15:06:16
    NUMA指CPU对不同内存单元的访问时间可能不一样,因而这些物理内存被划分为几个节点,每个节点里的内存访问时间一致,NUMA体系结构主要存在大型机器、alpha等,嵌入式的基本都是UMA。UMA也使用了节点概念,只是永远都...
  • Linux物理内存按固定大小的页面(一般为4K)划分内存,在内核初始化时,会建立一个全局struct page结构数组mem_map[ ]。如系统中有76G物理内存,则物理内存页面数为76*1024*1024k/4K= 19922944个页面,mem_map[ ]...
  • 关于Linux 虚拟内存和物理内存的理解。 首先,让我们看下虚拟内存: 第一层理解 每个进程都有自己独立的4G内存空间,各个进程的内存空间具有类似的结构 一个新进程建立的时候,将会建立起自己的内存空间,此进程的...
  • linux物理内存描述

    千次阅读 2011-12-31 10:36:00
    linux使用于广泛的体系结构,因此需要用一种与体系结构无关的方式来描述内存linux用VM描述和管理内存。在VM中兽药的普遍概念就是非一致内存访问。对于大型机器而言,内存会分成许多簇,依据簇与处理器“距离”的...
  • Linux内核采用页式存储管理,进程的地址空间被划分成固定大小的“页面”(page),物理内存同样被分为与页面大小相同的“页帧“(page frame),由MMU在运行时将虚拟地址“映射”成某个物理内存页帧上的地址,MMU...
  • 相反,对于用户态申请内存的请求,内核总是尽量延后分配物理内存,用户进程总是先获得一个虚拟内存区的使用权,最终通过缺页异常获得一块真正的物理内存。 1.物理内存的内核映射 IA32架构中内核虚拟地址空间只有...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 129,504
精华内容 51,801
关键字:

linux读取物理内存

linux 订阅