Начало! Этапы загрузки ОС Linux (в схеме)

24 мая, 2010 Рубрики: Linux, основы Linux

Загрузка Linux, System V, Upstart, BSDПриветствую всех посетителей моего родившегося блога!

В своей первой статье опишу свои первые познания в ОС Linux. Недавно поменял место работы и по служебной необходимости пришлось внедрять в свой мозг новую для себя операционную систему Linux. Ранее приходилось сталкиваться с Linux только как на рабочей станции, даже без привязки к локальной сети. Максимум до чего дошли руки – устанавливать rpm пакеты, читая пошаговый HOWTO, изъятый из просторов гугла и с использованием менеджера пакетов synaptic. При этом настройка занимала громадную уйму времени…

До настоящего времени приходилось администрировать только сети на основе просящего много денег мелкософта. Но с последними тенденциями в сфере лицензирования и обращения особого внимания органов на отсутствие заветных наклеечек на компьютерах организации, а так же желании руководства сэкономить на программном обеспечении (а что, программы продаются чтоли? О_о (с) ) пришлось “сесть собаку” на вопросах лицензий и тем самым подтолкнул себя к более глубокому изучению продуктов ОпенСорса. К тому же на новой работе стоит старенький ALT Linux Compact 3 с почтовиком qmail и pptp сервером для удаленного доступа, которых хочется скорей переустановить, ибо клиенты умирают от спама, места для почты практически не осталось + хочется какой-нибудь небольшой корпоративный Жаббер да и еще кучу пожеланий….

В общем начал я изучение с самых основ. В данной статье размещу, как можно более понятную последовательную схему загрузки ОС Linux. Данная схема, скажем так, плод моих умозаключений, буду рад вашим комментариям и дополнениям!

Итак:

Загрузка Linux, stage one

Описание 1 этапа:

Не углубляясь в кучу терминов и определений, данный этап можно описать следующими словами: BIOS из MBR (первые 512 байт диска, выбранного для загрузки) загружает First Boot Loader. FSB находит вторичный загрузчик, используя таблицу разделов, просматривая ее, обнаруживает активный раздел, после обнаружения этого раздела – загружает SSB в оперативную память и запускает его. Для корректной загрузки, активный раздел должен содержать каталог /boot,  который должен находиться в начале диска и содержать Second Stage Boot Loader. В целом, SSB – это программа, которая выводит список вариантов загрузки (меню выбора загрузки операционной системы). Загрузчиком может быть LILO (более старый) или GRUB. Загрузчик берет свои настройки из конфигурационного файла (/etc/lilo.conf – для LILO и /boot/grub/grub.conf или /boot/grub/menu.lst – для GRUB). Существуют и другие версии загрузчиков, такие как syslinux, PXElinux, Isolinux, uBoot, но для наглядности, в статье я затронул только LILO и GRUB. Хочу отметить, что исторически (до появления загрузчиков LILO, GRUB и др. и когда образ ядра занимал объем не боле 1,44 Мб) данного этапа не существовало и загрузка происходила с дискеты без файловой системы, на которую был записан образ ядра Linux, который (образ) содержал в себе MBR, то есть BIOS загружал сразу образ ядра и передавал ему управление.

Вторая стадия загрузки Linux

Описание 2 этапа:

 Второй этап можно характеризовать так: подготовка системы для запуска служб демонов. При подготовке, Загрузчик загружает в память образ ядра из каталога /boot. Давайте рассмотрим пример образа ядра на примере ОС Debian 6:

boot@debian:~# file /boot/vmlinuz-2.6.32-5-686
/boot/vmlinuz-2.6.32-5-686: Linux kernel x86 boot executable bzImage, \
     version 2.6.32-5-686 (unknown@Debian) #, RO-rootFS, swap_dev 0x2, Normal VGA

