精华内容
下载资源
问答
  • 液晶屏在设计驱动的时候,都要设置显存,液晶屏上显示的内容都是来自于显存。把要显示的内容(数据)写入显存,液晶屏上就显示图片、文字...在这里使用内存映射(mmap函数)实现了对bmp图片显示,硬件平台为GEC6818
  • 一、现在,我们能够正常显示一张800*480 24位bmp格式图片,但是是使用write写入数据到lcd设备上,速度比较慢,所以想办法写入数据时,不要使用write(),使用内存映射。 1、使用write()函数实现的代码。 可以显示24...

    一、现在,我们能够正常显示一张800*480 24位bmp格式图片,但是是使用write写入数据到lcd设备上,速度比较慢,所以想办法写入数据时,不要使用write(),使用内存映射。

    1、使用write()函数实现的代码。
    可以显示24位,800*480像素点的图片。
    int show_bmp(const char *bmp_path)
    {
        FILE *fp;
        int n,lcd;
        int i,j;
        int x,y;
        char bmp_buf[800*480*3] = {0};
        char lcd_buf[800*480*4] = {0};
        char show_buf[800*480*4] = {0};
        
        /*1. 打开图片 */
        fp = fopen(bmp_path,"r"); //默认在最开头
        if(fp == NULL)
            printf("fopen error!\n");
        
        /*2. 打开lcd设备 */
        lcd = open("/dev/fb0",O_RDWR);
        if(lcd < 0)
            printf("open fb0 error!\n");
        
        /*3. 先跳过54个头数据 */
        fseek(fp,54,SEEK_SET);
        
        /*4. 将图片的数据读取到缓冲区中 */
        n = fread(bmp_buf,800*480*3,1,fp);
        if(n!=1)
            printf("fread error!\n");
        
        /*5. 将24位转32位 */
        for(i=0,j=0;i<800*480*4;i+=4,j+=3)
        {
            lcd_buf[i] = bmp_buf[j];      
            lcd_buf[i+1] = bmp_buf[j+1];    
            lcd_buf[i+2] = bmp_buf[j+2];    
            lcd_buf[i+3] = 0;               
        }
        
        /* 6. 上下颠倒 */
        for(y=0;y<480;y++)
        {
            for(x=0;x<800*4;x++)
            {
                show_buf[800*4*y+x] = lcd_buf[800*4*(479-y)+x];
            }
        }

        //show_buf就是图片的正常内容。
        
        /* 7. 将缓冲区的数据写入到屏幕上 */
        n = write(lcd,show_buf,sizeof(show_buf));
        if(n!=sizeof(show_buf))
            printf("write error!\n");
        
        /* 8. 关闭文件 */
        fclose(fp);
        close(lcd);    
    }

    2、使用内存映射的思路。
    1)分析到show_buf就是图片的正常内容。

    show_buf[0]  -> 第一个像素点的第一个字节
    show_buf[1]  -> 第一个像素点的第二个字节
    show_buf[2]  -> 第一个像素点的第三个字节
    show_buf[3]  -> 第一个像素点的第四个字节
    show_buf[4]  -> 第二个像素点的第一个字节
    show_buf[5]  -> 第二个像素点的第二个字节

    2)产生一片内存空间,然后将像素点数据写入到内存中。
       p = mmap();
       memcpy(p);

    3)自然lcd上就会有对应的颜色。

       练习1: 使用内存映射的方式来完成显示图片,即完成mmap_show_bmp()函数。

    int mmap_show_bmp(const char *bmp_path)
    {
        FILE *fp;
        int n,lcd;
        int i,j;
        int x,y;
        int k;
        char bmp_buf[800*480*3] = {0};
        char lcd_buf[800*480*4] = {0};
        char show_buf[800*480*4] = {0};
        
        /*1. 打开图片 */
        fp = fopen(bmp_path,"r"); //默认在最开头
        if(fp == NULL)
            printf("fopen error!\n");
        
        /*2. 打开lcd设备 */
        lcd = open("/dev/fb0",O_RDWR);
        if(lcd < 0)
            printf("open fb0 error!\n");
        
        /*3. 先跳过54个头数据 */
        fseek(fp,54,SEEK_SET);
        
        /*4. 将图片的数据读取到缓冲区中 */
        n = fread(bmp_buf,800*480*3,1,fp);
        if(n!=1)
            printf("fread error!\n");
        
        /*5. 将24位转32位 */
        for(i=0,j=0;i<800*480*4;i+=4,j+=3)
        {
            lcd_buf[i] = bmp_buf[j];      
            lcd_buf[i+1] = bmp_buf[j+1];    
            lcd_buf[i+2] = bmp_buf[j+2];    
            lcd_buf[i+3] = 0;               
        }
        
        /* 6. 上下颠倒 */
        for(y=0;y<480;y++)
        {
            for(x=0;x<800*4;x++)
            {
                show_buf[800*4*y+x] = lcd_buf[800*4*(479-y)+x];
            }
        }
        
        /* 7. 产生一片内存空间,作为映射 */
        char *p = (char *)mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd,0);
        if(p == (void *)-1)
            printf("mmap error!\n");
        
        /* 8. 将数据拷贝到内存上 */
        for(k=0;k<800*480*4;k++)
        {
            memcpy(p+k,&show_buf[k],1);
        }
        
        /* 7. 关闭文件 */
        munmap(p,800*480*4);
        fclose(fp);
        close(lcd);
        
        return 0;
    }

       练习2: 完成以下的函数。
           show_all_bmp("./xx.bmp",200,200,300,200)

    "./xx.bmp"  -> 显示图片的路径。
    200: 显示的起点的x轴坐标。
    200: 显示的起点的y轴坐标。
    300: 图片的宽度。
    200: 图片的高度。

    参考: 显示任意大小的图片.jpg

    int show_all_bmp(const char *bmp_path,int s_x,int s_y,int wide,int high)
    {
        FILE *fp = NULL;
        int i,j;
        int x,y;
        int lcd;
        int k = 0;
        char *p = NULL;
        int x_max = (s_x+wide)*4;
        int y_max = s_y + high;
        
        char bmp_buf[wide*high*3];
        char lcd_buf[wide*high*4];
        char show_buf[wide*high*4];
        
        //1. 先检查图片尺寸是否允许
        if( (s_x+wide) > 800 || (s_y+high) > 480 )
        {
            return -1;
        }

        
        //2. 访问图片
        fp = fopen(bmp_path,"r");
        if(fp == NULL)
            printf("fopen error!\n");
        
        //3. 跳过54个头数据
        fseek(fp,54,SEEK_SET);
        
        //4. 将图片的内容读取出来
        fread(bmp_buf,wide*high*3,1,fp);
        
        //5. 24位转32位
        for(i=0,j=0;i<wide*high*4;i+=4,j+=3)
        {
            lcd_buf[i] = bmp_buf[j];
            lcd_buf[i+1] = bmp_buf[j+1];
            lcd_buf[i+2] = bmp_buf[j+2];
            lcd_buf[i+3] = 0;
        }
        
        
        //6. 上下颠倒。
        for(y=0;y<high;y++)
        {
            for(x=0;x<wide*4;x++)
            {
                show_buf[wide*4*y+x] = lcd_buf[wide*4*(high-1-y)+x];
            }
        }
        //现在:show_buf就是小图片的正常数据
        
        //7. 访问lcd设备
        lcd = open("/dev/fb0",O_RDWR);
        if(lcd < 0)
            printf("open lcd error!\n");
        
        //8. 内存映射
        p = (char*)mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd,0);
        if(p == (void *)-1)
            printf("mmap error!\n");
        
        //9.将show_buf的内容刷到内存上
        for(y=s_y;y<y_max;y++)
        {
            for(x=s_x*4;x<x_max;x++)
            {
                memcpy(p+800*4*y+x,&show_buf[k],1);
                k++;
            }
        }
        
        //10. 撤销映射,然后关闭文件。
        munmap(p,800*480*4);
        close(lcd);
        fclose(fp);
        
        return 0;
    }

    二、目录IO函数接口。
    1、学习目录IO的意义?
    目录是存放数据的一种方式,如果有大量的文件,我们可考虑将文件存放在一个目录,这样去管理这个目录,就等价于管理所有的文件。

    2、访问文件与访问目录有什么区别?
    访问目录,可以得到该目录下的目录项,该目录项包含了文件的名字,文件的类型等..
    访问文件,可以得到该文件里面的字符。

    3、目录IO中有哪些接口?
    1)如何打开一个目录呢?  -> opendir()  -> man 3 opendir
    功能: open a directory
        //打开一个目录
    头文件:
        #include <sys/types.h>
            #include <dirent.h>   -> 目录专属头文件
    原型:
        DIR *opendir(const char *name);

    参数:
        name:需要打开的那个目录的路径。 (绝对路径/相对路径)

    返回值:
        成功:目录流指针。
        失败:NULL。

    1)、什么是目录流指针?
    打开一个目录,会返回一个指针,该指针默认指向目录中的第一项。

    2)、问题一: 使用opendir来打开一个目录,就等价于切换到该目录下吗?  -> 不是
    3)、问题二: 要是目录下没有文件,那指向NULL吗?  -> 不为NULL。

    #include <sys/types.h>
    #include <dirent.h>
    #include <stdio.h>
    #include <stdlib.h>

    int main(int argc,char *argv[])
    {
        system("pwd");
        
        DIR *dp = opendir("./ggy_dir");
        if(dp == NULL)
            printf("opendir error!\n");
        
        system("pwd");
        
        return 0;
    }

    2)如何切换到目录下?  -> chdir()  -> man 2 chdir
      (这里所说的切换,只是在程序中切换,ubuntu里面的路径不会变。)

    功能:change working directory
    头文件:
        #include <unistd.h>

    原型:
        int chdir(const char *path);

    参数:
        path:你想切换到那个目标的路径。 

    返回值:
        成功:0
        失败:-1

    #include <sys/types.h>
    #include <dirent.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>

    int main(int argc,char *argv[])
    {
        system("pwd");
        
        DIR *dp = opendir("./ggy_dir");
        if(dp == NULL)
            printf("opendir error!\n");
        
        system("pwd");
        
        int ret;
        ret = chdir("./ggy_dir");
        if(ret == -1)
            printf("chdir error!\n");
        
        system("pwd");
        
        return 0;
    }
    结果:
    gec@ubuntu:/mnt/hgfs/GZ2057/06 文件IO/06/code$ ./dir
    /mnt/hgfs/GZ2057/06 文件IO/06/code
    /mnt/hgfs/GZ2057/06 文件IO/06/code
    /mnt/hgfs/GZ2057/06 文件IO/06/code/ggy_dir

    注意:切换到一个目录下的好处是什么。
    如果不切换:
    fopen("./ggy_dir/ggy.txt");

    如果切换:
    fopen("./ggy.txt");

    3)如何读取目录下的内容?  -> readdir()  -> man 3 readdir
    功能:read a directory
    头文件:
        #include <dirent.h>
    原型:
        struct dirent *readdir(DIR *dirp);

    参数:
        dirp:目录流指针

    返回值:
        成功:结构体指针  struct dirent *
        失败:NULL

    struct dirent {
                   ino_t          d_ino;       //索引号
                   off_t          d_off;         //偏移量     
                   unsigned short d_reclen;    //该项的长度  
                   unsigned char  d_type;      //文件类型
                   char           d_name[256]; //文件名
    };

    d_type:
    enum
    {
        DT_UNKNOWN = 0,  -> 未知类型
        DT_FIFO = 1,     -> 管道文件
        DT_CHR = 2,      -> 字符设备文件
        DT_DIR = 4,      -> 目录文件
        DT_BLK = 6,      -> 块设备文件
        DT_REG = 8,      -> 普通文件
        DT_LNK = 10,     -> 链接文件
        DT_SOCK = 12,    -> 套接字文件
    };

         例题: 打开ggy_dir,并将里面的每一个目录项的类型与文件名读取出来。

    #include <sys/types.h>
    #include <dirent.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>

    int main(int argc,char *argv[])
    {
        DIR *dp = opendir("./ggy_dir");
        if(dp == NULL)
            printf("opendir error!\n");
        
        int ret;
        ret = chdir("./ggy_dir");
        if(ret == -1)
            printf("chdir error!\n");
        
        struct dirent *ep = NULL;
        
        while(1)
        {
            ep = readdir(dp);
            if(ep == NULL)
            {
                break;
            }
            
            if(ep->d_name[0] == '.')
            {
                continue;
            }
            
            printf("type = %d\n",ep->d_type);
            printf("name = %s\n",ep->d_name);
            printf("==========================\n");
        }
        
        return 0;
    }

    4)关闭目录。  -> closedir()  -> man 3 closedir
    功能: close a directory
    头文件:
        #include <sys/types.h>
            #include <dirent.h>

    原型:
        int closedir(DIR *dirp);

    参数:
        dirp:目录流指针

    返回值:
        成功:0
        失败:-1

    5)重置目录流指针。  -> rewinddir()  -> man 3 rewinddir
    功能: reset directory stream
        //重置目录指针

    头文件:
        #include <sys/types.h>
            #include <dirent.h>

    原型:
        void rewinddir(DIR *dirp);

    参数:
        dirp:目录流指针


    返回值:无。


     

    展开全文
  • C#内存映射文件学习总结

    千次阅读 2017-05-05 22:43:54
    C#内存映射文件学习 http://www.cnblogs.com/flyant/p/4443187.html 内存映射文件是由一个文件到进程地址空间的映射。  C#提供了允许应用程序把文件映射到一个进程的函(MemoryMappedFile.CreateOrOpen)...

    C#内存映射文件学习

    http://www.cnblogs.com/flyant/p/4443187.html


    内存映射文件是由一个文件到进程地址空间的映射。


            C#提供了允许应用程序把文件映射到一个进程的函(MemoryMappedFile.CreateOrOpen)。内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。


            共享内存是内存映射文件的一种特殊情况,内存映射的是一块内存,而非磁盘上的文件。共享内存的主语是进程(Process),操作系统默认会给每一个进程分配一个内存空间,每一个进程只允许访问操作系统分配给它的哪一段内存,而不能访问其他进程的。而有时候需要在不同进程之间访问同一段内存,怎么办呢?操作系统给出了创建访问共享内存的API,需要共享内存的进程可以通过这一组定义好的API来访问多个进程之间共有的内存,各个进程访问这一段内存就像访问一个硬盘上的文件一样。而.Net 4.0中引入了System.IO.MemoryMappedFiles命名空间,这个命名空间的类对windows 共享内存相关API做了封装,使.Net程序员可以更方便的使用内存映射文件。


    内存映射文件实现进程间通讯


            内存映射文件是实现进程通讯的又一种方法,我们可以通过共享剪贴板、共享物理文件来实现进程间的数据共享,这里我们还可以通过内存映射文件来实现共享,这样,文件内的数据就可以用内存读/写指令来访问,而不是用ReadFile和WriteFile这样的I/O系统函数,从而提高了文件存取速度。这种方式更加快捷高效,最适用于需要读取文件并且对文件内包含的信息做语法分析的应用程序,如:对输入文件进行语法分析的彩色语法编辑器,编译器等。这种数据共享是让两个或多个进程映射同一文件映射对象的视图,即它们在共享同一物理存储页。这样,当一个进程向内存映射文件的一个视图写入数据时,其他的进程立即在自己的视图中看到变化。


    注意:


            对文件映射对象要使用同一名字。


            是让两个或多个进程映射同一文件映射对象的视图,即它们在共享同一物理存储页。这样,当一个进程向内存映射文件的一个视图写入数据时,其他的进程立即在自己的视图中看到变化。但要注意,对文件映射对象要使用同一名字。


     内存映射文件使用实例:


    1.      在同一进程内同时读写同一内存映射文件


    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.IO;
    using System.IO.MemoryMappedFiles;


    namespace UseMMFInProcess
    {
        public partial class frmMain : Form
        {
            public frmMain()
            {
                InitializeComponent();
                CreateMemoryMapFile();
            }
            private const int FILE_SIZE = 512;
            /// <summary>
            /// 引用内存映射文件
            /// </summary>
            private MemoryMappedFile memoryFile = null;
            /// <summary>
            /// 用于访问内存映射文件的存取对象
            /// </summary>
            private MemoryMappedViewAccessor accessor1, accessor2,accessor;
            /// <summary>
            /// 创建内存映射文件
            /// </summary>
            private void CreateMemoryMapFile()
            {
                try
                {     
                    memoryFile = MemoryMappedFile.CreateFromFile("MyFile.dat", FileMode.OpenOrCreate, "MyFile", FILE_SIZE);               
                    //访问文件前半段
                    accessor1 = memoryFile.CreateViewAccessor(0, FILE_SIZE / 2);            
                    //访问文件后半段
                    accessor2 = memoryFile.CreateViewAccessor(FILE_SIZE / 2, FILE_SIZE / 2);              
                    //访问全部文件
                    accessor = memoryFile.CreateViewAccessor();
                    //InitFileContent();
                    lblInfo.Text = "内存文件创建成功";
                    ShowFileContents();
                }
                catch (Exception ex)
                {
                    lblInfo.Text = ex.Message;
                }
            }
            /// <summary>
            /// 关闭并释放资源
            /// </summary>
            private void DisposeMemoryMapFile()
            {
                if (accessor1 != null)
                    accessor1.Dispose();
                if (accessor2 != null)
                    accessor2.Dispose();
                if (memoryFile != null)
                    memoryFile.Dispose();
            }


            private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
            {
                DisposeMemoryMapFile();
            }


            private void btnWrite1_Click(object sender, EventArgs e)
            {
                if (textBox1.Text.Length == 0)
                {
                    lblInfo.Text = "请输入一个字符";
                    return;
                }
                char[] chs = textBox1.Text.ToCharArray();
                char ch = chs[0];
               
                for (int i = 0; i < FILE_SIZE / 2; i += 2)
                    accessor1.Write(i, ch);
                
                lblInfo.Text = "字符“" + ch + "”已写到文件前半部份";
                ShowFileContents();
            }


            private void btnShow_Click(object sender, EventArgs e)
            {
                ShowFileContents();
            }


            /// <summary>
            /// 初始化文件内容为可视的字符“0”
            /// </summary>
            private void InitFileContent()
            {
                for (int i = 0; i < FILE_SIZE; i += 2) 
                    accessor.Write(i,'0');
            }
            /// <summary>
            /// 显示文件内容
            /// </summary>
            private void ShowFileContents()
            {
                StringBuilder sb = new StringBuilder(FILE_SIZE);
                sb.Append("上半段内容:\n");


                int j = 0;
                for (int i = 0; i < FILE_SIZE / 2; i += 2)
                {
                    sb.Append("\t");
                    char ch = accessor.ReadChar(i);
                    sb.Append(j);
                    sb.Append(":");
                    sb.Append(ch);
                    j++;
                }
                sb.Append("\n下半段内容:\n");


                for (int i = FILE_SIZE / 2; i < FILE_SIZE; i += 2)
                {
                    sb.Append("\t");
                    char ch = accessor.ReadChar(i);
                    sb.Append(j);
                    sb.Append(":");
                    sb.Append(ch);
                    j++;
                }
                richTextBox1.Text = sb.ToString();
            }


            private void btnWrite2_Click(object sender, EventArgs e)
            {
                if (textBox2.Text.Length == 0)
                {
                    lblInfo.Text = "请输入一个字符";
                    return;
                }
                char[] chs = textBox2.Text.ToCharArray();
                char ch = chs[0];


                for (int i = 0; i < FILE_SIZE / 2; i += 2)
                    accessor2.Write(i, ch);
                lblInfo.Text = "字符“" + ch + "”已写到文件后半部份";
                ShowFileContents();
            }
        }
    }


    2.      使用内存映射文件在进程间传送值类型数据


    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;


    namespace UseMMFBetweenProcess
    {
        /// <summary>
        /// 要共享的数据结构,注意,其成员不能是引用类型
        /// </summary>
        public struct MyStructure
        {
            public int IntValue
            {
                get;
                set;
            }
            public float FloatValue
            {
                get;
                set;
            } 
        } 
    }


    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.IO.MemoryMappedFiles;
    using System.IO;


    namespace UseMMFBetweenProcess
    {
        public partial class frmMain : Form
        {
            public frmMain()
            {
                InitializeComponent();
                InitMemoryMappedFile();
            }


            /// <summary>
            /// 内存映射文件的容量
            /// </summary>
            private const int FileSize = 1024 * 1024;
            private MemoryMappedFile file = null;
            private MemoryMappedViewAccessor accessor = null;


            /// <summary>
            /// 初始化内存映射文件
            /// </summary>
            private void InitMemoryMappedFile()
            {
                file = MemoryMappedFile.CreateOrOpen("UseMMFBetweenProcess", FileSize);
                accessor = file.CreateViewAccessor();
                lblInfo.Text = "内存文件创建或连接成功";         
            }


            /// <summary>
            /// 要共享的数据对象
            /// </summary>
            private MyStructure data;


            /// <summary>
            /// 显示数据到窗体上
            /// </summary>
            private void ShowData()
            {
                textBox1.Text = data.IntValue.ToString();
                textBox2.Text = data.FloatValue.ToString();
            }


            /// <summary>
            /// 根据用户输入更新数据
            /// </summary>
            private void UpdateData()
            {
                data.IntValue = int.Parse(textBox1.Text);
                data.FloatValue = float.Parse(textBox2.Text);
            }


            private void btnSave_Click(object sender, EventArgs e)
            {
                try
                {
                    UpdateData();
                    accessor.Write<MyStructure>(0, ref data);
                    lblInfo.Text = "数据已经保存到内存文件中";
                }
                catch (Exception ex)
                {
                    lblInfo.Text = ex.Message;
                }
            }


            private void btnLoad_Click(object sender, EventArgs e)
            {
                accessor.Read<MyStructure>(0, out data);
                ShowData();
                lblInfo.Text = "成功从内存文件中提取了数据";
            }


            private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
            {
                if (accessor != null)
                    accessor.Dispose();
                if (file != null)
                    file.Dispose();
            }
        }
    }


    3.      利用序列化技术通过内存映射文件实现进程通讯


    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.IO;
    using System.IO.MemoryMappedFiles;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;


    namespace UseMMFBetweenProcess2
    {
        public partial class frmMain : Form
        {
            public frmMain()
            {
                InitializeComponent();
                InitMemoryMappedFile();
            }
           
            /// <summary>
            /// 图片
            /// </summary>
            private Image bmp
            {
                get
                {
                    return pictureBox1.Image;
                }
                set
                {
                    pictureBox1.Image = value;
                }
            }
           
           /// <summary>
           /// 图片说明
           /// </summary>
            private string info
            {
                get
                {
                    return txtImageInfo.Text;
                }
                set
                {
                    txtImageInfo.Text = value;
                }
            }


            private MemoryMappedFile memoryFile = null;


            private MemoryMappedViewStream stream = null;


            /// <summary>
            /// 最大容量:10M
            /// </summary>
            private const int FileSize = 1024 * 1024 * 10;  


            /// <summary>
            /// 创建内存映射文件,获取其读写流
            /// </summary>
            private void InitMemoryMappedFile()
            {
                try
                {
      memoryFile = MemoryMappedFile.CreateOrOpen("UseMMFBetweenProcess2", FileSize);
                stream = memoryFile.CreateViewStream();
                }
                catch (Exception ex )
                {
                    MessageBox.Show(ex.Message);
                    Close();
                }
            }
            /// <summary>
            /// 释放相关资源
            /// </summary>
            private void DisposeMemoryMappedFile()
            {
                if (stream != null)
                    stream.Close();
                if (memoryFile != null)
                    memoryFile.Dispose();
            }


            private void btnLoadPic_Click(object sender, EventArgs e)
            {
                ChooseImageFile();
            }


            //选择图片
            private void ChooseImageFile()
            {
                if (openFileDialog1.ShowDialog() == DialogResult.OK)
                {
                    bmp = new Bitmap(openFileDialog1.FileName);
                }
            }
            //根据用户设定的信息创建对象
            private MyPic CreateMyPicObj()
            {
                MyPic obj = new MyPic();
                obj.pic = bmp;
                obj.picInfo = info;
                return obj;
            }


            /// <summary>
            /// 将MyPic对象保存到内存映射文件中
            /// </summary>
            private void SaveToMMF()
            {
                try
                {
                MyPic obj = CreateMyPicObj();
                IFormatter formatter = new BinaryFormatter();
                stream.Seek(0, SeekOrigin.Begin);
                formatter.Serialize(stream, obj);
                MessageBox.Show("对象已保存到内存映射文件中");
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }


     private void LoadFromMMF()
            {
                try
                {
               // CreateMyPicObj();
                IFormatter formatter = new BinaryFormatter();
                stream.Seek(0, SeekOrigin.Begin);
                MyPic obj =   formatter.Deserialize(stream) as MyPic;
                if (obj != null)
                {
                    bmp = obj.pic;
                    info = obj.picInfo;
                }
              }
              catch (Exception ex)
              {
                  MessageBox.Show(ex.Message);
              }
            }


            private void btnExit_Click(object sender, EventArgs e)
            {
                Close();
            }


            private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
            {
                DisposeMemoryMappedFile();
            }


            private void btnSaveToMMF_Click(object sender, EventArgs e)
            {
                SaveToMMF();
            }


            private void btnLoadFromMMF_Click(object sender, EventArgs e)
            {
                LoadFromMMF();
            }
        }
    }
    ========

    C# 用内存映射文件读取大文件(.txt)

    http://www.cnblogs.com/criedshy/archive/2010/06/13/1757826.html
      网上有好多这类的文章,大部分都是用C/C++写的,也有部分C#写的,都思想都是一样的,调用win32 API。


      至于什么是内存映射文件,相信还是有好多人不知道是怎么一回事的,我也是偶然看window 核心编程了解到的。


      C# 读取大文件的方法也是用的用StreamReader一次读出来,再用MemoryStream放在内存,再用StreamReader一行行的读出来,速度也挺快的,16M的文本大概也就8秒左右,算起来差不多算快了。不过还是不能满足大文件(我没测试)。


    string content = string.Empty;


                  using (StreamReader sr = new StreamReader(op.FileName))
                    {
                        content = sr.ReadToEnd();//一次性读入内存
                    }
                    MemoryStream ms = new MemoryStream(Encoding.GetEncoding("GB2312").GetBytes(content));//放入内存流,以便逐行读取
                    long line = 0;
                    using (StreamReader sr = new StreamReader(ms))
                    {
                        while (sr.Peek() > -1)
                        {


                            this.richTextBox1.AppendText(sr.ReadLine() + "\r\n");
                            Application.DoEvents();
                        }


                   }


     内存映射文件概述


      内存文件映射也是Windows的一种内存管理方法,提供了一个统一的内存管理特征,使应用程序可以通过内存指针对磁盘上的文件进行访问,其过程就如同对加载了文件的内存的访问。通过文件映射这种使磁盘文件的全部或部分内容与进程虚拟地址空间的某个区域建立映射关联的能力,可以直接对被映射的文件进行访问,而不必执行文件I/O操作也无需对文件内容进行缓冲处理。内存文件映射的这种特性是非常适合于用来管理大尺寸文件的。


      在使用内存映射文件进行I/O处理时,系统对数据的传输按页面来进行。至于内部的所有内存页面则是由虚拟内存管理器来负责管理,由其来决定内存页面何时被分页到磁盘,哪些页面应该被释放以便为其它进程提供空闲空间,以及每个进程可以拥有超出实际分配物理内存之外的多少个页面空间等等。由于虚拟内存管理器是以一种统一的方式来处理所有磁盘I/O的(以页面为单位对内存数据进行读写),因此这种优化使其有能力以足够快的速度来处理内存操作。


      使用内存映射文件时所进行的任何实际I/O交互都是在内存中进行并以标准的内存地址形式来访问。磁盘的周期性分页也是由操作系统在后台隐蔽实现的,对应用程序而言是完全透明的。内存映射文件的这种特性在进行大文件的磁盘事务操作时将获得很高的效益。


      需要说明的是,在系统的正常的分页操作过程中,内存映射文件并非一成不变的,它将被定期更新。如果系统要使用的页面目前正被某个内存映射文件所占用,系统将释放此页面,如果页面数据尚未保存,系统将在释放页面之前自动完成页面数据到磁盘的写入。


      对于使用页虚拟存储管理的Windows操作系统,内存映射文件是其内部已有的内存管理组件的一个扩充。由可执行代码页面和数据页面组成的应用程序可根据需要由操作系统来将这些页面换进或换出内存。如果内存中的某个页面不再需要,操作系统将撤消此页面原拥用者对它的控制权,并释放该页面以供其它进程使用。只有在该页面再次成为需求页面时,才会从磁盘上的可执行文件重新读入内存。同样地,当一个进程初始化启动时,内存的页面将用来存储该应用程序的静态、动态数据,一旦对它们的操作被提交,这些页面也将被备份至系统的页面文件,这与可执行文件被用来备份执行代码页面的过程是很类似的。图1展示了代码页面和数据页面在磁盘存储器上的备份过程:


    图1 进程的代码页、数据页在磁盘存储器上的备份


      显然,如果可以采取同一种方式来处理代码和数据页面,无疑将会提高程序的执行效率,而内存映射文件的使用恰恰可以满足此需求。
    对大文件的管理


      内存映射文件对象在关闭对象之前并没有必要撤销内存映射文件的所有视图。在对象被释放之前,所有的脏页面将自动写入磁盘。通过 CloseHandle()关闭内存映射文件对象,只是释放该对象,如果内存映射文件代表的是磁盘文件,那么还需要调用标准文件I/O函数来将其关闭。在处理大文件处理时,内存映射文件将表示出卓越的优势,只需要消耗极少的物理资源,对系统的影响微乎其微。下面先给出内存映射文件的一般编程流程框图:


    图2 使用内存映射文件的一般流程


      而在某些特殊行业,经常要面对十几GB乃至几十GB容量的巨型文件,而一个32位进程所拥有的虚拟地址空间只有232 = 4GB,显然不能一次将文件映像全部映射进来。对于这种情况只能依次将大文件的各个部分映射到进程中的一个较小的地址空间。这需要对上面的一般流程进行适当的更改:


      1)映射文件开头的映像。


      2)对该映像进行访问。


      3)取消此映像


      4)映射一个从文件中的一个更深的位移开始的新映像。


      5)重复步骤2,直到访问完全部的文件数据。




       下面是用C#写的代码,大部分代码转自网上,自己在原来的基础上改了一改。


    C#内存映射文件代码
     
         经过测试16M的文本4秒可以读出来。


         现在是有两个问题还没有解决:


    1.就是编码的问题,用Unicode解码的时候,文件大会很慢,而用ANSI和ASCII就很快。不知道为什么,望知情者告之。


    2.怎么知道文件的编码是什么?用win32 IsTestUnicode只能判断两种,而且还不保证是对。
    ========

    c#实现内存映射文件共享内存

    http://blog.csdn.net/wangtiewei/article/details/51112668  
    内存映射文件是利用虚拟内存把文件映射到进程的地址空间中去,在此之后进程操作文件,就像操作进程空间里的地址一样了,比如使用C语言的 memcpy等内存操作的函数。这种方法能够很好的应用在需要频繁处理一个文件或者是一个大文件的场合,这种方式处理IO效率比普通IO效率要高
    共享内存是内存映射文件的一种特殊情况,内存映射的是一块内存,而非磁盘上的文件。共享内存的主语是进程(Process),操作系统默认会给每一 个进程分配一个内存空间,每一个进程只允许访问操作系统分配给它的哪一段内存,而不能访问其他进程的。而有时候需要在不同进程之间访问同一段内存,怎么办 呢?操作系统给出了创建访问共享内存的API,需要共享内存的进程可以通过这一组定义好的API来访问多个进程之间共有的内存,各个进程访问这一段内存就 像访问一个硬盘上的文件一样。而.Net 4.0中引入了System.IO. MemoryMappedFiles命名空间,这个命名空间的类对windows 共享内存相关API做了封装,使.Net程序员可以更方便的使用内存映射文件。
    在C#中使用共享内存。以下App1的代码让用户输入一行文本到共享内存中;App2不停的刷新控制台,输出最新的共享内存内容;App3实现的功能和App2相同,但读取方法不同。


    App1代码:  
    using System;  
    using System.Collections.Generic;android从资源文件中读取文件流显示  
    using System.Linq;  
    using System.Text;  
      
    using System.IO;  
      
    //引用内存映射文件命名空间  
    using System.IO.MemoryMappedFiles;  
      
    namespace App1  
    {  
        class Program  
        {  
            static void Main(string[] args)  
            {  
                long capacity = 1<<10<<10;  
      
                //创建或者打开共享内存  
                using (var mmf = MemoryMappedFile.CreateOrOpen("testMmf", capacity, MemoryMappedFileAccess.ReadWrite))  
                {  
                    //通过MemoryMappedFile的CreateViewAccssor方法获得共享内存的访问器  
                    var viewAccessor = mmf.CreateViewAccessor(0, capacity);  
                    //循环写入,使在这个进程中可以向共享内存中写入不同的字符串值  
                    while (true)  
                    {  
                        Console.WriteLine("请输入一行要写入共享内存的文字:");  
      
                        string input = Console.ReadLine();  
      
                        //向共享内存开始位置写入字符串的长度  
                        viewAccessor.Write(0, input.Length);  
      
                        //向共享内存4位置写入字符  
                        viewAccessor.WriteArray<char>(4, input.ToArray(), 0, input.Length);  
                    }  
      
                }  
                  
            }  
        }  
    }  


    App2代码:  
    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Text;  
    using System.Threading;  
      
    //引用使用内存映射文件需要的命名空间  
    using System.IO.MemoryMappedFiles;  
      
    namespace App2  
    {  
        class Program  
        {  
            static void Main(string[] args)  
            {  
                  long capacity = 1<<10<<10;  
      
                  using (var mmf = MemoryMappedFile.OpenExisting("testMmf"))  
                  {  
                      MemoryMappedViewAccessor viewAccessor = mmf.CreateViewAccessor(0, capacity);  
      
                      //循环刷新共享内存字符串的值  
                      while (true)  
                      {  
                          //读取字符长度  
                          int strLength = viewAccessor.ReadInt32(0);                        
                          char[] charsInMMf = new char[strLength];  
                          //读取字符  
                          viewAccessor.ReadArray<char>(4, charsInMMf, 0, strLength);  
                          Console.Clear();  
                          Console.Write(charsInMMf);  
                          Console.Write("\r");  
                          Thread.Sleep(200);  
                      }  
                  }  
            }  
        }  
    }  


    App3代码:  
    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Text;  
      
    using System.IO.MemoryMappedFiles;  
    using System.IO;  
      
    namespace App3  
    {  
        class Program  
        {  
            static void Main(string[] args)  
            {  
                long capacity = 1 << 10 << 10;  
                //打开共享内存  
                using (var mmf = MemoryMappedFile.OpenExisting("testMmf"))  
                {  
                    //使用CreateViewStream方法返回stream实例  
                    using (var mmViewStream = mmf.CreateViewStream(0, capacity))  
                    {  
                        //这里要制定Unicode编码否则会出问题  
                        using (BinaryReader rdr = new BinaryReader(mmViewStream,Encoding.Unicode))  
                        {  
                            while (true)  
                            {  
                                mmViewStream.Seek(0, SeekOrigin.Begin);  
      
                                int length = rdr.ReadInt32();  
      
                                char[] chars = rdr.ReadChars(length);  
      
                                Console.Write(chars);  
                                Console.Write("\r");  
      
                                System.Threading.Thread.Sleep(200);  
                                Console.Clear();  
                            }  
                        }  
                    }  
                }  
            }  
        }  
    }  
    在读数据时用了2种方法。
    因为在之前很少会用到进程之间的通信,所以此方法只是想初步的认识下。此程序写的过于简陋,有很多东西都没有去判断。比如说是怎么创建了一个共享内存怎么取删除它等等。。。
    ========

    用C#实现的内存映射

    http://blog.csdn.net/linux7985/article/details/5853358


    当文件过大时,无法一次性载入内存时,就需要分次,分段的载入文件


    主要是用了以下的WinAPI


    LPVOID MapViewOfFile(HANDLE hFileMappingObject,
      DWORD dwDesiredAccess,
      DWORD dwFileOffsetHigh,
      DWORD dwFileOffsetLow,
      DWORD dwNumberOfBytesToMap);
     
      MapViewOfFile() 函数负责把文件数据映射到进程的地址空间,参数hFileMappingObject 为 CreateFileMapping()返回的文件映像对象句柄。参数dwDesiredAccess则再次指定了对文件数据的访问方式,而且同样要与 CreateFileMapping()函数所设置的保护属性相匹配。虽然这里一再对保护属性进行重复设置看似多余,但却可以使应用程序能更多的对数据的保护属性实行有效控制。MapViewOfFile()函数允许全部或部分映射文件,在映射时,需要指定数据文件的偏移地址以及待映射的长度。其中,文件的偏移地址由DWORD型的参数dwFileOffsetHigh和dwFileOffsetLow组成的64位值来指定,而且必须是操作系统的分配粒度的整数倍,对于Windows操作系统,分配粒度固定为64KB。当然,也可以通过如下代码来动态获取当前操作系统的分配粒度:


      SYSTEM_INFO sinf;
      GetSystemInfo(&sinf);
      DWORD dwAllocationGranularity = sinf.dwAllocationGranularity;


      参数dwNumberOfBytesToMap指定了数据文件的映射长度,这里需要特别指出的是,对于Windows 9x操作系统,如果MapViewOfFile()无法找到足够大的区域来存放整个文件映射对象,将返回空值(NULL);但是在Windows 2000下,MapViewOfFile()只需要为必要的视图找到足够大的一个区域即可,而无须考虑整个文件映射对象的大小。


    由此看出,分页映射文件时,每页的起始位置startpos,必须为64K的整数倍。


    以下贴出源代码,防止忘记了


    using System;  
    using System.Collections.Generic;  
    using System.Text;  
    using System.Runtime.InteropServices;  
      
    namespace BlueVision.SaYuan.FileMapping  
    {  
        public class ShareMemory  
        {  
            [DllImport( "user32.dll", CharSet = CharSet.Auto )]  
            public static extern IntPtr SendMessage( IntPtr hWnd, int Msg, int wParam, IntPtr lParam );  
      
            [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )]  
            public static extern IntPtr CreateFileMapping( IntPtr hFile, IntPtr lpAttributes, uint flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName );  
      
            [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )]  
            public static extern IntPtr OpenFileMapping( int dwDesiredAccess, [MarshalAs( UnmanagedType.Bool )] bool bInheritHandle, string lpName );  
      
            [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )]  
            public static extern IntPtr MapViewOfFile( IntPtr hFileMapping, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap );  
      
            [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )]  
            public static extern bool UnmapViewOfFile( IntPtr pvBaseAddress );  
      
            [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )]  
            public static extern bool CloseHandle( IntPtr handle );  
      
            [DllImport( "kernel32", EntryPoint = "GetLastError" )]  
            public static extern int GetLastError();  
      
            [DllImport( "kernel32.dll" )]  
            static extern void GetSystemInfo( out SYSTEM_INFO lpSystemInfo );  
      
            [StructLayout( LayoutKind.Sequential )]  
            public struct SYSTEM_INFO  
            {  
                public ushort processorArchitecture;  
                ushort reserved;  
                public uint pageSize;  
                public IntPtr minimumApplicationAddress;  
                public IntPtr maximumApplicationAddress;  
                public IntPtr activeProcessorMask;  
                public uint numberOfProcessors;  
                public uint processorType;  
                public uint allocationGranularity;  
                public ushort processorLevel;  
                public ushort processorRevision;  
            }  
            /// <summary>  
            /// 获取系统的分配粒度  
            /// </summary>  
            /// <returns></returns>  
            public static uint GetPartitionsize()  
            {  
                SYSTEM_INFO sysInfo;  
                GetSystemInfo( out sysInfo );  
                return sysInfo.allocationGranularity;  
            }  
      
            const int ERROR_ALREADY_EXISTS = 183;  
      
            const int FILE_MAP_COPY = 0x0001;  
            const int FILE_MAP_WRITE = 0x0002;  
            const int FILE_MAP_READ = 0x0004;  
            const int FILE_MAP_ALL_ACCESS = 0x0002 | 0x0004;  
      
            const int PAGE_READONLY = 0x02;  
            const int PAGE_READWRITE = 0x04;  
            const int PAGE_WRITECOPY = 0x08;  
            const int PAGE_EXECUTE = 0x10;  
            const int PAGE_EXECUTE_READ = 0x20;  
            const int PAGE_EXECUTE_READWRITE = 0x40;  
      
            const int SEC_COMMIT = 0x8000000;  
            const int SEC_IMAGE = 0x1000000;  
            const int SEC_NOCACHE = 0x10000000;  
            const int SEC_RESERVE = 0x4000000;  
      
            IntPtr m_fHandle;  
      
            IntPtr m_hSharedMemoryFile = IntPtr.Zero;  
            IntPtr m_pwData = IntPtr.Zero;  
            bool m_bAlreadyExist = false;  
            bool m_bInit = false;  
            uint m_MemSize = 0x1400000;//20M  
            long m_offsetBegin = 0;  
            long m_FileSize = 0;  
            FileReader File = new FileReader();  
        
            /// <summary>  
            ///  初始化文件  
            /// </summary>  
            /// <param name="MemSize">缓冲大小</param>  
            public ShareMemory( string filename, uint memSize )  
            {  
                // 分页映射文件时,每页的起始位置startpos,必须为64K的整数倍。  
                // memSize即缓存区的大小必须是系统分配粒度的整倍说,window系统的分配粒度是64KB  
                this.m_MemSize = memSize;  
                Init( filename );  
            }  
        
            /// <summary>  
            /// 默认映射20M缓冲  
            /// </summary>  
            /// <param name="filename"></param>  
            public ShareMemory( string filename )  
            {  
                this.m_MemSize = 0x1400000;  
                Init( filename );  
            }  
      
            ~ShareMemory()  
            {  
                Close();  
            }  
      
            /// <summary>  
            /// 初始化共享内存  
            ///   
            /// 共享内存名称  
            /// 共享内存大小  
            /// </summary>  
            /// <param name="strName"></param>  
            protected void Init( string strName )  
            {  
                //if (lngSize <= 0 || lngSize > 0x00800000) lngSize = 0x00800000;  
      
                if ( !System.IO.File.Exists( strName ) ) throw new Exception( "未找到文件" );  
      
                System.IO.FileInfo f = new System.IO.FileInfo( strName );    
                m_FileSize = f.Length;    
                m_fHandle = File.Open( strName );  
      
                if ( strName.Length > 0 )  
                {  
                    //创建文件映射  
                    m_hSharedMemoryFile = CreateFileMapping( m_fHandle, IntPtr.Zero, ( uint )PAGE_READONLY, 0, ( uint )m_FileSize, "mdata" );  
                    if ( m_hSharedMemoryFile == IntPtr.Zero )  
                    {  
                        m_bAlreadyExist = false;  
                        m_bInit = false;  
                        throw new Exception( "CreateFileMapping失败LastError=" + GetLastError().ToString() );  
                    }  
                    else  
                        m_bInit = true;  
      
                    映射第一块文件  
                    //m_pwData = MapViewOfFile(m_hSharedMemoryFile, FILE_MAP_READ, 0, 0, (uint)m_MemSize);  
                    //if (m_pwData == IntPtr.Zero)  
                    //{  
                    //    m_bInit = false;  
                    //    throw new Exception("m_hSharedMemoryFile失败LastError=" + GetLastError().ToString());  
                    //}  
      
                }  
            }  
            /// <summary>  
            /// 获取高32位  
            /// </summary>  
            /// <param name="intValue"></param>  
            /// <returns></returns>  
            private static uint GetHighWord( UInt64 intValue )  
            {  
                return Convert.ToUInt32( intValue >> 32 );  
            }  
            /// <summary>  
            /// 获取低32位  
            /// </summary>  
            /// <param name="intValue"></param>  
            /// <returns></returns>  
            private static uint GetLowWord( UInt64 intValue )  
            {  
      
                return Convert.ToUInt32( intValue & 0x00000000FFFFFFFF );  
            }  
      
            /// <summary>  
            /// 获取下一个文件块 块大小为20M  
            /// </summary>  
            /// <returns>false 表示已经是最后一块文件</returns>  
            public uint GetNextblock()  
            {  
                if ( !this.m_bInit ) throw new Exception( "文件未初始化。" );  
                //if ( m_offsetBegin + m_MemSize >= m_FileSize ) return false;  
      
                uint m_Size = GetMemberSize();  
                if ( m_Size == 0 ) return m_Size;  
      
                // 更改缓冲区大小  
                m_MemSize = m_Size;  
      
                //卸载前一个文件  
                //bool l_result = UnmapViewOfFile( m_pwData );  
                //m_pwData = IntPtr.Zero;  
      
      
                m_pwData = MapViewOfFile( m_hSharedMemoryFile, FILE_MAP_READ, GetHighWord( ( UInt64 )m_offsetBegin ), GetLowWord( ( UInt64 )m_offsetBegin ), m_Size );  
                if ( m_pwData == IntPtr.Zero )  
                {  
                    m_bInit = false;  
                    throw new Exception( "映射文件块失败" + GetLastError().ToString() );  
                }  
                m_offsetBegin = m_offsetBegin + m_Size;  
      
                return m_Size; //创建成功  
            }  
            /// <summary>  
            /// 返回映射区大小  
            /// </summary>  
            /// <returns></returns>  
            private uint GetMemberSize()  
            {  
                if ( m_offsetBegin >= m_FileSize )  
                {  
                    return 0;  
                }  
                else if ( m_offsetBegin + m_MemSize >= m_FileSize )  
                {  
                    long temp = m_FileSize - m_offsetBegin;  
                    return ( uint )temp;  
                }  
                else  
                    return m_MemSize;  
            }  
      
            /// <summary>  
            /// 关闭内存映射  
            /// </summary>  
            public void Close()  
            {  
                if ( m_bInit )  
                {  
                    UnmapViewOfFile( m_pwData );  
                    CloseHandle( m_hSharedMemoryFile );  
                    File.Close();  
                }  
            }  
      
            /// <summary>  
            /// 从当前块中获取数据  
            /// </summary>  
            /// <param name="bytData">数据</param>  
            /// <param name="lngAddr">起始数据</param>  
            /// <param name="lngSize">数据长度,最大值=缓冲长度</param>  
            /// <param name="Unmap">读取完成是否卸载缓冲区</param>  
            /// <returns></returns>  
            public void Read( ref byte[] bytData, int lngAddr, int lngSize, bool Unmap )  
            {  
                if ( lngAddr + lngSize > m_MemSize )  
                    throw new Exception( "Read操作超出数据区" );  
                if ( m_bInit )  
                {  
                    // string bb = Marshal.PtrToStringAuto(m_pwData);//  
                    Marshal.Copy( m_pwData, bytData, lngAddr, lngSize );  
                }  
                else  
                {  
                    throw new Exception( "文件未初始化" );  
                }  
      
                if ( Unmap )  
                {  
                    bool l_result = UnmapViewOfFile( m_pwData );  
                    if ( l_result )  
                        m_pwData = IntPtr.Zero;  
                }  
            }  
      
            /// <summary>  
            /// 从当前块中获取数据  
            /// </summary>  
            /// <param name="bytData">数据</param>  
            /// <param name="lngAddr">起始数据</param>  
            /// <param name="lngSize">数据长度,最大值=缓冲长度</param>  
            /// <exception cref="Exception: Read操作超出数据区"></exception>  
            /// <exception cref="Exception: 文件未初始化"></exception>  
            /// <returns></returns>  
            public void Read( ref byte[] bytData, int lngAddr, int lngSize )  
            {  
                if ( lngAddr + lngSize > m_MemSize )  
                    throw new Exception( "Read操作超出数据区" );  
                if ( m_bInit )  
                {  
                    Marshal.Copy( m_pwData, bytData, lngAddr, lngSize );  
                }  
                else  
                {  
                    throw new Exception( "文件未初始化" );  
                }  
            }  
      
            /// <summary>  
            /// 从当前块中获取数据  
            /// </summary>  
            /// <param name="lngAddr">缓存区偏移量</param>  
            /// <param name="byteData">数据数组</param>  
            /// <param name="StartIndex">数据数组开始复制的下标</param>  
            /// <param name="lngSize">数据长度,最大值=缓冲长度</param>  
            /// <exception cref="Exception: 起始数据超过缓冲区长度"></exception>  
            /// <exception cref="Exception: 文件未初始化"></exception>  
            /// <returns>返回实际读取值</returns>  
            public uint ReadBytes( int lngAddr, ref byte[] byteData, int StartIndex, uint intSize )  
            {  
                if ( lngAddr >= m_MemSize )  
                    throw new Exception( "起始数据超过缓冲区长度" );  
      
                if ( lngAddr + intSize > m_MemSize )  
                    intSize = m_MemSize - ( uint )lngAddr;  
      
                if ( m_bInit )  
                {  
                    IntPtr s = new IntPtr( ( long )m_pwData + lngAddr ); // 地址偏移  
                    Marshal.Copy( s, byteData, StartIndex, ( int )intSize );  
                }  
                else  
                {  
                    throw new Exception( "文件未初始化" );  
                }  
      
                return intSize;  
            }  
      
            /// <summary>  
            /// 写数据  
            /// </summary>  
            /// <param name="bytData">数据</param>  
            /// <param name="lngAddr">起始地址</param>  
            /// <param name="lngSize">个数</param>  
            /// <returns></returns>  
            private int Write( byte[] bytData, int lngAddr, int lngSize )  
            {  
                if ( lngAddr + lngSize > m_MemSize ) return 2; //超出数据区  
                if ( m_bInit )  
                {  
                    Marshal.Copy( bytData, lngAddr, m_pwData, lngSize );  
                }  
                else  
                {  
                    return 1; //共享内存未初始化  
                }  
                return 0; //写成功  
            }  
        }  
        internal class FileReader  
        {  
            const uint GENERIC_READ = 0x80000000;  
            const uint OPEN_EXISTING = 3;  
            System.IntPtr handle;  
      
            [DllImport( "kernel32", SetLastError = true )]  
            public static extern System.IntPtr CreateFile(  
                string FileName,          // file name  
                uint DesiredAccess,       // access mode  
                uint ShareMode,           // share mode  
                uint SecurityAttributes,  // Security Attributes  
                uint CreationDisposition, // how to create  
                uint FlagsAndAttributes,  // file attributes  
                int hTemplateFile         // handle to template file  
            );  
      
            [System.Runtime.InteropServices.DllImport( "kernel32", SetLastError = true )]  
            static extern bool CloseHandle  
            (  
                System.IntPtr hObject // handle to object  
            ); 


            public IntPtr Open( string FileName )  
            {  
                // open the existing file for reading         
                handle = CreateFile  
                (  
                    FileName,  
                    GENERIC_READ,  
                    0,  
                    0,  
                    OPEN_EXISTING,  
                    0,  
                    0  
                );  
      
                if ( handle != System.IntPtr.Zero )  
                {  
                    return handle;  
                }  
                else  
                {  
                    throw new Exception( "打开文件失败" );  
                }  
            }  
      
            public bool Close()  
            {  
                return CloseHandle( handle );  
            }  
        }  
    }  
    ========

    .NET Framework自带的文件内存映射类

    http://www.cnblogs.com/briny/archive/2012/11/25/2787188.html
    最近一直为文件内存映射发愁,整个两周一直折腾这个东西。在64位系统和32位系统还要针对内存的高低位进行计算。好麻烦。。还是没搞定
    偶然从MSDN上发现.NET 4.0把内存文件映射加到了.NET类库中。。好像方便了很多啊。。比用C#直接调用WINDOWS API方便多了。所以
    这个必须果断记录之。。。项目马上要用,为了加强内存数据交换的效率。。这个。。。必须啊。。


    任务


    使用的方法或属性


    从磁盘上的文件中获取表示持久内存映射文件的 MemoryMappedFile 对象。
    MemoryMappedFile.CreateFromFile 方法。
    获取表示非持久内存映射文件(与磁盘上的文件不关联)的 MemoryMappedFile 对象。
    MemoryMappedFile.CreateNew 方法。
    - 或 -
    MemoryMappedFile.CreateOrOpen 方法。
    获取现有内存映射文件(持久文件或非持久文件)的 MemoryMappedFile 对象。
    MemoryMappedFile.OpenExisting 方法。
    获取针对内存映射文件的顺序访问视图的 UnmanagedMemoryStream 对象。
    MemoryMappedFile.CreateViewStream 方法。
    获取针对内存映射文件的随机访问视图的 UnmanagedMemoryAccessor 对象。
    MemoryMappedFile.CreateViewAccessor 方法。
    获取要用于非托管代码的 SafeMemoryMappedViewHandle 对象。
    MemoryMappedFile.SafeMemoryMappedFileHandle 属性。
    - 或 -
    MemoryMappedViewAccessor.SafeMemoryMappedViewHandle 属性。
    - 或 -
    MemoryMappedViewStream.SafeMemoryMappedViewHandle 属性。
    将内存分配推迟到创建视图后进行(仅限于非持久文件)。
    (若要确定当前系统页大小,请使用 Environment.SystemPageSize 属性。)
    带 MemoryMappedFileOptions.DelayAllocatePages 值的 CreateNew 方法。
    - 或 -
    将 MemoryMappedFileOptions 枚举作为参数的 CreateOrOpen 方法。
     
    持久文件内存映射:


    CreateFromFile 方法基于磁盘上的现有文件创建一个内存映射文件。


     1 using System;
     2 using System.IO;
     3 using System.IO.MemoryMappedFiles;
     4 using System.Runtime.InteropServices;
     5 
     6 class Program
     7 {
     8     static void Main(string[] args)
     9     {
    10         long offset = 0x10000000; // 256 megabytes
    11         long length = 0x20000000; // 512 megabytes
    12 
    13         // Create the memory-mapped file.
    14         using (var mmf = MemoryMappedFile.CreateFromFile(@"c:\ExtremelyLargeImage.data", FileMode.Open,"ImgA"))
    15         {
    16             // Create a random access view, from the 256th megabyte (the offset)
    17             // to the 768th megabyte (the offset plus length).
    18             using (var accessor = mmf.CreateViewAccessor(offset, length))
    19             {
    20                 int colorSize = Marshal.SizeOf(typeof(MyColor));
    21                 MyColor color;
    22 
    23                 // Make changes to the view.
    24                 for (long i = 0; i < length; i += colorSize)
    25                 {
    26                     accessor.Read(i, out color);
    27                     color.Brighten(10);
    28                     accessor.Write(i, ref color);
    29                 }
    30             }
    31         }
    32     }
    33 }
    34 
    35 public struct MyColor
    36 {
    37     public short Red;
    38     public short Green;
    39     public short Blue;
    40     public short Alpha;
    41 
    42     // Make the view brigher.
    43     public void Brighten(short value)
    44     {
    45         Red = (short)Math.Min(short.MaxValue, (int)Red + value);
    46         Green = (short)Math.Min(short.MaxValue, (int)Green + value);
    47         Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);
    48         Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);
    49     }
    50 }


     非持久文件内存映射:


    CreateNew 和 CreateOrOpen 方法创建一个未映射到磁盘上的现有文件的内存映射文件。


     1 using System;
     2 using System.IO;
     3 using System.IO.MemoryMappedFiles;
     4 using System.Threading;
     5 
     6 class Program
     7 {
     8     // Process A:
     9     static void Main(string[] args)
    10     {
    11         using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("testmap", 10000))
    12         {
    13             bool mutexCreated;
    14             Mutex mutex = new Mutex(true, "testmapmutex", out mutexCreated);
    15             using (MemoryMappedViewStream stream = mmf.CreateViewStream())
    16             {
    17                 BinaryWriter writer = new BinaryWriter(stream);
    18                 writer.Write(1);
    19             }
    20             mutex.ReleaseMutex();
    21 
    22             Console.WriteLine("Start Process B and press ENTER to continue.");
    23             Console.ReadLine();
    24 
    25             Console.WriteLine("Start Process C and press ENTER to continue.");
    26             Console.ReadLine();
    27 
    28             mutex.WaitOne();
    29             using (MemoryMappedViewStream stream = mmf.CreateViewStream())
    30             {
    31                 BinaryReader reader = new BinaryReader(stream);
    32                 Console.WriteLine("Process A says: {0}", reader.ReadBoolean());
    33                 Console.WriteLine("Process B says: {0}", reader.ReadBoolean());
    34                 Console.WriteLine("Process C says: {0}", reader.ReadBoolean());
    35             }
    36             mutex.ReleaseMutex();
    37         }
    38     }
    39 }
    ========
    展开全文
  • C#内存映射文件学习

    千次阅读 2012-05-17 17:10:35
    内存映射文件是由一个文件到进程地址空间的映射。  C#提供了允许应用程序把文件映射到一个进程的函(MemoryMappedFile.CreateOrOpen)。内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的...

            内存映射文件是由一个文件到进程地址空间的映射。

            C#提供了允许应用程序把文件映射到一个进程的函(MemoryMappedFile.CreateOrOpen)。内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。

            共享内存是内存映射文件的一种特殊情况,内存映射的是一块内存,而非磁盘上的文件。共享内存的主语是进程(Process),操作系统默认会给每一个进程分配一个内存空间,每一个进程只允许访问操作系统分配给它的哪一段内存,而不能访问其他进程的。而有时候需要在不同进程之间访问同一段内存,怎么办呢?操作系统给出了创建访问共享内存的API,需要共享内存的进程可以通过这一组定义好的API来访问多个进程之间共有的内存,各个进程访问这一段内存就像访问一个硬盘上的文件一样。而.Net 4.0中引入了System.IO.MemoryMappedFiles命名空间,这个命名空间的类对windows 共享内存相关API做了封装,使.Net程序员可以更方便的使用内存映射文件。

    内存映射文件实现进程间通讯

            内存映射文件是实现进程通讯的又一种方法,我们可以通过共享剪贴板、共享物理文件来实现进程间的数据共享,这里我们还可以通过内存映射文件来实现共享,这样,文件内的数据就可以用内存读/写指令来访问,而不是用ReadFile和WriteFile这样的I/O系统函数,从而提高了文件存取速度。这种方式更加快捷高效,最适用于需要读取文件并且对文件内包含的信息做语法分析的应用程序,如:对输入文件进行语法分析的彩色语法编辑器,编译器等。这种数据共享是让两个或多个进程映射同一文件映射对象的视图,即它们在共享同一物理存储页。这样,当一个进程向内存映射文件的一个视图写入数据时,其他的进程立即在自己的视图中看到变化。

    注意:

            对文件映射对象要使用同一名字。

            是让两个或多个进程映射同一文件映射对象的视图,即它们在共享同一物理存储页。这样,当一个进程向内存映射文件的一个视图写入数据时,其他的进程立即在自己的视图中看到变化。但要注意,对文件映射对象要使用同一名字。

     内存映射文件使用实例:

    1.      在同一进程内同时读写同一内存映射文件

     

     

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.IO;
    using System.IO.MemoryMappedFiles;
    
    
    namespace UseMMFInProcess
    {
        public partial class frmMain : Form
        {
            public frmMain()
            {
                InitializeComponent();
                CreateMemoryMapFile();
            }
            private const int FILE_SIZE = 512;
            /// <summary>
            /// 引用内存映射文件
            /// </summary>
            private MemoryMappedFile memoryFile = null;
            /// <summary>
            /// 用于访问内存映射文件的存取对象
            /// </summary>
            private MemoryMappedViewAccessor accessor1, accessor2,accessor;
            /// <summary>
            /// 创建内存映射文件
            /// </summary>
            private void CreateMemoryMapFile()
            {
                try
                {     
                    memoryFile = MemoryMappedFile.CreateFromFile("MyFile.dat", FileMode.OpenOrCreate, "MyFile", FILE_SIZE);               
                    //访问文件前半段
                    accessor1 = memoryFile.CreateViewAccessor(0, FILE_SIZE / 2);            
                    //访问文件后半段
                    accessor2 = memoryFile.CreateViewAccessor(FILE_SIZE / 2, FILE_SIZE / 2);              
                    //访问全部文件
                    accessor = memoryFile.CreateViewAccessor();
                    //InitFileContent();
                    lblInfo.Text = "内存文件创建成功";
                    ShowFileContents();
                }
                catch (Exception ex)
                {
                    lblInfo.Text = ex.Message;
                }
            }
            /// <summary>
            /// 关闭并释放资源
            /// </summary>
            private void DisposeMemoryMapFile()
            {
                if (accessor1 != null)
                    accessor1.Dispose();
                if (accessor2 != null)
                    accessor2.Dispose();
                if (memoryFile != null)
                    memoryFile.Dispose();
            }
    
            private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
            {
                DisposeMemoryMapFile();
            }
    
            private void btnWrite1_Click(object sender, EventArgs e)
            {
                if (textBox1.Text.Length == 0)
                {
                    lblInfo.Text = "请输入一个字符";
                    return;
                }
                char[] chs = textBox1.Text.ToCharArray();
                char ch = chs[0];
               
                for (int i = 0; i < FILE_SIZE / 2; i += 2)
                    accessor1.Write(i, ch);
                
                lblInfo.Text = "字符“" + ch + "”已写到文件前半部份";
                ShowFileContents();
            }
    
            private void btnShow_Click(object sender, EventArgs e)
            {
                ShowFileContents();
            }
    
            /// <summary>
            /// 初始化文件内容为可视的字符“0”
            /// </summary>
            private void InitFileContent()
            {
                for (int i = 0; i < FILE_SIZE; i += 2) 
                    accessor.Write(i,'0');
            }
            /// <summary>
            /// 显示文件内容
            /// </summary>
            private void ShowFileContents()
            {
                StringBuilder sb = new StringBuilder(FILE_SIZE);
                sb.Append("上半段内容:\n");
    
                int j = 0;
                for (int i = 0; i < FILE_SIZE / 2; i += 2)
                {
                    sb.Append("\t");
                    char ch = accessor.ReadChar(i);
                    sb.Append(j);
                    sb.Append(":");
                    sb.Append(ch);
                    j++;
                }
                sb.Append("\n下半段内容:\n");
    
                for (int i = FILE_SIZE / 2; i < FILE_SIZE; i += 2)
                {
                    sb.Append("\t");
                    char ch = accessor.ReadChar(i);
                    sb.Append(j);
                    sb.Append(":");
                    sb.Append(ch);
                    j++;
                }
                richTextBox1.Text = sb.ToString();
            }
    
            private void btnWrite2_Click(object sender, EventArgs e)
            {
                if (textBox2.Text.Length == 0)
                {
                    lblInfo.Text = "请输入一个字符";
                    return;
                }
                char[] chs = textBox2.Text.ToCharArray();
                char ch = chs[0];
    
                for (int i = 0; i < FILE_SIZE / 2; i += 2)
                    accessor2.Write(i, ch);
                lblInfo.Text = "字符“" + ch + "”已写到文件后半部份";
                ShowFileContents();
            }
        }
    }
    

    2.      使用内存映射文件在进程间传送值类型数据

     

     

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace UseMMFBetweenProcess
    {
        /// <summary>
        /// 要共享的数据结构,注意,其成员不能是引用类型
        /// </summary>
        public struct MyStructure
        {
            public int IntValue
            {
                get;
                set;
            }
            public float FloatValue
            {
                get;
                set;
            } 
        } 
    }
    
    
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.IO.MemoryMappedFiles;
    using System.IO;
    
    namespace UseMMFBetweenProcess
    {
        public partial class frmMain : Form
        {
            public frmMain()
            {
                InitializeComponent();
                InitMemoryMappedFile();
            }
    
            /// <summary>
            /// 内存映射文件的容量
            /// </summary>
            private const int FileSize = 1024 * 1024;
            private MemoryMappedFile file = null;
            private MemoryMappedViewAccessor accessor = null;
    
            /// <summary>
            /// 初始化内存映射文件
            /// </summary>
            private void InitMemoryMappedFile()
            {
                file = MemoryMappedFile.CreateOrOpen("UseMMFBetweenProcess", FileSize);
                accessor = file.CreateViewAccessor();
                lblInfo.Text = "内存文件创建或连接成功";         
            }
    
            /// <summary>
            /// 要共享的数据对象
            /// </summary>
            private MyStructure data;
    
            /// <summary>
            /// 显示数据到窗体上
            /// </summary>
            private void ShowData()
            {
                textBox1.Text = data.IntValue.ToString();
                textBox2.Text = data.FloatValue.ToString();
            }
    
            /// <summary>
            /// 根据用户输入更新数据
            /// </summary>
            private void UpdateData()
            {
                data.IntValue = int.Parse(textBox1.Text);
                data.FloatValue = float.Parse(textBox2.Text);
            }
    
            private void btnSave_Click(object sender, EventArgs e)
            {
                try
                {
                    UpdateData();
                    accessor.Write<MyStructure>(0, ref data);
                    lblInfo.Text = "数据已经保存到内存文件中";
                }
                catch (Exception ex)
                {
                    lblInfo.Text = ex.Message;
                }
            }
    
            private void btnLoad_Click(object sender, EventArgs e)
            {
                accessor.Read<MyStructure>(0, out data);
                ShowData();
                lblInfo.Text = "成功从内存文件中提取了数据";
            }
    
            private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
            {
                if (accessor != null)
                    accessor.Dispose();
                if (file != null)
                    file.Dispose();
            }
        }
    }
    

    3.      利用序列化技术通过内存映射文件实现进程通讯

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.IO;
    using System.IO.MemoryMappedFiles;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;
    
    namespace UseMMFBetweenProcess2
    {
        public partial class frmMain : Form
        {
            public frmMain()
            {
                InitializeComponent();
                InitMemoryMappedFile();
            }
           
            /// <summary>
            /// 图片
            /// </summary>
            private Image bmp
            {
                get
                {
                    return pictureBox1.Image;
                }
                set
                {
                    pictureBox1.Image = value;
                }
            }
           
           /// <summary>
           /// 图片说明
           /// </summary>
            private string info
            {
                get
                {
                    return txtImageInfo.Text;
                }
                set
                {
                    txtImageInfo.Text = value;
                }
            }
    
            private MemoryMappedFile memoryFile = null;
    
            private MemoryMappedViewStream stream = null;
    
            /// <summary>
            /// 最大容量:10M
            /// </summary>
            private const int FileSize = 1024 * 1024 * 10;  
    
            /// <summary>
            /// 创建内存映射文件,获取其读写流
            /// </summary>
            private void InitMemoryMappedFile()
            {
                try
                {
      memoryFile = MemoryMappedFile.CreateOrOpen("UseMMFBetweenProcess2", FileSize);
                stream = memoryFile.CreateViewStream();
                }
                catch (Exception ex )
                {
                    MessageBox.Show(ex.Message);
                    Close();
                }
            }
            /// <summary>
            /// 释放相关资源
            /// </summary>
            private void DisposeMemoryMappedFile()
            {
                if (stream != null)
                    stream.Close();
                if (memoryFile != null)
                    memoryFile.Dispose();
            }
    
            private void btnLoadPic_Click(object sender, EventArgs e)
            {
                ChooseImageFile();
            }
    
            //选择图片
            private void ChooseImageFile()
            {
                if (openFileDialog1.ShowDialog() == DialogResult.OK)
                {
                    bmp = new Bitmap(openFileDialog1.FileName);
                }
            }
            //根据用户设定的信息创建对象
            private MyPic CreateMyPicObj()
            {
                MyPic obj = new MyPic();
                obj.pic = bmp;
                obj.picInfo = info;
                return obj;
            }
    
            /// <summary>
            /// 将MyPic对象保存到内存映射文件中
            /// </summary>
            private void SaveToMMF()
            {
                try
                {
                MyPic obj = CreateMyPicObj();
                IFormatter formatter = new BinaryFormatter();
                stream.Seek(0, SeekOrigin.Begin);
                formatter.Serialize(stream, obj);
                MessageBox.Show("对象已保存到内存映射文件中");
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
    
     private void LoadFromMMF()
            {
                try
                {
               // CreateMyPicObj();
                IFormatter formatter = new BinaryFormatter();
                stream.Seek(0, SeekOrigin.Begin);
                MyPic obj =   formatter.Deserialize(stream) as MyPic;
                if (obj != null)
                {
                    bmp = obj.pic;
                    info = obj.picInfo;
                }
              }
              catch (Exception ex)
              {
                  MessageBox.Show(ex.Message);
              }
            }
    
            private void btnExit_Click(object sender, EventArgs e)
            {
                Close();
            }
    
            private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
            {
                DisposeMemoryMappedFile();
            }
    
            private void btnSaveToMMF_Click(object sender, EventArgs e)
            {
                SaveToMMF();
            }
    
            private void btnLoadFromMMF_Click(object sender, EventArgs e)
            {
                LoadFromMMF();
            }
        }
    }
    


    展开全文
  • ARCGIS 出图显示不能映射元文件到内存 没有足够的内存解决
  • 图片描述 内存映射(减少数据在用户空间和内核空间之间的拷贝操作,适合大量数据传输) Linux内核提供一种访问磁盘文件的特殊方式,它可以将内存中某块地址空间和我们要指定的磁盘文件相关联,从而把我们对这块内存的...

    在这里插入图片描述

                             网络IO和磁盘IO详解
    
    1. 缓存IO

      缓存I/O又被称作标准I/O,大多数文件系统的默认I/O操作都是缓存I/O。在Linux的缓存I/O机制中,数据先从磁盘复制到内核空间的缓冲区,然后从内核空间缓冲区复制到应用程序的地址空间。
      
      读操作:操作系统检查内核的缓冲区有没有需要的数据,如果已经缓存了,那么就直接从缓存中返回;否则从磁盘中读取,然后缓存在操作系统的缓存中。
      
      写操作:将数据从用户空间复制到内核空间的缓存中。这时对用户程序来说写操作就已经完成,至于什么时候再写到磁盘中由操作系统决定,除非显示地调用了sync同步命令(详情参考《【珍藏】linux 同步IO: sync、fsync与fdatasync》)。
      
      缓存I/O的优点:1)在一定程度上分离了内核空间和用户空间,保护系统本身的运行安全;2)可以减少读盘的次数,从而提高性能。
      
      缓存I/O的缺点:在缓存 I/O 机制中,DMA 方式可以将数据直接从磁盘读到页缓存中,或者将数据从页缓存直接写回到磁盘上,而不能直接在应用程序地址空间和磁盘之间进行数据传输,这样,数据在传输过程中需要在应用程序地址空间(用户空间)和缓存(内核空间)之间进行多次数据拷贝操作,这些数据拷贝操作所带来的CPU以及内存开销是非常大的。
      
    2. 直接IO

      直接IO就是应用程序直接访问磁盘数据,而不经过内核缓冲区,这样做的目的是减少一次从内核缓冲区到用户程序缓存的数据复制。比如说数据库管理系统这类应用,它们更倾向于选择它们自己的缓存机制,因为数据库管理系统往往比操作系统更了解数据库中存放的数据,数据库管理系统可以提供一种更加有效的缓存机制来提高数据库中数据的存取性能。
      
      直接IO的缺点:如果访问的数据不在应用程序缓存中,那么每次数据都会直接从磁盘加载,这种直接加载会非常缓存。通常直接IO与异步IO结合使用,会得到比较好的性能。(异步IO:当访问数据的线程发出请求之后,线程会接着去处理其他事,而不是阻塞等待)
      

    下图分析了写场景下的DirectIO和BufferIO:

    首先,磁盘IO主要的延时是由(以15000rpm硬盘为例): 机械转动延时(机械磁盘的主要性能瓶颈,平均为2ms) + 寻址延时(2~3ms) + 块传输延时(一般4k每块,40m/s的传输速度,延时一般为0.1ms) 决定。(平均为5ms)

    而网络IO主要延时由: 服务器响应延时 + 带宽限制 + 网络延时 + 跳转路由延时 + 本地接收延时 决定。(一般为几十到几千毫秒,受环境干扰极大)

    所以两者一般来说网络IO延时要大于磁盘IO的延时。

    用Redis作缓存是因为,Redis就是设计来做缓存的阿。

    Reids作缓存的几大优势:

    1, 简单的K-V式数据存储方式,单一的 get set 模式比传统SQL性能提升显著

    2, 纯in mem db 形式,将数据缓存在内存中,减少服务器磁盘IO时间。

    更新一下数据源:

    ref :

    《大型网站技术架构:核心原理与案例分析》

    作者:李晨曦
    链接:https://www.zhihu.com/question/47589908/answer/114768530
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    Google的Jeff Dean给的一些数据(一个talk的ppt, “Designs, Lessons and Advice from Building Large Distributed Systems” 23页),可以看到1Gbps的网络比硬盘的bandwidth高了很多,记住这些数据对设计高性能系统和对系统的性能估算很有帮助。

    L1 cache reference 0.5 ns

    Branch mispredict 5 ns

    L2 cache reference 7 ns

    Mutex lock/unlock 25 ns

    Main memory reference 100 ns

    Compress 1K bytes with Zippy 3,000 ns

    Send 2K bytes over 1 Gbps network 20,000 ns

    Read 1 MB sequentially from memory 250,000 ns

    Round trip within same datacenter 500,000 ns

    Disk seek 10,000,000 ns

    Read 1 MB sequentially from disk 20,000,000 ns

    Send packet CA->Netherlands->CA 150,000,000 ns

    PIO与DMA
    有必要简单地说说慢速I/O设备和内存之间的数据传输方式。

    PIO
    我们拿磁盘来说,很早以前,磁盘和内存之间的数据传输是需要CPU控制的,也就是说如果我们读取磁盘文件到内存中,数据要经过CPU存储转发,这种方式称为PIO。显然这种方式非常不合理,需要占用大量的CPU时间来读取文件,造成文件访问时系统几乎停止响应。

    DMA
    后来,DMA(直接内存访问,Direct Memory Access)取代了PIO,它可以不经过CPU而直接进行磁盘和内存的数据交换。在DMA模式下,CPU只需要向DMA控制器下达指令,让DMA控制器来处理数据的传送即可,DMA控制器通过系统总线来传输数据,传送完毕再通知CPU,这样就在很大程度上降低了CPU占有率,大大节省了系统资源,而它的传输速度与PIO的差异其实并不十分明显,因为这主要取决于慢速设备的速度。

    可以肯定的是,PIO模式的计算机我们现在已经很少见到了。

    标准文件访问方式
    图片描述

    具体步骤:
    当应用程序调用read接口时,操作系统检查在内核的高速缓存有没有需要的数据,如果已经缓存了,那么就直接从缓存中返回,如果没有,则从磁盘中读取,然后缓存在操作系统的缓存中。

    应用程序调用write接口时,将数据从用户地址空间复制到内核地址空间的缓存中,这时对用户程序来说,写操作已经完成,至于什么时候再写到磁盘中,由操作系统决定,除非显示调用了sync同步命令。
    图片描述

    内存映射(减少数据在用户空间和内核空间之间的拷贝操作,适合大量数据传输)
    Linux内核提供一种访问磁盘文件的特殊方式,它可以将内存中某块地址空间和我们要指定的磁盘文件相关联,从而把我们对这块内存的访问转换为对磁盘文件的访问,这种技术称为内存映射(Memory Mapping)。

    操作系统将内存中的某一块区域与磁盘中的文件关联起来,当要访问内存中的一段数据时,转换为访问文件的某一段数据。这种方式的目的同样是减少数据从内核空间缓存到用户空间缓存的数据复制操作,因为这两个空间的数据是共享的。

    内存映射是指将硬盘上文件的位置与进程逻辑地址空间中一块大小相同的区域一一对应,当要访问内存中一段数据时,转换为访问文件的某一段数据。这种方式的目的同样是减少数据在用户空间和内核空间之间的拷贝操作。当大量数据需要传输的时候,采用内存映射方式去访问文件会获得比较好的效率。

    使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。

    图片描述

    访问步骤
    图片描述

    在大多数情况下,使用内存映射可以提高磁盘I/O的性能,它无须使用read()或write()等系统调用来访问文件,而是通过mmap()系统调用来建立内存和磁盘文件的关联,然后像访问内存一样自由地访问文件。
    有两种类型的内存映射,共享型和私有型,前者可以将任何对内存的写操作都同步到磁盘文件,而且所有映射同一个文件的进程都共享任意一个进程对映射内存的修改;后者映射的文件只能是只读文件,所以不可以将对内存的写同步到文件,而且多个进程不共享修改。显然,共享型内存映射的效率偏低,因为如果一个文件被很多进程映射,那么每次的修改同步将花费一定的开销。

    直接I/O(绕过内核缓冲区,自己管理I/O缓存区)
    在Linux 2.6中,内存映射和直接访问文件没有本质上差异,因为数据从进程用户态内存空间到磁盘都要经过两次复制,即在磁盘与内核缓冲区之间以及在内核缓冲区与用户态内存空间。
    引入内核缓冲区的目的在于提高磁盘文件的访问性能,因为当进程需要读取磁盘文件时,如果文件内容已经在内核缓冲区中,那么就不需要再次访问磁盘;而当进程需要向文件中写入数据时,实际上只是写到了内核缓冲区便告诉进程已经写成功,而真正写入磁盘是通过一定的策略进行延迟的。

    然而,对于一些较复杂的应用,比如数据库服务器,它们为了充分提高性能,希望绕过内核缓冲区,由自己在用户态空间实现并管理I/O缓冲区,包括缓存机制和写延迟机制等,以支持独特的查询机制,比如数据库可以根据更加合理的策略来提高查询缓存命中率。另一方面,绕过内核缓冲区也可以减少系统内存的开销,因为内核缓冲区本身就在使用系统内存。

    应用程序直接访问磁盘数据,不经过操作系统内核数据缓冲区,这样做的目的是减少一次从内核缓冲区到用户程序缓存的数据复制。这种方式通常是在对数据的缓存管理由应用程序实现的数据库管理系统中。
    直接I/O的缺点就是如果访问的数据不在应用程序缓存中,那么每次数据都会直接从磁盘进行加载,这种直接加载会非常缓慢。通常直接I/O跟异步I/O结合使用会得到较好的性能。

    图片描述

    访问步骤
    图片描述

    Linux提供了对这种需求的支持,即在open()系统调用中增加参数选项O_DIRECT,用它打开的文件便可以绕过内核缓冲区的直接访问,这样便有效避免了CPU和内存的多余时间开销。

    顺便提一下,与O_DIRECT类似的一个选项是O_SYNC,后者只对写数据有效,它将写入内核缓冲区的数据立即写入磁盘,将机器故障时数据的丢失减少到最小,但是它仍然要经过内核缓冲区。

    sendfile/零拷贝(网络I/O,kafka用到此特性)
    普通的网络传输步骤如下:
    1)操作系统将数据从磁盘复制到操作系统内核的页缓存中
    2)应用将数据从内核缓存复制到应用的缓存中
    3)应用将数据写回内核的Socket缓存中
    4)操作系统将数据从Socket缓存区复制到网卡缓存,然后将其通过网络发出

    图片描述

    1、当调用read系统调用时,通过DMA(Direct Memory Access)将数据copy到内核模式
    2、然后由CPU控制将内核模式数据copy到用户模式下的 buffer中
    3、read调用完成后,write调用首先将用户模式下 buffer中的数据copy到内核模式下的socket buffer中
    4、最后通过DMA copy将内核模式下的socket buffer中的数据copy到网卡设备中传送。

    从上面的过程可以看出,数据白白从内核模式到用户模式走了一圈,浪费了两次copy,而这两次copy都是CPU copy,即占用CPU资源。

    sendfile
    图片描述

    通过sendfile传送文件只需要一次系统调用,当调用 sendfile时:
    1、首先通过DMA copy将数据从磁盘读取到kernel buffer中
    2、然后通过CPU copy将数据从kernel buffer copy到sokcet buffer中
    3、最终通过DMA copy将socket buffer中数据copy到网卡buffer中发送
    sendfile与read/write方式相比,少了 一次模式切换一次CPU copy。但是从上述过程中也可以发现从kernel buffer中将数据copy到socket buffer是没必要的。

    为此,Linux2.4内核对sendfile做了改进,下图所示
    图片描述
    改进后的处理过程如下:
    1、DMA copy将磁盘数据copy到kernel buffer中
    2、向socket buffer中追加当前要发送的数据在kernel buffer中的位置和偏移量
    3、DMA gather copy根据socket buffer中的位置和偏移量直接将kernel buffer中的数据copy到网卡上。
    经过上述过程,数据只经过了2次copy就从磁盘传送出去了。(事实上这个Zero copy是针对内核来讲的,数据在内核模式下是Zero-copy的)。
    当前许多高性能http server都引入了sendfile机制,如nginx,lighttpd等。

    FileChannel.transferTo(Java中的零拷贝)
    Java NIO中FileChannel.transferTo(long position, long count, WriteableByteChannel target)方法将当前通道中的数据传送到目标通道target中,在支持Zero-Copy的linux系统中,transferTo()的实现依赖于 sendfile()调用。

    图片描述

    传统方式对比零拷贝方式:

    图片描述

    整个数据通路涉及4次数据复制和2个系统调用,如果使用sendfile则可以避免多次数据复制,操作系统可以直接将数据从内核页缓存中复制到网卡缓存,这样可以大大加快整个过程的速度。

    大多数时候,我们都在向Web服务器请求静态文件,比如图片、样式表等,根据前面的介绍,我们知道在处理这些请求的过程中,磁盘文件的数据先要经过内核缓冲区,然后到达用户内存空间,因为是不需要任何处理的静态数据,所以它们又被送到网卡对应的内核缓冲区,接着再被送入网卡进行发送。

    数据从内核出去,绕了一圈,又回到内核,没有任何变化,看起来真是浪费时间。在Linux 2.4的内核中,尝试性地引入了一个称为khttpd的内核级Web服务器程序,它只处理静态文件的请求。引入它的目的便在于内核希望请求的处理尽量在内核完成,减少内核态的切换以及用户态数据复制的开销。

    同时,Linux通过系统调用将这种机制提供给了开发者,那就是sendfile()系统调用。它可以将磁盘文件的特定部分直接传送到代表客户端的socket描述符,加快了静态文件的请求速度,同时也减少了CPU和内存的开销。

    在OpenBSD和NetBSD中没有提供对sendfile的支持。通过strace的跟踪看到了Apache在处理151字节的小文件时,使用了mmap()系统调用来实现内存映射,但是在Apache处理较大文件的时候,内存映射会导致较大的内存开销,得不偿失,所以Apache使用了sendfile64()来传送文件,sendfile64()是sendfile()的扩展实现,它在Linux 2.4之后的版本中提供。

    这并不意味着sendfile在任何场景下都能发挥显著的作用。对于请求较小的静态文件,sendfile发挥的作用便显得不那么重要,通过压力测试,我们模拟100个并发用户请求151字节的静态文件,是否使用sendfile的吞吐率几乎是相同的,可见在处理小文件请求时,发送数据的环节在整个过程中所占时间的比例相比于大文件请求时要小很多,所以对于这部分的优化效果自然不十分明显。

    Zero-Copy&sendfile浅析

    展开全文
  • 显示屏的一些属性: 800*480像素点 每个像素点 4 个字节 操作步骤 1、打开 LCD 2、将颜色值写入LCD 3、关闭LCD 开始编写代码 编译代码 —》要想在ARM开发板上运行就需要使用 arm-linux-gcc 这个交叉编译器进行...
  • http://duguyidao.iteye.com/blog/1074012 Android ...在上次的代码中我是通过直接对原始图片进行了Scale制作为100*80的图片,然后显示出来,这样操作的话,当处理大图片的时候会浪费大量的VM内存,所以在上次提
  • lcd显示BMP图片

    2020-07-24 23:01:31
    基于GEC6818开发板的LCD显示BMP图片源程序, 硬件配置: CPU: 型号:s5p6818 架构(电路结构):Cortex - A53 频率:1.4Ghz+ 内存(动态内存-DRAM):1G 闪存(NAND FLASH): 8G
  • 嵌入式 在开发板显示bmp图片、jpeg图片 一、简述 记--在GEC6818开发板(800W*480H)显示24位的bmp图片、使用开源的jpeg库显示jpeg图片。 代码:链接: https://pan.baidu.com/s/1G3jzvdncocDMRbwCvsmSlg 密码: gz6...
  • springmvc上传图片并显示图片--支持多图片上传

    万次阅读 多人点赞 2015-07-13 21:44:23
    springmvc上传图片并显示图片(也能上传文件),多图片上传并显示图片,采用commons-fileupload,commons-io
  • HDR图片以及色调映射(ToneMapping)

    千次阅读 2020-09-11 21:06:11
    高动态范围(High-Dynamic Range,简称HDR),相应的,LDR(Low-Dynamic Range),也就是我们常见的8bit存储方式的图片.之前都是在介绍视频方面.但是在高动态图片(HDRI)方面也有一些少量应用, 目前常用的存储格式有三种:...
  • 本篇仅用于记录自己所学知识及应用,代码仍可优化,仅供参考,如果发现有错误的地方,...显示方式是使用内存映射方式(mmap),刷图显示速度比较快。 下面为封装好的BMP图像显示函数 //显示任意位置大小缩放的BMP图片
  • 本篇记录LCD显示屏加入百叶窗特效显示BMP图片,通过双线程并行显示,实现百叶窗特效。在显示函数中,已对BMP图片显示位置进行了居中设置,而缩小倍数只需通过在调用函数时进行传参即可。具体代码如下: myhead.h部分...
  • Android图形显示系统——一张图片显示流程

    万次阅读 多人点赞 2016-01-20 23:56:45
    Android设备上一张图片显示过程应用示例假如我们现在有一张这样的风景照 想在Android设备(比如一个小米pad)上显示出来。首先想到的是写一个应用,用一个ImageView,把这张照片附到ImageView上显示,如下面的...
  • opencv 能读取到图片,但是显示灰色

    千次阅读 2017-08-22 12:58:15
    今天接着在看canny边缘检测,却又遇到了问题,用的VS2017 Opencv 3.3.0 ,就是opencv 能读取到图片,但是显示灰色的一个窗口,且一闪而过。 下面是我出现问题的代码:未运行成功的代码: #include "stdafx.h" #...
  • I/O空间映射

    千次阅读 2012-12-07 12:46:46
    摘要: 注:部分资料和图片来源于网络,本文在学习过程中对网络资源进行再整理。I/O空间-----I/O端口和I/O内存 首先上图,如下:外设中的寄存器被称为I/O端口,外设中的内存被称为I/O内存。二者合起来统称为I/O空间...
  • C# 如何把一张bmp图片存入内存里面和读出来? 读出来要路径这种的
  • 纹理映射

    千次阅读 2013-03-11 15:37:34
    学习texture map纹理映射(贴图)有很多好处。比方说您想让一颗导弹飞过屏幕。根据前几课的知识,我们最可行的办法可能是很多个多边形来构建导弹的轮廓并加上有趣的颜色。使用纹理映射,您可以使用真实的导弹图像并...
  • 800*480bmp图片显示

    2020-08-17 19:52:14
    显示800*480像素的bmp图片(使用内存映射) 一、使用步骤 代码: 代码如下: #include "project.h" //定义一个存放触摸屏信息的结构体 struct input_event touch; //封装两个函数一个显示图片、一个触摸屏 int ...
  • lcd驱动程序之显示图片

    万次阅读 2015-12-29 17:13:56
    以前没有写Lcd驱动程序,现在开始做项目了,才发现Lcd驱动程序必须认真学习。还是老规矩先上代码。 内核版本:linux-3.4.2 lcd:4.3 lcd.c文件如下: #include #include #include #include ...
  • 颜色映射

    千次阅读 2010-03-29 16:25:00
    到目前为止,我们的绘制操作都只使用了黑白两色。现在我们就看看如何使用丰富的颜色来绘制。首先,是没有完全足够的颜色的。屏幕控制器同时只能支持有限的颜色。...一个颜色映射是一个与屏幕控制器同时可以
  • 图片的性能优化 图片的性能优化,运行期间检测不合理的图片- https://www.jianshu.com/p/adeeee995bc5 - Bitmap 内存模型 在 API10 之前,Bitmap 对象本身存在 Dalvik Heap 中,像素是存在 native 中,这样像素并...
  • 探讨Android内存管理

    千次阅读 2020-05-03 16:21:35
    前言 在看这篇文章之前,需要Linux内存管理基础,推荐先学习一些Linux内存管理基础知识点,这里我们在做一些应用层的优化工作,但从底层理解一些...Android 运行时 (ART) 和 Dalvik 虚拟机使用分页和内存映射来管理内...
  • 再谈光子映射

    千次阅读 2017-07-27 18:10:01
    在前一篇文章中提到了光子映射算法具有高效,扩展性好,能轻易捕捉到各种光照效果(比如Monte Carlo ray tracing不易捕捉到的SDS路径),但是它也存在很多问题,比如它本身是一个有偏的算法,boundary bias和topolo
  • 虚拟地址空间映射到物理地址空间

    千次阅读 2017-06-19 10:14:46
    虚拟地址空间映射到物理地址空间参考如下  当处理器读或写入内存位置时,它会使用虚拟地址。作为读或写操作的一部分,处理器将虚拟地址转换为物理地址。通过虚拟地址访问内存有以下优势:  程序可以使用一系列...
  • OpenGL显示图片

    千次阅读 2009-02-23 21:23:00
    显示一个正方形位图文件pictest.bmp。举例: LoadT8("images/pictest.bmp", iTex); glTranslatef(0,0,-10); texture(iTex); tPic(3.0f); 附:各个函数的源码://绘制正方形void tPic(float e){ glBegin(GL_...
  • OpenGL: 显示图片

    千次阅读 2014-07-18 09:13:34
    显示一个正方形位图文件pictest.bmp。举例: LoadT8("images/pictest.bmp", iTex); glTranslatef(0,0,-10); texture(iTex); tPic(3.0f); 附:各个函数的源码://绘制正方形void tPic(float e){ glBegin(GL_...
  • 读取、显示、保存图片

    千次阅读 2014-03-23 11:42:33
    OpenCV中最基本的操作:读取、显示、保存图片。 OpenCV2.0版本引入与Matlab命令相同的函数,imread、imshow、imwrite,获取图片更将方便。 读取文件 imread Mat imread(const string& filename, int flags=1 ) 其中...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 52,118
精华内容 20,847
关键字:

内存映射显示图片