2

I need to call the Linux-function sysinfo

It's declaration is int sysinfo(struct sysinfo *info); with

Until Linux 2.3.16, sysinfo() used to return information in the following structure:

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 */
};

and the sizes were given in bytes.

Since Linux 2.3.23 (i386), 2.3.48 (all architectures) the structure is:

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 */
};

This is what I have so far:

The function 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);  

And the struct mapping:

[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 */
}

The problem is that "long" in C code is processor-architecture-specific, because in x86 32-Bit Linux, long's size is 32 Bit, and in x86 64-Bit Linux, it's 64 bit, so I have to take IntPtr, and because it's unsigned, I take UIntPtr.

In C#/mono however, long is always defined as Int64.

Now it's a bit inconvenient to work with IntPtr. Is there any MarshalAs attribute I could apply, or a custom marshaler that I could write so that I can actually have ulong in the struct, but it maps natively to IntPtr ? So that the same code works on both x86-32 and x86-64.

Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442

2 Answers2

3

No, there isn't any marshalling magic you can do to fix this.

You can however hide the fields and provide property accessors:

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
}
Rolf Bjarne Kvinge
  • 19,253
  • 2
  • 42
  • 86
  • Have already started doing that before you wrote this answer. Except I put the accessors into a separate class, and create the sysinfo_t struct in the wrapper class as member, and perform the syscall in the constructor. It's easier to read that way. – Stefan Steiger May 07 '14 at 06:39
0

C 'long' type is very difficult to marshal since there is no type in .NET that matches its size on all platforms. The problem is that the C 'long' type can be 4 bytes long on some platforms (Win32, Win64 and Linux32) and in the same time it can be 8 bytes long on the other platforms (Linux64). In .NET there is 'int' type which is 4 bytes long regardless of platform and there is 'long' type which is 8 bytes long regardless of platform. Neither of them can be used as a multiplatform alternative for C 'long' type and mono documentation suggest to use IntPtr as you have already found out but I find this approach very inconvenient (it is also unusable on Win64!). Therefore I personally prefer to marshal two different sets of functions and structures, one with 'int' .NET type for platforms where C 'long' type is 4 bytes long and the other with 'long' .NET type for platforms where C 'long' type is 8 bytes long. Then I just use if (IntPtr.Size == 8) condition in runtime to decide which set should should be used.

jariq
  • 11,681
  • 3
  • 33
  • 52
  • Mmmmh, writing 2 structs is not much to my taste. I prefer DRY. – Stefan Steiger May 07 '14 at 06:36
  • 1
    @Quandary I agree with you in this specific case but once you'll need to support also Windows platforms then you will end with no other option. – jariq May 07 '14 at 07:23
  • Might also be enough to have another Linux system with ARM processor, like the ChromeBook. Then I'll probably go with an abstract class and a CreateInstance static method. – Stefan Steiger May 09 '14 at 10:42
  • Yep, arm definitely needs 4 http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Babfcgfc.html, but that works with the existing struct. – Stefan Steiger May 09 '14 at 11:00
  • IntPtr.Size == 8 has nothing to do with c long type size – denfromufa Feb 18 '17 at 03:12