Я пытаюсь получить вывод 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
?