从Linux上的.NET Core中的C#获取uname发布字段

问题描述 投票:0回答:1

我正在尝试在Ubuntu 18.04上运行的.NET Core 2.2中的C#中获取uname -r的输出。

我写这篇文章时考虑到了性能,所以一直在尝试使用P / Invoke来实现它。

uname(2)文档表明我需要使用相关大小的字段传递结构。在玩了很多变化之后,我想出了:

[StructLayout(LayoutKind.Sequential)]
unsafe internal struct Utsname
{
    public fixed byte sysname[65];

    public fixed byte nodename[65];

    public fixed byte release[65];

    public fixed byte version[65];

    public fixed byte machine[65];
}

public static class Main
{
    [DllImport("libc.so.6", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int uname(ref Utsname buf);

    public static void Main(string[] args)
    {
        byte[] bs = new byte[65];
        unsafe
        {
            var buf = new utsname();
            uname(ref buf);
            Marshal.Copy((IntPtr)buf.release, bs, 0, 65);
        }

        Console.WriteLine(Encoding.UTF8.GetString(bs));
    }
}

这似乎有效,但将其转换为包装函数,如:

public static class Main
{

...

    public static string GetUnameRelease()
    {
        var bs = new List<byte>();
        unsafe
        {
            var buf = new utsname();
            uname(ref buf);

            int i = 0;
            byte* p = buf.release;
            while (i < 65 && *p != 0)
            {
                bs.Add(*p);
                p++;
                i++;
            }
        }
        return Encoding.UTF8.GetString(bs.ToArray());
    }

    public static void Main(string[] args)
    {
        Console.WriteLine(GetUnameRelease());
    }
}

似乎导致它失败。我只是不确定我做错了什么。它无声地失败,可能是由于段错误,虽然我不知道在哪里/如何得到它的痕迹。

Other struct marshalling methods I've tried

我还尝试了一些其他方法来恢复结构。

最简单的似乎是具有固定长度值的string字段(但我认为这会失败,因为调用者需要为被调用者分配可变字段来设置):

internal struct Utsname
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
    public string sysname;

    ...
}

或者一个简单的byte数组:

internal struct Utsname
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 65)]
    public byte[] sysname;

    ...
}

在这种情况下,我假设在将托管数组传递给调用时,问题与In / Out调用约定有关。

我尝试使用out而不是ref来简化P / Invoke,但我得到的印象是uname()希望调用者在调用之前分配内存。

我也尝试使用[In][Out]属性,但不确定默认值是什么或如何使用它们会改变事物。

Writing an external C library to wrap the call

我还写了一个小的C库来包装调用,使调用约定更容易处理:

#include <string.h>
#include <stdlib.h>
#include <sys/utsname.h>

char *get_uname_release()
{
    struct utsname buf;

    uname(&buf);

    size_t len = strlen(buf.release);

    char *release = malloc(len * sizeof(char));

    strcpy(release, buf.release);

    return release;
}

我用gcc -shared -o libget_uname.so -fPIC get_uname.c编译了这个并将它放在主要的托管DLL旁边。

仅仅通过以下方式调用此操作就容易多了:

public static class Main
{
    ...

    [DllImport("libget_uname.so", EntryPoint = "uname_get_release", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    internal static extern string GetUnameRelease();
}

这似乎每次我都使用它。

但我反对在代码中包含一个本机库,如果可能直接用P / Invoke代替。

Using a Process call instead

另一个明显的简单选择就是将uname coreutil称为子进程:

public static class Main
{
    ...

    public static string GetUnameRelease()
    {
        var unameProc = new Process()
        {
            StartInfo = new ProcessStartInfo()
            {
                FileName = "uname",
                Arguments = "-r",
                UseShellExecute = false,
                RedirectStandardOutput = true,
                CreateNoWindow = true
            }
        };

        unameProc.Start();
        unameProc.WaitForExit();
        return unameProc.StandardOutput.ReadToEnd();
    }
}

但是我希望避免子进程的开销......也许它在Linux上并没有那么糟糕而且值得做什么?

但我现在花了一段时间研究PInvoke,所以我想知道它是否可能。

Questions

所以我的问题是:

  • 从C#获得releaseuname字段的最佳(最快可靠)方法是什么?
  • 我如何可靠地在libc中调用uname()系统调用来获取utsname结构?
c# .net-core posix pinvoke
1个回答
0
投票

将代码移动到函数时它不起作用的原因是你的结构不包含domainname成员,所以当你调用uname时,它会破坏你为你的结构分配的内存之外的内存。

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
unsafe internal struct Utsname
{
        public fixed byte sysname[65];
        public fixed byte nodename[65];
        public fixed byte release[65];
        public fixed byte version[65];
        public fixed byte machine[65];
        public fixed byte domainname[65];
}

public static class Program
{
        [DllImport("libc.so.6", CallingConvention = CallingConvention.Cdecl)]
        internal static extern int uname(ref Utsname buf);

        public static void Main(string[] args)
        {
                Console.WriteLine(GetUnameRelease());
        }

        static unsafe string GetUnameRelease()
        {
                Utsname buf;
                uname(ref buf);
                return Marshal.PtrToStringAnsi((IntPtr)buf.release);
        }
}
© www.soinside.com 2019 - 2024. All rights reserved.