【问题标题】:C# pInvoke with long in both 32 & 64 Bit Linux:在 32 位和 64 位 Linux 中使用 long 的 C# pInvoke:
【发布时间】:2014-05-06 10:48:35
【问题描述】:

我需要调用 Linux 函数sysinfo

它的声明是 int sysinfo(结构 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 代码中的“long”是特定于处理器架构的,因为在 x86 32 位 Linux 中,long 的大小是 32 位,而在 x86 64 位 Linux 中,它是 64 位,所以我必须取 IntPtr,因为它是无符号的,所以我取 UIntPtr。

然而,在 C#/mono 中,long 始终定义为 Int64。

现在使用 IntPtr 有点不方便。 是否有任何我可以应用的 MarshalAs 属性,或者我可以编写的自定义封送拆收器,以便我实际上可以在结构中拥有 ulong ,但它本机映射到 IntPtr ?这样相同的代码就可以在 x86-32 和 x86-64 上运行。

【问题讨论】:

    标签: c# linux mono pinvoke marshalling


    【解决方案1】:

    不,没有任何编组魔法可以解决此问题。

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

    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
    }
    

    【讨论】:

    • 在你写这个答案之前已经开始这样做了。除了我将访问器放入一个单独的类中,并在包装​​类中创建 sysinfo_t 结构作为成员,并在构造函数中执行系统调用。这样更容易阅读。
    【解决方案2】:

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

    【讨论】:

    • 嗯,写 2 个结构对我来说并不多。我更喜欢 DRY。
    • @Quandary 在这种特定情况下我同意你的看法,但是一旦你还需要支持 Windows 平台,那么你将别无选择。
    • 可能也足以拥有另一个带有 ARM 处理器的 Linux 系统,例如 ChromeBook。然后我可能会使用一个抽象类和一个 CreateInstance 静态方法。
    • 是的,arm 肯定需要 4 个infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/…,但这适用于现有结构。
    • IntPtr.Size == 8 与 c long 类型大小无关
    猜你喜欢
    • 1970-01-01
    • 2014-11-06
    • 2013-08-01
    • 2014-07-28
    • 2015-01-07
    • 2013-10-23
    • 2011-11-08
    • 1970-01-01
    • 2012-09-17
    相关资源
    最近更新 更多