Журнал LinuxFormat - перейти на главную

LXF98:Mono

Материал из Linuxformat
(Различия между версиями)
Перейти к: навигация, поиск
 
(не показаны 6 промежуточных версий 2 участников)
Строка 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> и продвигайтесь далее, обрабатывая аргументы и по желанию добавляя собственную функциональность.

Текущая версия на 18:25, 27 апреля 2008

Содержание

[править] 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() и продвигайтесь далее, обрабатывая аргументы и по желанию добавляя собственную функциональность.
Персональные инструменты
купить
подписаться
Яндекс.Метрика