在Linux中,将C转换为C#(Mono)的内存映射文件/共享内存。

4
我正在开发一个嵌入式系统,每秒获取约20兆字节的数据。我的底层采集、控制和处理层将大部分数据转换为一些重要的值,但对于最终用户来说,查看未经处理的数据窗口也是有用的。
我正在使用C#和mod-mono开发ASP.NET前端。我希望ASP.NET页面的服务器端能够轻松地请求最近半秒钟左右的数据。由于C++代码具有实时约束,因此我不能使用消息传递来响应请求-它可能会被太多客户端或者快速刷新的操作所拖累。我希望能够将数据放置到某个地方,以便任意数量的C#读取器可以根据需要访问它。
我设想一个共享内存区域,其中包含最新的16或32MB数据的滚动缓冲区。C++代码不断更新它,而C#代码可以随时查看它。有没有办法处理这个问题?所有关于使用内存映射文件的信息似乎都集中在派生子进程上,而不是让两个无关的进程用它进行IPC-它是否必须在C#应用程序可以看到之前击中磁盘(或fs缓存等),还是两个程序之间的内存映射实际上使它们共享相同的页面?
有没有办法在C#中访问POSIX共享内存对象?

内存映射似乎是传递数据的正确方向。据我了解,对于IPC通信,您仍需要一些东西来告诉“好的,现在获取更新视图”(调用[CreateView()](http://msdn.microsoft.com/ en-us / library / dd267538.aspx)),这可以是命名互斥或简单轮询。还有管道 - Sinatr
@Sinatr 我只会进行简单的轮询 - 我知道只要系统开着,数据就会被读取,供我查看。我更关心的是内存映射文件在 Mono 和 C 之间是否能正常工作。我担心它看起来像是在工作,但实际上却依赖于文件系统/磁盘,而不是两个进程映射到同一内存页。 - Katie
它在POSIX描述中,参见维基百科 - Sinatr
1个回答

9

以下是使用内存映射文件(Memory-mapped file)实现C程序和C#程序之间共享信息的示例(两个不同进程):

  1. From console create file: dd if=/dev/zero of=/tmp/sharedfile bs=12288 count=1

  2. The C# program:

    using System;
    using System.IO;
    using System.IO.MemoryMappedFiles;
    using System.Threading;
    
    namespace FileSharedMemory
    {
        class MainClass
        {
            public static void Main (string[] args)
            {
    
                using (var mmf = MemoryMappedFile.CreateFromFile("/tmp/sharedfile", FileMode.OpenOrCreate, "/tmp/sharedfile"))
                {
                    using (var stream = mmf.CreateViewStream ()) {
                        // 1. C program, filled memory-mapped file with the 'G' character (200 characters)
                        var data = stream.ReadByte ();
                        while (data != -1)
                        {
                            Console.WriteLine ((char)data);
                            data = stream.ReadByte ();
                         }
    
                         // 2. We write "Goose" at the beginning of memory-mapped file.
                         stream.Position = 0;
                         var buffer = new byte[] { 0x47, 0x6F, 0x6F, 0x73, 0x65 };
                         stream.Write (buffer, 0, 5);
    
                         Thread.Sleep (20000);
    
                         // 3. C program, filled memory-mapped file with the 'H' character (200 characters)
                         stream.Position = 0;
                         data = stream.ReadByte ();
                         while (data != -1)
                         {
                             Console.WriteLine ((char)data);
                             data = stream.ReadByte ();
                         }
                    }
                }
            }
        }
    }
    
  3. The C program:

    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/mman.h>
    #include <errno.h>
    
    int main(int argc, char *argv[])
    {
        int fd;
        int index;
        char *data;
        const char *filepath = "/tmp/sharedfile";
    
        if ((fd = open(filepath, O_CREAT|O_RDWR, (mode_t)00700)) == -1) {
            perror("open");
            exit(EXIT_FAILURE);
        }
    
        data = mmap(NULL, 12288, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);
        if (data == MAP_FAILED) {
            perror("mmap");
            exit(EXIT_FAILURE);
        }
    
    
        for (index= 0; index < 200; index++) {
            data[index] = 'G';
        } 
    
        sleep(10);
    
        // We must see 'Goose' at the beginning of memory-mapped file.
        for (index= 0; index < 200; index++) {
            fprintf(stdout, "%c", data[index]);
        }
    
        for (index= 0; index < 200; index++) {
            data[index] = 'H';
        }
    
        if (msync(data, 12288, MS_SYNC) == -1) {
            perror("Error sync to disk");
        } 
    
        if (munmap(data, 12288) == -1) {
            close(fd);
            perror("Error un-mmapping");
            exit(EXIT_FAILURE);
        }
    
        close(fd);
    
        return 0;
    }
    

谢谢提供的示例 - 你知道Mono的MemoryMappedFile.CreateFromFile是否可以与使用shm_open而不是open创建的对象一起使用吗?我预计它会,但没有找到任何文档 - 我会在有机会回到这个项目的这部分时自己尝试一下。我对使用shm_open很感兴趣,因为我真的不需要将其写入磁盘,而且每当系统开启时缓冲区都会被不断更新,因此如果它是基于文件的,它将不断写入磁盘。 - Katie
只是一个提示:字节(bytes)不能是-1。它可以包含0..255范围内的值。如果它是有符号字节(signed byte),则可以保存-128到+128之间的值。因此,这段代码:var data = stream.ReadByte(); while (data != -1)将无法正常工作。 - Jon Lennart Aasenden
@JonLennartAasenden 当到达流的末尾时,您会得到-1的值。 - Gooseman
我注意到变量被定义为“var”,这本质上与变体相同。它将适应您分配的值以适合其类型。如果它是一个固定的字节字段,-1将不适合,您将收到一个超出范围的异常。例如:UByte value = stream.readByte(1);在这里,如果“value”是有符号字节,则-1的值仅在其为有效值,否则只有0到255之间的值才是有效的。但由于它是未经类型化的变量,因此任何值都可以存储在其中。 - Jon Lennart Aasenden
1
@Jon:var 不是无类型的,stream.ReadByte() 返回的不是字节而是整数,这就是为什么 -1 是一个有效的结果。 - sunside
当您使用“var”关键字创建变量时,它将采用您分配给它的第一个元素的数据类型。 - Jon Lennart Aasenden

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接