LXF98:Mono

Материал из Linuxformat.

(Различия между версиями)
Перейти к: навигация, поиск
 
(6 промежуточных версий не показаны.)
Строка 1: Строка 1:
 +
{{Цикл/Mono}}
 +
==<font color=darkred>Mono:</font> Назад в Unix==
==<font color=darkred>Mono:</font> Назад в Unix==
Строка 11: Строка 13:
===Posix и вы===
===Posix и вы===
-
Имеется два типа людей, в основном использующих Posix: конечные пользователи и разработчики. Значит, практически все! Рассмотрим каждый тип отдельно, начав с пользователя. Да, я помню свои слова, что конечный пользователь игнорирует Posix, но это не совсем верно – фактически, вы используете преимущества инструментов и интерфейсов Posix при каждом обращении к командной строке. Видите ли, Posix заправляет едва ли не всем, что даруют Unix-подобные операционные системы – как работает ваша командная строка, есть ли инструменты типа awk и компиляторов, как происходит взаимодействие программ через каналы. Пусть даже конечные пользователи ничего не знают и знать не хотят о Posix, они обязательно опираются на его набор функций! Что касается разработчиков, то любой из пишущих код на С должен работать с одним из многих интерфейсов ядра и вызовами стандартной библиотеки С, входящими в Posix, и эти функции – например, malloc, system, printf, fopen и другие – доступны везде, куда ни сунься.
+
Имеется два типа людей, в основном использующих Posix: конечные пользователи и разработчики. Значит, практически все! Рассмотрим каждый тип отдельно, начав с пользователя. Да, я помню свои слова, что конечный пользователь игнорирует Posix, но это не совсем верно – фактически, вы используете преимущества инструментов и интерфейсов Posix при каждом обращении к командной строке. Видите ли, Posix заправляет едва ли не всем, что даруют Unix-подобные операционные системы – как работает ваша командная строка, есть ли инструменты типа awk и компиляторов, как происходит взаимодействие программ через каналы. Пусть даже конечные пользователи ничего не знают и знать не хотят о Posix, они обязательно опираются на его набор функций! Что касается разработчиков, то любой из пишущих код на С должен работать с одним из многих интерфейсов ядра и вызовами стандартной библиотеки С, входящими в Posix, и эти функции – например, <font color=darkred>malloc</font>, <font color=darkred>system</font>, <font color=darkred>printf</font>, <font color=darkred>fopen</font> и другие – доступны везде, куда ни сунься.
Это ставит нас перед вопросом: «Какой прок в использовании Posix?» Общеизвестно, что все системные вызовы Posix скопированы в стандарте среды .NET, с использованием управляемых эквивалентов: вы можете читать и записывать файлы, работать со строками, открывать сокеты, читать данные файловой системы и так далее, не беспокоясь о распределении памяти, потому что .NET освободит все, когда сработает сборщик мусора. Но использование версий Posix дает некоторые преимущества:
Это ставит нас перед вопросом: «Какой прок в использовании Posix?» Общеизвестно, что все системные вызовы Posix скопированы в стандарте среды .NET, с использованием управляемых эквивалентов: вы можете читать и записывать файлы, работать со строками, открывать сокеты, читать данные файловой системы и так далее, не беспокоясь о распределении памяти, потому что .NET освободит все, когда сработает сборщик мусора. Но использование версий Posix дает некоторые преимущества:
-
*Унаследованный код очень легко портировать. Вы можете взять код на C и запросто перенести его на C#, затем, при добавлении новых функций, добавить расширенную функциональность, присущую C#.
+
# Унаследованный код очень легко портировать. Вы можете взять код на C и запросто перенести его на C#, затем, при добавлении новых функций, добавить расширенную функциональность, присущую C#.
-
 