В приведенном листинге, видно, что команда file выводит информацию о файле образа ядра. В данной информации говориться, что это ядро линукс (Linux kernel), 32-битной архитектуры (x86), содержащий возможность загрузки (boot), исполняемый (executable), в формате bzImage (то есть сжатое, бывают образы не сжатые), далее указывается версия ядра и кое-какие другие параметры образа. Данных файлов может быть несколько (в зависимости от количества установленных версий ядра) и для загрузки выбирается тот, который указан в настройках загрузчика. Образ ядра, инициализирует и подготавливает память, процессор/ы, остальное оборудование, монтирует корневой раздел в режиме только для чтения для загрузки остальной системы (устройство и раздел на котором размещен корень системы должен быть указан в настройках загрузчика GRUB (/boor/grub.conf) или LILO (/boor/lilo.conf)) в виде параметра root=. При этом, выводится сообщение VFS: Mounted root (ext2 filesystem) readonly. Кроме того, ядро из конфигурационного файла загрузчика получает параметры загрузки, такие как корневая файловая система, отображать сообщения ядра или нет и т.п. Параметры, переданные текущему загруженному ядру можно посмотреть в файле /proc/cmdline. Вот пример параметров все того же Debian:

boot@debian:~# cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-2.6.32-5-686 root=UUID=6852e86c-b8f1-49d0-b1eb-9d10171083c3 ro quiet

Т.к. ядро Linux является модульным, то при загрузке может возникнуть необходимость подключить модуль ядра, который находится на еще не примонтированной файловой системе. Для решения данной проблемы при загрузке подгружается архив файловой системы (он же инициализационный RAM диск или initrd), содержащий в себе необходимый для загрузки набор модулей ядра. Вот так он выглядит для указанного выше ядра:

root@debian:~# file /boot/initrd.img-2.6.32-5-686
/boot/initrd.img-2.6.32-5-686: gzip compressed data, from Unix, last modified: Thu Mar 17 09:44:39 2011

Какой архив initrd подгружать при загрузке, так же указывается в FSB GRUB или LILO:

boot@debian:~# grep initrd -B4 /boot/grub/menu.lst
title           Debian GNU/Linux, kernel 2.6.26-2-686
root            (hd0,0)
kernel          /boot/vmlinuz-2.6.26-2-686 root=/dev/sda1 ro quiet
initrd          /boot/initrd.img-2.6.26-2-686

Т.к. стандартный вывод (вывод сообщений на экран) должен быть связан с каким-либо процессом, соответственно с идентификатором процесса, а у ядра нет идентификатора, оно оно помещает сообщения ядра (и модулей) в буфер кольца ядра и выводит на экран. Данный буфер еще называется dmesg. Его содержимое можно просмотреть, выполнив команду dmesg. После полной инициализации ядро передает управление процеcсу init (первому системному процессу с PID=1). На экран выводится сообщение INIT: version 2.76 booting. При этом, бинарный файл init последовательно ищется в корневом разделе в каталогах: /sbin/init, /etc/init, /bin/init, если в указанных местах не обнаружен файл, то ядро пытается запустить шелл /bin/sh (это, собственно, есть однопользовательский режим загрузки, он же режим восстановления). При этом, не запускается ни один демон. Если не найден и шелл, то вываливается ошибка Kernel panic: No init found. Try passing init= option. Данная ситуация может возникнуть скорее всего, потому что неверно смонтирован корневой раздел.

Третья стадия загрузки Linux

Описание 3 этапа:

До текущего момента процесс запуска любой UNIX системы практически не отличался. Третий этап загрузки может отличаться в зависимости от платформы, будь то Linux, BSD, MacOS и др. Я подробно рассмотрю процесс загрузки операционной системы Linux с реализацией процесса запуска с помощью пакета sysvinit (так же именуемого System V – “систем 5“). В Linux в последнее время активно внедряется разработанный “убунтологами” пакет upstart, который приходит на смену SysV, данный процесс я так же кратко опишу и немного расскажу о загрузке BSD-систем.

На третьем этапе загрузки System V происходит следующее:

После запуска, процесс init, согласно конфигурации в файле /etc/inittab (а точнее строке, начинающийся на si::sysinit:/etc/……) первым делом выполняет скрипт /etc/rc.d/rc.sysinit (для RedHat) или /etc/init.d/rcS (для Debian), которые выполняют базовое конфигурирование системы (загрузка модулей, проверка корневой ФС и монтирование не чтение/запись, установка имени хоста, времени, монтирование оставшихся разделов, запуск сети, монтирование сетевых ФС и др.), а так же данный скрипт с помощью утилиты initlog направляет сообщения о загрузке в /var/log/messages. На данном этапе, нет уровня выполнения. Далее, запускается скрипт инициализации /etc/rc.d/rc (для RedHat) или /etc/init.d/rc (для Debian), которому передается уровень запуска в виде параметра от 0 до 6 (в соответствии с настройками из файла /etc/inittab, в котором указан уровень загрузки (выполнения) ОС по умолчанию и каталог /etc/rc*.d, в котором расположены скрипты запуска демонов/служб для соответствующего уровня запуска), запускает скрипты из каталога, соответствующего текущему уровню запуска.

