Философия UNIX или fork() vs CreateThread()

22 июля 2010

Вот уже вторую неделю в Москве царит невыносимая жара. Вентиляторы и кондиционеры в магазинах не найти, прохладительные напитки не помогают. Уровень желания работать (УЖР) по десятибалльной шкале — «ноль точка ноль», мысли путаются, кодинг не идет. Поэтому сегодняшний пост будет «на философскую тему» и почти не будет содержать кода.

Идею для этого поста подсказали мне вы, дорогие читатели блога. Если бы не ваши вопросы в комментариях (почему wget, а не LWP и почему fork(), а не pthread_*), его бы точно не было. Или он появился значительно позже. Так что большое вам спасибо!

Наверняка вы знаете или по крайней мере слышали о философии UNIX. Но если вдруг не слышали или подзабыли, всегда можно ознакомиться с соответствующей страницей в Википедии. Согласно ей:

Философия UNIX — это набор культурных норм и философских подходов к разработке программного обеспечения, основанных на опыте ведущих разработчиков операционной системы UNIX.

Я бы сказал, «многолетнем опыте разработчиков ПО под ОС семейства UNIX».

Философия UNIX гласит:

  • Пишите программы, которые делают что-то одно и делают это хорошо.
  • Пишите программы, которые бы работали вместе.
  • Пишите программы, которые бы поддерживали текстовые потоки, поскольку это универсальный интерфейс.

Звучит интересно, но как это работает на практике? Рассмотрим пример. Пусть у нас есть скрипт на Perl, который тянет некую страницу из интернета и парсит ее. Есть по крайней мере два способа сделать это — использовать либо утилиту wget (или любой аналогичный даунлоадер), либо perl-модуль LWP. Тут есть над чем подумать, так что обратимся к многолетнему опыту unix-программистов.

Так, давайте-ка посмотрим. «Работать вместе», «использовать текстовые потоки» — похоже, что по философии UNIX нужно сделать выбор в пользу wget. И действительно, wget имеет неоспоримые преимущества перед LWP. Во-первых, загрузить файл по протоколу HTTP или FTP с помощью wget можно, написав всего лишь одну строчку кода:

#!/usr/bin/perl
use strict;

my $data = `wget -q http://example.ru/ -O -`;

В случае же с LWP нужно намного больше кода:

#!/usr/bin/perl
use strict;
use LWP::UserAgent;

my $ua = LWP::UserAgent->new;
my $q = HTTP::Request->new(GET => 'http://example.ru/');
my $data = $ua->request($q);

Пока не особо убедительно, верно? А теперь давайте представим, что нам нужно скачать несколько страниц, список которых хранится в файле. Или рекурсивно загрузить содержимое всего сайта. У wget на оба случая предусмотрены специальный ключи командной строки — «-i» и «-r». А в случае с LWP нам придется писать дополнительный код и довольно много.

Но как же так получилось, что в модуле LWP не оказалось нужных нам функций? Просто разработчики этого модуля не следовали философии UNIX, а мыслили примерно так: «Вряд ли это кому-то понадобится, а если понадобится, напишет самостоятельно».

Смотрите-ка, что происходит. Мы используем всего лишь одну программу (wget), которая делает что-то одно (качает файлы) и делает это хорошо (ключи «-i» и «-r»). Прямо согласно «первому правилу» философии UNIX. Используя wget в своей программе, мы смогли существенно уменьшить объем кода, а значит и уменьшить время разработки, время отладки, сделать программу более простой и понятной.

Вы спросите, а как же быть, если мы хотим использовать наш скрипт как под UNIX, так и под Windows? Это будет не просто, но если установить wget.exe и сделать это правильно, скрипт будет прекрасно работать и под Windows безо всяких изменений. Но тут может возникнуть проблема производительности.

Дело в том, что в Windows, в отличие от UNIX, создание нового процесса — довольно «дорогая» операция. Мир Windows — это монолитные приложения, динамические библиотеки, CreateThread() и мьютексы, в то время, как мир UNIX — это небольшие утилиты, fork(), сигналы и пайпы. Да, в Windows тоже есть пайпы и дочерние процессы, ровно как и в UNIX есть нити и библиотеки, и это позволяет более-менее комфортно писать кроссплатформенные приложения. Но идеология остается разной.

Так какая же идеология более удачная? Что выгоднее для программистов и пользователей? Лично мне больше по душе unix way, потому что он проще. Также следуя ему намного проще как писать программы, так и отлаживать их. Программу, вызываемую с помощью fork-exec и имеющую ключ «-v» легко отладить даже без помощи gdb, а вот найти ошибку в многопоточном windows-приложении без OllyDbg бывает ой как не просто, я проверял! Мне иногда даже кажется, что ООП, UML, отладчики и все остальное придумали исключительно для Windows.

Хочу отметить, что все написанное — это лишь мое мнение, человека, пару месяцев назад закончившего институт. Не исключено, что когда-нибудь оно изменится. И пожалуйста, поймите меня правильно, я не фанатик философии UNIX. Судя по тому, что Winodws 7 и Ubuntu — вполне себе работоспособные операционки с кучей приложений, право на жизнь имеет каждый из подходов.

Дополнение: Преимущества и недостатки микросервисной архитектуры

Метки: , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.