Получить поле uname release из C # в .NET Core для Linux - PullRequest
0 голосов
/ 16 марта 2019

Я пытаюсь получить вывод uname -r в C # в .NET Core 2.2, работающем в Ubuntu 18.04.

Я пишу это с учетом производительности, поэтому пытался использовать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());
    }
}

Кажется, вызываетпотерпеть поражение.Я просто не уверен, что я делаю неправильно.Он молча терпит неудачу, предположительно из-за segfault, хотя я не уверен, где / как получить его след.

Другие методы структурирования, которые я пробовал

Я также попробовалнесколько других способов вернуть структуру.

Самыми простыми, кажется, были поля 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], но не уверен, какие значения по умолчанию или как их использовать, может изменить ситуацию.

Написание внешней библиотеки C для переноса вызова

Я тоже шповерните небольшую библиотеку 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 напрямую.

Используя вместо этого Process вызов

Другой очевидный простой выбор - просто вызвать 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Теперь я хотел бы знать, возможно ли это.

Вопросы

Итак, мои вопросы:

  • Какой самый лучший (самый надежный) способ получитьrelease поле из uname из C #?
  • Как бы IP / вызывал uname() syscall в libc надежно вернуть структуру utsname?

1 Ответ

0 голосов
/ 17 марта 2019

Причина, по которой он не работает при перемещении кода в функцию, заключается в том, что ваша структура не содержит члена 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);
        }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...