在 Linux 上的 .NET Core 中从 C# 获取 uname 发布字段 [英] Get uname release field from C# in .NET Core on Linux

查看:16
本文介绍了在 Linux 上的 .NET Core 中从 C# 获取 uname 发布字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to get the output of uname -r in C# in .NET Core 2.2 running on Ubuntu 18.04.

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

I'm writing this with performance in mind, so have been trying to use a P/Invoke to achieve it.

uname(2) 文档表明我需要传入一个具有相关大小字段的结构.在玩了很多变化之后,我想出了:

The uname(2) docs indicate I need to pass a struct in with the relevant sized fields. After playing with a lot of variations, I came up with:

[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));
    }
}

这似乎有效,但将其移动到一个包装函数中,例如:

This seems to work, but moving it into a wrapper function like:

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());
    }
}

似乎导致它失败.我只是不确定我做错了什么.它默默地失败了,大概是由于段错误,尽管我不确定在哪里/如何找到它的踪迹.

Seems to cause it to fail. I'm just not sure what I'm doing wrong. It fails silently, presumably due to a segfault, although I'm not sure where/how to get a trace of that.

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

I also tried a few other ways to get the struct back.

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

The simplest seemed to be the string fields with fixed-length values (but I assume this fails because the caller needs to allocate mutable fields for the callee to set):

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 调用约定有关.

In this case, I assume the problem is something to do with the In/Out calling convention when passing a managed array into the call.

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

I tried using out instead of ref to simplify the P/Invoke as well, but I get the impression uname() expects the caller to allocate the memory before the call.

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

I also tried using the [In] and [Out] attributes, but not sure what the defaults are or how using them would change things.

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

I also wrote a small C library to wrap the call to make the calling convention easier to handle:

#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 旁边.

I compiled this with gcc -shared -o libget_uname.so -fPIC get_uname.c and put it next to the main managed DLL.

调用它要容易得多,只需:

Calling this was much easier, with just:

public static class Main
{
    ...

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

每次我使用它时,这似乎都有效.

This seemed to work every time I used it.

但我不喜欢在代码中包含本机库,如果可以直接 P/Invoke 来代替的话.

But I'm averse to including a native library in code, if it might be possible to just P/Invoke directly instead.

另一个显而易见的简单选择就是将 uname coreutil 作为子进程调用:

The other obvious simple choice would just be to call the uname coreutil as a subprocess:

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 上并没有那么糟糕并且值得做?

But I was hoping to avoid the overhead of a subprocess... Perhaps it's not so bad on Linux and just worth doing?

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

But I've spent a while looking into the PInvoke now, so I would like to know if it's possible.

所以我的问题是:

  • 从 C# 的 uname 获取 release 字段的最佳(最可靠)方法是什么?
  • 我如何可靠地在 libc 中 P/Invoke uname() 系统调用以获取 utsname 结构?
  • What's the best (fastest reliable) way to get the release field from uname from C#?
  • How would I P/Invoke the uname() syscall in libc reliably to get the utsname struct back?

推荐答案

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

The reason it is not working when you move the code to a function is that your structure does not include the domainname member, so when you call uname it is clobbering memory beyond the memory you allocated for your structure.

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);
        }
}

这篇关于在 Linux 上的 .NET Core 中从 C# 获取 uname 发布字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