+
# В том же русле: для C-программистов вполне очевидно, что делает код C# Posix, а это облегчает изучение и сопровождение.
-
*В том же русле: для C-программистов вполне очевидно, что делает код C# Posix, а это облегчает изучение и сопровождение.
+
# Вы можете использовать преимущества специфичной для Posix функциональности. Например, чтение данных из файла '''/etc/passwd''' в обычном .NET коде необходимо делать вручную, а с использованием инструментов Posix это раз плюнуть.
-
 
+
-
*Вы можете использовать преимущества специфичной для Posix функциональности. Например, чтение данных из файла /etc/passwd в обычном .NET коде необходимо делать вручную, а с использованием инструментов Posix это раз плюнуть.
+
Итак, использование Posix не лишено преимуществ, но вдобавок имеется одно большое неудобство: львиная доля Posix работает с указателями.
Итак, использование Posix не лишено преимуществ, но вдобавок имеется одно большое неудобство: львиная доля Posix работает с указателями.
Строка 27: Строка 27:
===Базируемся на Stdlib===
===Базируемся на Stdlib===
-
Имеется три компонента для поддержки Unix в Mono: Mono.Posix, Mono.Unix и Mono.Unix.Native. Два последних отличаются лишь тем, что Mono.Unix – это небольшая обертка для Mono.Unix.Native, но вы можете использовать ту, где вам комфортнее.
+
Имеется три компонента для поддержки Unix в Mono: <font color=darkred>Mono.Posix</font>, <font color=darkred>Mono.Unix</font> и <font color=darkred>Mono.Unix.Native</font>. Два последних отличаются лишь тем, что <font color=darkred>Mono.Unix</font> – это небольшая обертка для <font color=darkred>Mono.Unix.Native</font>, но вы можете использовать ту, где вам комфортнее.
-
 
+
-
Начнем с простого: создадим новое решение под названием Monix, затем изменим его код Main.cs так:
+
 +
Начнем с простого: создадим новое решение под названием <font color=darkred>Monix</font>, затем изменим его код '''Main.cs''' так:
 +
<source lang="c">
using Mono.Posix;
using Mono.Posix;
using Mono.Unix;
using Mono.Unix;
Строка 39: Строка 39:
class Monix {
class Monix {
public static void Main(string[] args) {
public static void Main(string[] args) {
-
Stdlib.system(“ls”);
+
Stdlib.system("ls");
}
}
}
}
}
}
 +
</source>
 +
Этот простой код – основа для всех дальнейших: будем изменять только строку <font color=darkred>Stdlib.system()</font> да добавлять кое-какие кусочки.
 +