Далее, процесс init согласно уровню загрузки просматривает каталог /etc/rc.d/rc0.d/ (в данном примере, цифра 0(ноль) в имени rc0.d соответствует уровню загрузки – нулевому), в котором содержатся симлинки (ссылки) на скрипты запуска системных служб, которые, в свою очередь, расположены в /etc/rc.d/init.d/ (для Red Hat) или в /etc/init.d/ (для Debian). Ссылки имеют следующий формат:

<S|K><число><имя_службы>, в котором: S – запуск службы (Start), K – остановка службы (Kill), <число> – число, определяющее последовательность запуска служб (00 – самая первая), <имя_службы> -имя запускаемой службы.

Уровни выполнения бывают следующие:

  • 0: полная остановка машины;
  • 1: single-user (однопользовательский) режим; (используется в случае серьезных проблем или для восстановления системы)
  • 2: multi-user (многопользовательский) режим, без поддержки сети;
  • 3: Мulti-user (многопользовательский) режим с поддержкой сети; (используется преимущественно на серверных системах)
  • 4: неиспользуемый;
  • 5: Мulti-user (многопользовательский) режим с поддержкой сети + графический интерфейс для входа в систему (login);
  • 6: перезагрузка.

При этом, в разных дистрибутивах, возможны вариации уровней загрузки. Дистрибутив Slackware использует уровень выполнения 4 вместо 5 для полного запуска системы X Window. Debian использует один уровень выполнения для любого многопользовательского режима, обычно это уровень 2.

После запуска всех демонов, содержащихся в каталоге /etc/rc.d/rc0.d/, процесс Init запускает сценарий /etc/rc.d/rc.local (для RedHat) или /etc/rc.local (для Debian). В данном сценарии можно разместить свои настройки, которые вступят в силу после запуска всех демонов.

Далее, процесс Init запускает процесс mingetty, который запрашивает имя пользователя (о расположении mingetty, также сказано в файл /etc/inittab). После ввода имени пользователя, mingetty передает введенную информацию процессу login. Процесс login просматривает файлы /etc/passwd и /etc/shadow на наличие указанного пользователя и выводит запрос пароля. После ввода пароля пользователя, процесс login сравнивает хеш пароля с данными в файле /etc/shadow и в случае совпадения, запускает шелл.

Указанный процесс актуален для входа в текстовую консоль, при входе в графическую оболочку, вместо mingetty, процесс init запускает процесс gdm (для GNOME) или kdm (для KDE), в зависимости от того, какой оконный менеджер установлен в системе. gdm (или kdm) запускает X-сервер и выводит окно аутентификации.

После успешной аутентификации, просматривается файл конфигурации /etc/group, который определяет принадлежность пользователя к группам. А так же запускается стартовый конфигурационный сценарий /etc/X11/gdm/PreSession (для X-сервера) и конфигурационные стартовые скрипты (/etc/profile и др.) для bash, которые устанавливают настройки окружения пользователя.

Уровнем выполнения можно управлять с помощью команд.

На третьем этапе загрузки Upstart происходит следующее:

Давайте немного затронем вопрос загрузки новой системы Upstart, которая введена в действие с Ubuntu 7.10 и старше, Fedora 9, RHEL 6, планируется в Debian, …? (если есть другие дистрибутивы с uppstart, буду рад комментариям об этом). Основное отличие upstart от system V в том, что работа upstart основана на обработке событий. К стати будет сказано, что одной из основных задач внедрения upstart была – уйти от последовательного запуска сервисов в SysV, тем самым ускорить загрузку ОС, сделав процесс запуска служб параллельным. Итак, система upstart при запуске, процессом init генерируется событие startup (запуск – одно из двух основных событий) – старт системы, а событие shutdown – при выключении системы. Другое ключевое событие – это ctrlaltdel, которое указывает, на то, что вы нажали клавиши Ctrl-Alt-Delete. В соответствии с событием генерируемым процессом init, обрабатываются конфигурационные фалы (*.conf) из каталога /etc/init/ (в более старых версиях upstart – каталог /etc/event.d/). В этом, собственно, и заключается вся работа upstart. Для обратной совместимости с System V существует файл /etc/init/rc.conf, который запускает демоны, согласно SysV.

