C#pInvoke with 32 in& 64位Linux:

时间:2014-05-06 10:48:36

标签: c# linux mono pinvoke marshalling

我需要调用Linux函数sysinfo

它的声明是 int sysinfo(struct sysinfo * info); 与

在Linux 2.3.16之前,sysinfo()用于返回以下结构中的信息:

struct sysinfo {
    long uptime;             /* Seconds since boot */
    unsigned long loads[3];  /* 1, 5, and 15 minute load averages */
    unsigned long totalram;  /* Total usable main memory size */
    unsigned long freeram;   /* Available memory size */
    unsigned long sharedram; /* Amount of shared memory */
    unsigned long bufferram; /* Memory used by buffers */
    unsigned long totalswap; /* Total swap space size */
    unsigned long freeswap;  /* swap space still available */
    unsigned short procs;    /* Number of current processes */
    char _f[22];             /* Pads structure to 64 bytes */
};

,大小以字节为单位。

自Linux 2.3.23(i386),2.3.48(所有架构)以来,结构是:

struct sysinfo {
    long uptime;             /* Seconds since boot */
    unsigned long loads[3];  /* 1, 5, and 15 minute load averages */
    unsigned long totalram;  /* Total usable main memory size */
    unsigned long freeram;   /* Available memory size */
    unsigned long sharedram; /* Amount of shared memory */
    unsigned long bufferram; /* Memory used by buffers */
    unsigned long totalswap; /* Total swap space size */
    unsigned long freeswap;  /* swap space still available */
    unsigned short procs;    /* Number of current processes */
    unsigned long totalhigh; /* Total high memory size */
    unsigned long freehigh;  /* Available high memory size */
    unsigned int mem_unit;   /* Memory unit size in bytes */
    char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding to 64 bytes */
};

这是我到目前为止所做的:

功能pinvoke:

private const string DoesntFindLibC =@"/lib/x86_64-linux-gnu/libc.so.6";

[System.Runtime.InteropServices.DllImport(DoesntFindLibC)]
private static extern int sysinfo(ref sysinfo_t info);  

结构映射:

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
struct sysinfo_t
{
    public System.UIntPtr  uptime;             /* Seconds since boot */
    [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=3)]
    public System.UIntPtr [] loads;  /* 1, 5, and 15 minute load averages */


    public System.UIntPtr totalram;  /* Total usable main memory size */
    public System.UIntPtr  freeram;   /* Available memory size */
    public System.UIntPtr  sharedram; /* Amount of shared memory */
    public System.UIntPtr  bufferram; /* Memory used by buffers */


    // [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.
    public System.UIntPtr  totalswap; /* Total swap space size */

    public System.UIntPtr  freeswap;  /* swap space still available */
    public ushort procs;    /* Number of current processes */

    [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=22)]
    public char[] _f; /* Pads structure to 64 bytes */
}

问题是"长"在C代码中是特定于处理器架构的,因为在x86 32位Linux中,长度为32位,在x86 64位Linux中,它是64位,所以我必须采用IntPtr ,因为它是未签名的,我接受UIntPtr。

在C#/ mono中,long总是定义为Int64。

现在使用IntPtr有点不方便。 是否有我可以应用的MarshalAs属性,或者我可以编写的自定义封送程序,以便我可以在结构中实际拥有ulong,但它本地映射到IntPtr?因此,相同的代码适用于x86-32和x86-64。

2 个答案:

答案 0 :(得分:2)

不,你没有任何编组魔法来解决这个问题。

但是,您可以隐藏字段并提供属性访问者:

using System.Runtime.InteropServices;

[StructLayoutAttribute(LayoutKind.Sequential)]
struct sysinfo_t
{
    System.UIntPtr  _uptime;             /* Seconds since boot */
    public ulong uptime {
        get { return (ulong) _uptime; }
        set { _uptime = new UIntPtr (value); }
    }

    [MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
    System.UIntPtr [] _loads;  /* 1, 5, and 15 minute load averages */
    public ulong[] loads {
        get { return new ulong [] { (ulong) _loads [0], (ulong) _loads [1], (ulong) _loads [1]) };
        set { _loads = new UIntPtr [] { new UIntPtr (value [0]), new UIntPtr (value [1]), new UIntPtr (value [2]) }; }
    }

    // etc
}

答案 1 :(得分:0)

C'long'类型非常难以编组,因为.NET中没有与所有平台上的大小相匹配的类型。问题是在某些平台(Win32,Win64和Linux32)上C'long'类型可以是4个字节长,同时在其他平台(Linux64)上它可以是8个字节长。在.NET中有'int'类型,无论平台如何都是4字节长,并且有'long'类型,无论平台如何都是8字节长。它们都不能用作C'long'类型和mono documentation suggest to use IntPtr的多平台替代品,正如您已经发现的那样,但我发现这种方法非常不方便(它在Win64上也无法使用!)。因此,我个人更喜欢编组两组不同的函数和结构,一个用'int'NET类型用于C'long'类型为4字节长的平台,另一组用'long'.NET类型为C'long'类型长度为8个字节的平台。然后我只在运行时使用if (IntPtr.Size == 8)条件来决定应该使用哪个集合。