Проверьте наличие Mono.Posix и добавьте ссылку на него в проект. В нашем первом методе мы воспользуемся классом <font color=darkred>Stdlib</font> для вызова <font color=darkred>system()</font>. Класс <font color=darkred>Stdlib</font> содержит, в основном, статические методы, то есть вам не нужно создавать объект <font color=darkred>Stdlib</font> для вызова этих методов. Метод <font color=darkred>system()</font> (следите за регистром <font color=darkred>s</font> – он нижний: сейчас мы в стране С!) исполняет любую команду на локальной машине, словно он был введен в командной строке. Для нашего примера это означает запуск ls, поэтому программа выведет список каталогов, как если бы вы сами запустили «ls».
-
Этот простой код – основа для всех дальнейших: будем изменять только строку Stdlib.system() да добавлять кое-какие кусочки.
+
После ввода <font color=darkred>Stdlib.system()</font>, MonoDevelop должна вывести информацию о параметрах метода <font color=darkred>system()</font>, и вы увидите, что он принимает строки C#. В этом месте разработчики Mono адаптировали библиотеку вызовов C для лучшей совместимости с программированием .NET – обычно, в терминах С, <font color=darkred>system()</font> получает <font color=darkred>const char*</font>, так что использование строк более изящно!
-
Проверьте наличие Mono.Posix и добавьте ссылку на него в проект. В нашем первом методе мы воспользуемся классом Stdlib для вызова
+
-
system(). Класс Stdlib содержит, в основном, статические методы, то есть вам не нужно создавать объект Stdlib для вызова этих методов.
+
-
Метод system() (следите за регистром s – он нижний: сейчас мы в стране С!) исполняет любую команду на локальной машине, словно
+
-
он был введен в командной строке. Для нашего примера это означает запуск ls, поэтому программа выведет список каталогов, как если бы
+
-
вы сами запустили «ls».
+
-
После ввода Stdlib.system(, MonoDevelop должна вывести информацию о параметрах метода system(), и вы увидите, что он принимает строки C#. В этом месте разработчики Mono адаптировали библиотеку вызовов C для лучшей совместимости с программированием .NET – обычно, в терминах С, system() получает const char*, так что использование строк более изящно!
+
Этот переход существует лишь в некоторых методах. Например, <font color=darkred>printf()</font> также дружественен к .NET, поэтому вы можете писать код вроде этого:
-
 
+
<source lang="c">
-
Этот переход существует лишь в некоторых методах. Например, printf() также дружественен к .NET, поэтому вы можете писать код вроде этого:
+
Stdlib.printf("Hello, %s!\n", "world");
-
 
+
Stdlib.printf(string.Format("Hello, {0}!\n", "world"));
-
Stdlib.printf(“Hello, %s!\n”, “world”);
+
</source>
-
Stdlib.printf(string.Format(“Hello, {0}!\n”, “world”));
+
С другой стороны, методы <font color=darkred>fopen()</font>, <font color=darkred>fwrite()</font> и <font color=darkred>fclose()</font> для работы с файлами требуют указателей. В C# указатели известны как <font color=darkred>IntPtr</font>, потому что это представление указателя в целочисленном типе данных. Эти <font color=darkred>IntPtr</font>'ы могут восприниматься как данные с неизвестной структурой: их нельзя прочесть без использования специфичных для этих данных методов. Например, файлы открываются так:
-
 
+
<source lang="c">
-
С другой стороны, методы fopen(), fwrite() и fclose() для работы с файлами требуют указателей. В C# указатели известны как IntPtr, потому что это представление указателя в целочисленном типе данных. Эти IntPtr’ы могут восприниматься как данные с неизвестной структурой: их нельзя прочесть без использования специфичных для этих данных методов. Например, файлы открываются так:
+
IntPtr foo = Stdlib.fopen("file.txt", "w");
-
 
+
</source>
-
IntPtr foo = Stdlib.fopen(“file.txt”, “w”);
+
Но вы не можете читать или записывать с этого файлового дескриптора без других методов <font color=darkred>Stdlib. foo IntPtr</font> – всего лишь дескриптор данных, и сам по себе бесполезен. На самом деле, это даже небезопасно: любая память, присвоенная указателю, недоступна сборщику мусора Mono, и необходимо освобождать ее вручную, не то образуется утечка [memory leak]. Вы можете выполнить запись в этот файл, затем закрыть его так:
-
 
+
<source lang="c">
-
Но вы не можете читать или записывать с этого файлового дескриптора без других методов Stdlib. foo IntPtr – всего лишь дескриптор данных, и сам по себе бесполезен. На самом деле, это даже небезопасно: любая память, присвоенная указателю, недоступна сборщику мусора Mono, и необходимо освобождать ее вручную, не то образуется утечка [memory leak]. Вы можете выполнить запись в этот файл, затем закрыть его так:
+
Stdlib.fwrite(Encoding.ASCII.GetBytes("Hello, world!"), foo);
-
 
+
-
Stdlib.fwrite(Encoding.ASCII.GetBytes(“Hello, world!), foo);
+
Stdlib.fclose(foo);
Stdlib.fclose(foo);
-
 
+
</source>
===Развернем обертки===
===Развернем обертки===
Как указывалось ранее, Mono предоставляет набор упрощенных оберток для базовых структур данных и системных вызовов Unix. Например,
Как указывалось ранее, Mono предоставляет набор упрощенных оберток для базовых структур данных и системных вызовов Unix. Например,
любую информацию о пользователе можно прочесть, создав объект UnixUserInfo таким образом:
любую информацию о пользователе можно прочесть, создав объект UnixUserInfo таким образом:
-
 
+
<source lang="c">
-
UnixUserInfo user = new UnixUserInfo(“paul”);
+
UnixUserInfo user = new UnixUserInfo("paul");
Console.WriteLine(user.HomeDirectory);
Console.WriteLine(user.HomeDirectory);
-
 
+
</source>
-
Класс UnixUserInfo читает информацию из /etc/passwd, и вы можете увидеть имя пользователя, информацию о группах, их командных оболочках и так далее. Подобные структуры существуют и для файловых систем – следующая строка кода выудит информацию о вашем корневом каталоге:
+
Класс <font color=darkred>UnixUserInfo</font> читает информацию из '''/etc/passwd''', и вы можете увидеть имя пользователя, информацию о группах, их командных оболочках и так далее. Подобные структуры существуют и для файловых систем – следующая строка кода выудит информацию о вашем корневом каталоге:
-
 
+
<source lang="c">
-
UnixDriveInfo drive = new UnixDriveInfo(/);
+
UnixDriveInfo drive = new UnixDriveInfo("/");
-
 
+
</source>
-
Затем вы можете узнать объем свободного пространства на диске, прочитав drive.AvailableFreeSpace. Это число возвращается в байтах, поэтому вы можете пожелать удобства ради преобразовать его в гигабайты:
+
Затем вы можете узнать объем свободного пространства на диске, прочитав <font color=darkred>drive.AvailableFreeSpace</font>. Это число возвращается в байтах, поэтому вы можете пожелать удобства ради преобразовать его в гигабайты:
-
 
+
<source lang="c">
Console.WriteLine(drive.AvailableFreeSpace / 1024 / 1024 / 1024.0);
Console.WriteLine(drive.AvailableFreeSpace / 1024 / 1024 / 1024.0);
 +
</source>
 +
Последнее <font color=darkred>1024</font> записано как <font color=darkred>1024.0</font>, потому что это заставит Mono преобразовать конечный результат в число с плавающей точкой, а не в целое – в противном случае результат не будет точным!
-
Последнее 1024 записано как 1024.0, потому что это заставит Mono преобразовать конечный результат в число с плавающей точкой, а не в целое – в противном случае результат не будет точным!
+
Иногда эти обертки имеют собственные методы, как в случае с <font color=darkred>UnixFileInfo</font> – она читает информацию о конкретных файлах, предоставляя вам такие методы, как <font color=darkred>CanAccess()</font>, но, что более важно, позволяет создавать символьные ссылки на файл путем вызова функции <font color=darkred>CreateSymbolicLink()</font>, примерно так:
-
 
+
<source lang="c">
-
Иногда эти обертки имеют собственные методы, как в случае с UnixFileInfo – она читает информацию о конкретных файлах, предоставляя вам такие методы, как CanAccess(), но, что более важно, позволяет создавать символьные ссылки на файл путем вызова функции CreateSymbolicLink(), примерно так:
+
UnixFileInfo file = new UnixFileInfo("file.txt");
-
+
file.CreateSymbolicLink("filesym.txt"
-
UnixFileInfo file = new UnixFileInfo(“file.txt”);
+
</source>
-
file.CreateSymbolicLink(“filesym.txt”
+
создаст ссылку '''filesym.txt''' на '''file.txt''', как если бы вы выполнили ''ln -s file.txt filesym.txt'' в командной строке.
-
 
+
-
создаст ссылку filesym.txt на file.txt, как если бы вы выполнили ln -s file.txt filesym.txt в командной строке.
+
===Звенит сигнал тревоги===
===Звенит сигнал тревоги===
-
Последний метод, который я хочу показать – signal(), он просто показывает, насколько хорошо интегрированы Mono и библиотека C: вы можете попросить Linux вызвать метод C# при поступлении любого сигнала. «Сигнал» в стране C – это то, что происходит, когда ОС пытается по каким-то причинам прервать программу. Например, нажатие Ctrl+C посылает программе SIGINT, что обычно приводит к выходу. А если вы не хотите, чтобы программа завершалась? Что ж, тогда потрудитесь сообщить C#, как поступать при получении SIGINT, и это делается при помощи метода signal(). Он принимает два параметра: сигнал, который вы хотите перехватить, и имя функции, вызываемой при получении сигнала.
+
Последний метод, который я хочу показать – <font color=darkred>signal()</font>, он просто показывает, насколько хорошо интегрированы Mono и библиотека C: вы можете попросить Linux вызвать метод C# при поступлении любого сигнала. «Сигнал» в стране C – это то, что происходит, когда ОС пытается по каким-то причинам прервать программу. Например, нажатие Ctrl+C посылает программе <font color=darkred>SIGINT</font>, что обычно приводит к выходу. А если вы не хотите, чтобы программа завершалась? Что ж, тогда потрудитесь сообщить C#, как поступать при получении <font color=darkred>SIGINT</font>, и это делается при помощи метода <font color=darkred>signal()</font>. Он принимает два параметра: сигнал, который вы хотите перехватить, и имя функции, вызываемой при получении сигнала.
-
 
+
-
Говоря о SIGINT – вот код, который необходимо ввести в программе, чтобы она не отвечала на Ctrl+C:
+
 +
Говоря о <font color=darkred>SIGINT</font> – вот код, который необходимо ввести в программе, чтобы она не отвечала на <font color=darkblue>Ctrl+C</font>:
 +
<source lang="c">
Stdlib.signal(Mono.Unix.Native.Signum.SIGINT, HandleSigInt);
Stdlib.signal(Mono.Unix.Native.Signum.SIGINT, HandleSigInt);
-
 
+
</source>
-
HandleSigInt – новый метод, который необходимо создать за пределами Main(). Вот пример:
+
<font color=darkred>HandleSigInt</font> – новый метод, который необходимо создать за пределами <font color=darkred>Main()</font>. Вот пример:
-
 
+
<source lang="c">
public static void HandleSigInt(int sig) {
public static void HandleSigInt(int sig) {
-
Console.WriteLine(“А я против!\n”);
+
Console.WriteLine(я против!\n");
}
}
-
 
+
</source>
-
Теперь при нажатии Ctrl+C пользователь получит сообщение-отказ; но это не остановит сигнал SIGKILL (посылаемый, когда кто-то выполняет kill -9 <ваш pid>).
+
Теперь при нажатии <font color=darkblue>Ctrl+C</font> пользователь получит сообщение-отказ; но это не остановит сигнал <font color=darkred>SIGKILL</font> (посылаемый, когда кто-то выполняет <font color=darkred>kill -9 <ваш pid></font>).
Конечно, вы не сможете протестировать обработку вашей программой сигналов прерывания, пока не заставите ее работать бесконечно:
Конечно, вы не сможете протестировать обработку вашей программой сигналов прерывания, пока не заставите ее работать бесконечно:
-
 
+
<source lang="c">
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
-
 
+
</source>
На этом наш блиц-тур по интеграции Mono и Unix закончен. Пожалуйста, не забывайте о потенциальных проблемах: утечки памяти – особенно в длительно работающих программах – могут вызвать серьезные осложнения, а привычка работы с Mono способствует небрежному обращению с памятью. Применение родных функций Unix делает миграцию с C на C# быстрой и простой, но в долгосрочной перспективе лучше начать вытеснять функции C-эквивалентами, родными для .NET...
На этом наш блиц-тур по интеграции Mono и Unix закончен. Пожалуйста, не забывайте о потенциальных проблемах: утечки памяти – особенно в длительно работающих программах – могут вызвать серьезные осложнения, а привычка работы с Mono способствует небрежному обращению с памятью. Применение родных функций Unix делает миграцию с C на C# быстрой и простой, но в долгосрочной перспективе лучше начать вытеснять функции C-эквивалентами, родными для .NET...
 +
 +
==Врезки==
 +
 +
===Скорая помощь===
 +
 +
*Не пытайтесь проигнорировать сигнал <font color=darkred>SIGKILL</font> – все равно не удастся: в противном случае некоторые программы никогда не завершались бы!
 +
 +
*Используя <font color=darkred>system()</font> и аргументы, переданные вашей функции, вы можете за минуты сколотить быструю оболочку. Начните с пересылки всего переданного в <font color=darkred>system()</font> и продвигайтесь далее, обрабатывая аргументы и по желанию добавляя собственную функциональность.

Текущая версия

C# для начинающих

Содержание

Mono: Назад в Unix

Хотя C# и новый, и передовой, Mono стоит на плечах уродливого монстра Posix. Пол Хадсон пробует заставить Unix-натуру Linux сработаться с .NET...

Имеет ли место садомазохизм в мире компьютеров? Если да, то вот он: я покажу вам, как заставить C# идти бок о бок с Posix и выиграть. Да, Posix – этот дурно задуманный процесс стандартизации, сбивающий с толку программистов, игнорируемый конечными пользователями, и все же подпирающий Linux и другие Unix-подобные ОС. Posix – это набор системных вызовов, интерфейсов и сигналов, определяющий, как мы, разработчики, взаимодействуем с операционной системой. Действующий стандарт Posix весьма обширен, но по сути мы должны заботиться только вот о чем: если вы пишете Posix-совместимый код, он должен работать в любой Posix-совместимой ОС.

Как ни странно, список совместимости включает Windows Vista, точнее, большинство основанных на NT версий Windows, коль скоро они имеют установленными службы Services for Unix. Но с нашей точки зрения важно то, что Linux, FreeBSD, OpenBSD и Syllable практически, а AIX, HP-UX, Minix, OS X и Solaris – полностью поддерживают Posix. Короче, использование функциональности Posix может заставить вас рвать на себе волосы, но, по крайней мере, вы в хорошей компании!

Posix и вы

Имеется два типа людей, в основном использующих Posix: конечные пользователи и разработчики. Значит, практически все! Рассмотрим каждый тип отдельно, начав с пользователя. Да, я помню свои слова, что конечный пользователь игнорирует Posix, но это не совсем верно – фактически, вы используете преимущества инструментов и интерфейсов Posix при каждом обращении к командной строке. Видите ли, Posix заправляет едва ли не всем, что даруют Unix-подобные операционные системы – как работает ваша командная строка, есть ли инструменты типа awk и компиляторов, как происходит взаимодействие программ через каналы. Пусть даже конечные пользователи ничего не знают и знать не хотят о Posix, они обязательно опираются на его набор функций! Что касается разработчиков, то любой из пишущих код на С должен работать с одним из многих интерфейсов ядра и вызовами стандартной библиотеки С, входящими в Posix, и эти функции – например, malloc, system, printf, fopen и другие – доступны везде, куда ни сунься.

Это ставит нас перед вопросом: «Какой прок в использовании Posix?» Общеизвестно, что все системные вызовы Posix скопированы в стандарте среды .NET, с использованием управляемых эквивалентов: вы можете читать и записывать файлы, работать со строками, открывать сокеты, читать данные файловой системы и так далее, не беспокоясь о распределении памяти, потому что .NET освободит все, когда сработает сборщик мусора. Но использование версий Posix дает некоторые преимущества:

  1. Унаследованный код очень легко портировать. Вы можете взять код на C и запросто перенести его на C#, затем, при добавлении новых функций, добавить расширенную функциональность, присущую C#.
  2. В том же русле: для C-программистов вполне очевидно, что делает код C# Posix, а это облегчает изучение и сопровождение.
  3. Вы можете использовать преимущества специфичной для Posix функциональности. Например, чтение данных из файла /etc/passwd в обычном .NET коде необходимо делать вручную, а с использованием инструментов Posix это раз плюнуть.

Итак, использование Posix не лишено преимуществ, но вдобавок имеется одно большое неудобство: львиная доля Posix работает с указателями.

«Указатели?» Так и слышу, как вы охнули. «Привет! Говорят 1980-е! Они требуют обратно свой безумный, анахроничный, осложненный переполнениями буфера доступ к памяти!» Именно так. Указатели – это программные имена, описывающие конкретный участок памяти. Например, переменная – указатель на строку содержит точный адрес в памяти, где располагается строка текста. Понятно, что это прекрасно для быстродействия, так как между программой и оборудованием нет посредников, но ужасно с точки зрения безопасности, потому что программа имеет полную власть над вашим компьютером: даже крошечная щелочка в безопасности может вылиться в захват системы. Теперь, когда вы знаете все о плюсах и минусах Posix, давайте нырнем в него и посмотрим, что тут можно сделать...

Базируемся на Stdlib

Имеется три компонента для поддержки Unix в Mono: Mono.Posix, Mono.Unix и Mono.Unix.Native. Два последних отличаются лишь тем, что Mono.Unix – это небольшая обертка для Mono.Unix.Native, но вы можете использовать ту, где вам комфортнее.

Начнем с простого: создадим новое решение под названием Monix, затем изменим его код Main.cs так:

using Mono.Posix;
 using Mono.Unix;
 using Mono.Unix.Native;
 using System;
 using System.Text;
 namespace monix {
    class Monix {
     public static void Main(string[] args) {
       Stdlib.system("ls");
     }
   }
 }

Этот простой код – основа для всех дальнейших: будем изменять только строку Stdlib.system() да добавлять кое-какие кусочки. Проверьте наличие Mono.Posix и добавьте ссылку на него в проект. В нашем первом методе мы воспользуемся классом Stdlib для вызова system(). Класс Stdlib содержит, в основном, статические методы, то есть вам не нужно создавать объект Stdlib для вызова этих методов. Метод system() (следите за регистром s – он нижний: сейчас мы в стране С!) исполняет любую команду на локальной машине, словно он был введен в командной строке. Для нашего примера это означает запуск ls, поэтому программа выведет список каталогов, как если бы вы сами запустили «ls».

После ввода Stdlib.system(), MonoDevelop должна вывести информацию о параметрах метода system(), и вы увидите, что он принимает строки C#. В этом месте разработчики Mono адаптировали библиотеку вызовов C для лучшей совместимости с программированием .NET – обычно, в терминах С, system() получает const char*, так что использование строк более изящно!

Этот переход существует лишь в некоторых методах. Например, printf() также дружественен к .NET, поэтому вы можете писать код вроде этого:

Stdlib.printf("Hello, %s!\n", "world");
 Stdlib.printf(string.Format("Hello, {0}!\n", "world"));

С другой стороны, методы fopen(), fwrite() и fclose() для работы с файлами требуют указателей. В C# указатели известны как IntPtr, потому что это представление указателя в целочисленном типе данных. Эти IntPtr'ы могут восприниматься как данные с неизвестной структурой: их нельзя прочесть без использования специфичных для этих данных методов. Например, файлы открываются так:

IntPtr foo = Stdlib.fopen("file.txt", "w");

Но вы не можете читать или записывать с этого файлового дескриптора без других методов Stdlib. foo IntPtr – всего лишь дескриптор данных, и сам по себе бесполезен. На самом деле, это даже небезопасно: любая память, присвоенная указателю, недоступна сборщику мусора Mono, и необходимо освобождать ее вручную, не то образуется утечка [memory leak]. Вы можете выполнить запись в этот файл, затем закрыть его так:

Stdlib.fwrite(Encoding.ASCII.GetBytes("Hello, world!"), foo);
 Stdlib.fclose(foo);

Развернем обертки

Как указывалось ранее, Mono предоставляет набор упрощенных оберток для базовых структур данных и системных вызовов Unix. Например, любую информацию о пользователе можно прочесть, создав объект UnixUserInfo таким образом:

UnixUserInfo user = new UnixUserInfo("paul");
 Console.WriteLine(user.HomeDirectory);

Класс UnixUserInfo читает информацию из /etc/passwd, и вы можете увидеть имя пользователя, информацию о группах, их командных оболочках и так далее. Подобные структуры существуют и для файловых систем – следующая строка кода выудит информацию о вашем корневом каталоге:

UnixDriveInfo drive = new UnixDriveInfo("/");

Затем вы можете узнать объем свободного пространства на диске, прочитав drive.AvailableFreeSpace. Это число возвращается в байтах, поэтому вы можете пожелать удобства ради преобразовать его в гигабайты:

Console.WriteLine(drive.AvailableFreeSpace / 1024 / 1024 / 1024.0);

Последнее 1024 записано как 1024.0, потому что это заставит Mono преобразовать конечный результат в число с плавающей точкой, а не в целое – в противном случае результат не будет точным!

Иногда эти обертки имеют собственные методы, как в случае с UnixFileInfo – она читает информацию о конкретных файлах, предоставляя вам такие методы, как CanAccess(), но, что более важно, позволяет создавать символьные ссылки на файл путем вызова функции CreateSymbolicLink(), примерно так:

UnixFileInfo file = new UnixFileInfo("file.txt");
 file.CreateSymbolicLink("filesym.txt"

создаст ссылку filesym.txt на file.txt, как если бы вы выполнили ln -s file.txt filesym.txt в командной строке.

Звенит сигнал тревоги

Последний метод, который я хочу показать – signal(), он просто показывает, насколько хорошо интегрированы Mono и библиотека C: вы можете попросить Linux вызвать метод C# при поступлении любого сигнала. «Сигнал» в стране C – это то, что происходит, когда ОС пытается по каким-то причинам прервать программу. Например, нажатие Ctrl+C посылает программе SIGINT, что обычно приводит к выходу. А если вы не хотите, чтобы программа завершалась? Что ж, тогда потрудитесь сообщить C#, как поступать при получении SIGINT, и это делается при помощи метода signal(). Он принимает два параметра: сигнал, который вы хотите перехватить, и имя функции, вызываемой при получении сигнала.

Говоря о SIGINT – вот код, который необходимо ввести в программе, чтобы она не отвечала на Ctrl+C:

Stdlib.signal(Mono.Unix.Native.Signum.SIGINT, HandleSigInt);

HandleSigInt – новый метод, который необходимо создать за пределами Main(). Вот пример:

public static void HandleSigInt(int sig) {
    Console.WriteLine("А я против!\n");
 }

Теперь при нажатии Ctrl+C пользователь получит сообщение-отказ; но это не остановит сигнал SIGKILL (посылаемый, когда кто-то выполняет kill -9 <ваш pid>).

Конечно, вы не сможете протестировать обработку вашей программой сигналов прерывания, пока не заставите ее работать бесконечно:

System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);

На этом наш блиц-тур по интеграции Mono и Unix закончен. Пожалуйста, не забывайте о потенциальных проблемах: утечки памяти – особенно в длительно работающих программах – могут вызвать серьезные осложнения, а привычка работы с Mono способствует небрежному обращению с памятью. Применение родных функций Unix делает миграцию с C на C# быстрой и простой, но в долгосрочной перспективе лучше начать вытеснять функции C-эквивалентами, родными для .NET...

Врезки

Скорая помощь

  • Не пытайтесь проигнорировать сигнал SIGKILL – все равно не удастся: в противном случае некоторые программы никогда не завершались бы!
  • Используя system() и аргументы, переданные вашей функции, вы можете за минуты сколотить быструю оболочку. Начните с пересылки всего переданного в system() и продвигайтесь далее, обрабатывая аргументы и по желанию добавляя собственную функциональность.
Личные инструменты
  • Купить электронную версию
  • Подписаться на бумажную версию