Давайте рассмотрим содержимое файлов /etc/init/*.conf. Каждый из файлов должен содержать start on event (по какому (on) событию (event) осуществлять запуск (start) службы), stop on event (по какому событию останавливать запуск службы), exec либо script (что, собственно, запустить по указанному выше событию). Наиболее часто применяемые события в конфигурационных файлах – это startup, runlevel, stopped и started. startup – это событие запуска операционной системы, runlevel (с указанием уровня или нескольких уровней) – событие при смене уровня запуска, stopped – событие после остановки задания, started – событие после завершения старта задания. Просмотрев конфигурационные файлы в каталоге /etc/init/ можно найти и другие события, о которых можно почитать в man upstart-events (7). В дополнение к стандартным записям exec и script существуют  записи pre-start и post-stop, реализующие выполнение подготовительных действия до выполнения exec и завершающие после завершения exec.

Данной системой загрузки также можно управлять командами.

Для отключения автоматического запуска службы в Upstart, необходимо:

  • Переименовать конфигурационный файл запуска службы в каталоге /etc/init в файл без расширения  “.conf”.
  • Или закомментировать строку “start on” с помощью символа ‘#’.

На третьем этапе загрузки BSD происходит следующее:

Перед описанием 3 этапа загрузки BSD хочу отметить, что по-умолчанию на 1 этапе используется загрузчик BSD Loader, работа которого аналогична любому другому загрузчику и тоже разбита на  подэтапы (загрузка FSB, SSB и т.д.). Файл конфигурации BSD Loader расположен в /boot/loader.conf. Соответственно, после запуска BSD Loader, происходит передача управления ядру и запуск процесса /sbin/init. Дальше запуск несколько отличается от Linux, т.к. в BSD нет понятия runlevel, то есть существует единый уровень исполнения. НО в BSD есть 2 режима загрузкиоднопользовательский (режим восстановления) и многопользовательский.

В однопользовательском режиме загрузка происходит:

  1. при выборе соответствующего пункта (Boot in single user mode) в меню начального загрузчика,
  2. при задании команды boot -s в командной строке загрузчика (после выбора пункта его меню Escape to loader prompt),
  3. при обнаружении серьезных (неустранимых автоматически) нарушений целостности файловой системы в ходе ее проверки на первой стадии инициализации.

При загрузке в однопользовательском режиме происходит запуск оболочки без пароля с правами суперпользователя и монтирование корневой ФС только на чтение. Запуска сценариев инициализации не происходит.

При загрузке в многопользовательском режиме, как и в Linux, выполняется 2 этап загрузки. На 3 этапе в BSD задача процесса init аналогична – это вызов и отработка сценариев инициализации, или стартовых скриптов, собранных в каталоге /etc и его подкаталогах и получение терминала (запуск процесса getty), установка его свойств и подготовка к аутентификации и авторизации (ввод логина/пароля) и последующее вытеснение getty процессом login. Отличие BSD в том, что в BSD имеет свои стартовые скрипты, отличные от Linux.

Основной стартовый скрипт -это /erc/rc, настройки  применяемые по умолчанию для скрипта /etc/rc процесс init считывает, из файла /etc/defaults/rc.conf, а настройки, специфичные для текущей системы, из /etc/rc.conf, после чего осуществляется монтирование файловых систем, перечисленных в файле /etc/fstab, запуск сетевых служб, различных системных даемонов и, наконец, выполнение скриптов запуска дополнительно установленных пакаджей. Строки /etc/rc.conf имеют вид параметр=”значение” (точнее service_name_enable=”YES/NO”). Соответственно, service_name_enable – это имя службы, которое должно соответствовать имени скрипта запуска службы из каталога /etc/rc.d/* (/etc/rc.d/service_name_enable, например /etc/rc.d/sshd) или /usr/local/etc/rc.d/* – для ПО установленного из портов, значение yes или no соответственно включает или выключает службу.

Некоторые дополнительные системные сервисы могут быть не учтены в файле /etc/rc.conf. Тогда для их запуска нужно прописать соответствующую команду в /etc/rc.local

Не записывайте свои команды в /etc/rc.conf. Для запуска демонов, или для выполнения вашей команды во время запуска – запишите ваш скрипт в /usr/local/etc/rc.d.

Процесс остановки системы BSD

Во время контролируемого процесса остановки системы через утилиту shutdown программа init будет пытаться запустить скрипт /etc/rc.shutdown, после чего будет посылать всем процессам сигнал TERM, а затем и KILL тем процессам, которые не завершили работу.

Итого, в BSD процесс загрузки до ужоса прост – запуск скрипта /etc/rc согласно настроек /etc/rc.conf, в котором прописано, какие скрипты запуска демонов из /etc/rc.d/* необходимо запустить, а какие – нет и финалом загрузки является скрипт /etc/rc.local.

Маленький итог:

В данной статье я постарался, как можно прозрачней описать процесс запуска операционных систем UNIX, в том виде, в каком я его понял. Некоторые пособия/статьи разделяют загрузку на большее количество этапов. Я постарался изложить так, чтобы прочитав статью, можно было понять процесс загрузки UNIX не вчитываясь в кучу других материалов. Конечно, никто не запрещает углубиться в изучение процесса загрузки ОС, благо в интернете их достаточно. Жду комментариев и дополнений.

Что еще почитать:

Про Upstart:
1. http://help.ubuntu.ru/wiki/upstart
2. http://upstart.ubuntu.com/
Загрузка FreeBSD из официального руководства: http://www.freebsd.org.ru/handbook/boot.html
Перевод /etc/defaults/rc.conf: http://www.lissyara.su/articles/freebsd/tuning/rc.conf/
man init для FreeBSD: http://www.opennet.ru/man.shtml?topic=init&category=8
man rc для FreeBSD:http://www.opennet.ru/man.shtml?topic=rc&category=8
BSD-системы: загрузка и инициализация: http://alv.me/?p=354

upd 2011.01.21: переработано и дополнено описание 3 этапа загрузки
upd 2011.06.10: дополнено описание 1 и 2 этапа загрузки
upd 2011.06.12: переработка и дополнение всех этапов загрузки, добавление процесса загрузки upstart и ОС BSD.
upd 2011.06.27: Дополнение 1 и 2 этапа загрузки.

С Уважением, Mc.Sim!




Теги: , , , , ,

35 комментариев к “Начало! Этапы загрузки ОС Linux (в схеме)”

  1. Hadji
    17 февраля, 2011 at 00:24
    1

    Все подробно объяснил.Спасибо

    • 17 февраля, 2011 at 00:37
      2

      Постарался как можно короче и понятней. Со временем буду более широко раскрывать тему.
      Пожалуйста. Буду рад видеть на блоге снова.

  2. Юрий
    3 июня, 2011 at 02:34
    3

    Браво!!!!!!!!!!!!
    Просто молодец, я перекопал кучу инфы, и по кускам сей процесс отрисовывал, а тут все последовательно в одном месте, и понятно.
    Спасибо огромное.
    Респект.

    • 3 июня, 2011 at 10:36
      4

      Пожалуйста. Приходите еще!

  3. Юрий
    3 июня, 2011 at 14:44
    5

    А по фряхе такого же описания нет?

    • 4 июня, 2011 at 10:25
      6

      К сожалению нет. Если будет когда-то время заняться фряхой – обязательно напишу, но пока другие приоритеты.

    • 12 июня, 2011 at 08:25
      7

      Юрий, прошу – по фряхе добавлен раздел.

  4. Юрий
    4 июня, 2011 at 15:55
    8

    Вот в этой фразе: /etc/inittab (а точнее строке, начинающийся на si::sysinit:/etc/……)
    не совсем пойму., у меня в файле iniitab всего одна строка – уровень загруски и все, а других строк нет, все закоменчено.

    • 4 июня, 2011 at 22:42
      9

      а что за система?

  5. Юрий
    8 июня, 2011 at 23:54
    10

    red hat enterprise 6.1

    • 10 июня, 2011 at 10:08
      11

      Я таки достал этот RHEL 6.1 и установил. В новых версиях RHEL и Ubuntu реализован новый процесс загрузки – upstart, для которого файл /etc/inittab неактуален. Я его (upstart) на днях рассмотрю в текущей статье.

  6. creed.d
    26 апреля, 2012 at 13:21
    12

    Убедил,/etc/init.d/networking—это больше скрипт а не демон.
    Спасибо за статью,впервые вижу такую подробную инструкцию по загрузке ОСЬ.

  7. wooRING
    7 июня, 2012 at 19:19
    13

    Да, признаю. Один из самых лучших блогов про Юних. =) Браво

  8. AlexTorrin
    10 июля, 2012 at 17:54
    14

    Спасибо большое!
    Upstart, насколько я понял, еще Mint использует.

    • 11 июля, 2012 at 23:07
      15

      mint на сколько я помню – это производный от убунту дистрибутив. Так что все же видимо так…

  9. Viktor
    18 декабря, 2012 at 22:59
    16

    Просто и доступно изложено. Красота!

  10. joe
    25 января, 2013 at 23:07
    17
    • 28 января, 2013 at 22:20
      18

      Собственно, этот метод и есть мной описанное решение:

        Переименовать конфигурационный файл запуска службы в каталоге /etc/init в файл без расширения “.conf”.
    • 28 января, 2013 at 22:21
      19

      Собственно, этот метод и есть мной описанное решение:

          Переименовать конфигурационный файл запуска службы в каталоге /etc/init в файл без расширения “.conf”.
      • joe
        3 февраля, 2013 at 02:35
        20

        Но ведь в процессе обновления пакетов на новые версии, установочные скрипты могут по ошибке восстановить файл *.conf? Получается, что служба может запуститься без ведома администратора?

        • 4 февраля, 2013 at 20:43
          21

          Если человек, ответственный за сопровождение пакета не учтет этот момент и файл будет восстановлен, то служба запуститься. А вообще, при установке\обновлении пост\прединсталльные скрипты должны отслеживать такие изменения или запрашивать действие пользователя. К сожалению, пока вплотную с апстартом не сталкивался, поэтому даже не могу сказать как там себя поведет пакетный менеджер… Нужно будет на досуге поэкспериментировать )

  11. joe
    25 января, 2013 at 23:12
    22

    И ещё, мне кажется, что исходя из качества и количества материала, вполне разумной кажется идея создания некоего краткого справочника или руководства в виде pdf или даже твёрдой копии

    • 28 января, 2013 at 22:22
      23

      Ох не знаю…. может это когда-то и произойдет, но явно не в ближайшем будущем. )))

  12. aprogrammer
    28 января, 2014 at 11:34
    24

    Спасибо за статью. Здесь – http://sysadmin.te.ua/linux/linux-boot.html еще добавлено немного подробнее об уровнях выполнения на практике. Может будет кому-то полезным.

  13. Андрей
    23 мая, 2014 at 00:39
    25

    Отличная статья, спасибо.

  14. Виктор
    10 ноября, 2014 at 13:01
    26

    А как понять какая систма иницилизации ОС в работе

    • 18 декабря, 2014 at 14:54
      27

      Посмотрите отсутствует ли в системе файл /etc/inittab и присутствует ли каталог /etc/init/ или /etc/event.d/. Если это так, значит у вас установлен upstart. Если имеется каталог /etc/systemd/system/*, то используется systemd.

  15. Специя
    15 апреля, 2015 at 23:33
    28

    Крутяк!
    А про systemd будет апдейт?

    • 7 июня, 2015 at 22:18
      29

      Возможно, но systemd значительно сложнее, поэтому о сроках говорить трудно.

  16. RedMan
    4 мая, 2015 at 12:26
    30

    Здравствуйте.
    Скажите, не планируете ли вы обновить статью в связи с переходом ряда дистрибутивов на systemd?
    Было бы любопытно узнать, велики ли изменения в плане загрузки.

    • 7 июня, 2015 at 22:41
      31

      Это скорее всего будет отдельная статья, но о сроках говорить не могу…

  17. Ilshat
    16 января, 2016 at 11:09
    32

    Максим, большое спасибо за статьи!!!! *THUMBS UP*

  18. taras
    4 мая, 2016 at 22:20
    33

    Пиши, Mc.Sim, пиши, ты мне заменяешь некоторих моих преподавателей))

    • drotex
      6 февраля, 2017 at 10:36
      34

      Пиши, Mc.Sim, пиши, ты мне заменяешь некоторих моих преподавателей))
      Reply

      … и некоторых мануалов тоже

  19. Evgenia
    11 июня, 2020 at 11:33
    35

    Невероятно! Десять лет статье, а до сих пор – кладезь знаний! Спасибо огромное автору.

Написать комментарий