Сборник статей Handbook inside ! : Linux не для идиотов inside ! : Версия 1 от 15. 07. 2007 2007


Linux не для идиотов: сборник рассказов и рецептов



Pdf просмотр
страница44/50
Дата14.11.2016
Размер5.65 Mb.
Просмотров7987
Скачиваний0
ТипСборник статей
1   ...   40   41   42   43   44   45   46   47   ...   50
Linux не для идиотов: сборник рассказов и рецептов
Автор: Артем Капитула (no-dashi, dalth & viking).
Скачано с http://myfotomx.com/dalth: 15.07.07
Ссылка на оригинал: http://myfotomx.com/dalth/linuxbook.odt
Предисловие
Я много лет работал с Linux, и, общаясь со многими единомышленниками, сделал один странный вывод: нам катастрофически не хватает документации. Причем не инструкций вида «сделайте так и вот так» и не справочных руководств, а некоторого
«мостика» между новичком, который видел только графическую оболочку подобную
GNOME или KDE, и профессионалом, который может скомпилировать необходимый ему драйвер, даже если этот драйвер упорно сопротивляется.
Соответственно, я попытался сделать попытку написать книжку (хотя на книгу этот материал не тянет, скорей на методичку), которая позволила бы сравнительно просто перейти с пользовательского уровня знакомства с Linux на более высокий уровень, не проходя по типичным ошибкам, и за сравнительно короткое время.
Некоторое время я использовал фрагменты этой книги также как часть учебного курса в Челябинском Государственном университете (ЧелГУ) для того, чтобы мои студенты могли ориентироваться в системе несколько лучше, чем на уровне команд ls/ps/exit. Конечно, если честно – как учебное пособие эта маленькая книга непригодна, но, как мне кажется, она неплохо подходит как дополнительная литература.
Если у вас есть пожелания и дополнения – пишите мне почтой на dalt74@gmail.com
, я постараюсь учесть ваши замечания в следующей редакции. Большое спасибо всем тем кто участвовал в рецензировании и помогал советами и замечаниями.
Искренне ваш, Артем Капитула (no-dashi, dalth & viking)
P.S. если вы будете распечатывать или выкладывать это пособие – пожалуйста, указывайте ссылку на автора и оригинальный источник, договорились?
P.P.S. пока что есть следующий to-do list: основы DNS, базовые настройки серверов и рабочих станций под типичные нужды. Ориентировочный срок следующей редакции – через три месяца.
Ядро и модули
Ядро Linux является единственным процессом, имеющим непосредственный доступ к аппаратуре – все остальные процессы обращаются к устройствам только через ядро. В ядре Linux можно выделить несколько важных подсистем: подсистему управления памятью; планировщик задач; подсистему VFS – виртуальную файловую
705
систему и драйверы.
Подсистема управления памятью управляет распределением оперативной памяти между задачами, а также обслуживает файл подкачки, планировщик задач управляет разделением процессорного времени между задачами (процессами и нитями), подсистема VFS предназначена для обслуживания файловых операций.
Драйверы предназначены для управления устройствами и поддержки различных протоколов. Существует две разновидности драйверов – статически подключенные в ядро драйверы и загружаемые модули; первые всегда загружены, вторые могут быть загружены при необходимости и выгружены, когда необходимость в них отпала. Каждый модуль и само ядро содержат сигнатуру версии – специальную метку, которая описывает версию ядра и некоторые опции, использованные при компиляции ядра. Кроме того, ядра версии 2.6 могут поддерживать цифровую подпись модулей. Это было сделано для повышения надежности системы – по умолчанию ядро не будет загружать и использовать драйверы, предназначенные для другой версии ядра, или собранные с другими опциями, поскольку это может привести к возникновению проблем. Версию ядра можно узнать с помощью команды uname:
[dalth@inferno dalth]$ uname -r
2.6.8.1
В принципе, утилиты для работы с модулями поддерживают возможность загрузки модулей, собранных для другого ядра – но пользоваться этой возможностью следует с крайней осторожностью, поскольку это может привести к непредсказуемым последствиями – от ошибок ядра (kernel panic) и вплоть до странных потерь данных и непонятных ошибок, взявшихся на пустом месте.
В большинстве дистрибутивов образ ядра располагается в каталоге /boot, а загружаемые модули ядра располагаются в /lib/modules/<версия_ядра>, там же располагается таблица зависимостей модулей, поскольку некоторые модули могут нуждаться для своей работы в других модулях (например, драйвер поддержки SCSI- дисков нуждается в драйвере поддержки SCSI – как следствие этого, если объекты какого-либо модуля используются другим драйвером, такой модуль невозможно выгрузить). Следующий листинг демонстрирует достаточно типичное содержание каталога модулей для ядер линейки 2.6:
706

[root@viking dev]# ls -l /lib/modules/2.6.8.1/
total 616
lrwxrwxrwx 1 root root 18 Авг 27 15:36 build -> /usr/src/linux drwxr-xr-x 10 root root 4096 Окт 1 13:55 kernel
-rw-r--r-- 1 root root 108680 Окт 1 13:56 modules.alias
-rw-r--r-- 1 root root 69 Окт 1 13:56 modules.ccwmap
-rw-r--r-- 1 root root 153967 Окт 1 13:56 modules.dep
-rw-r--r-- 1 root root 73 Окт 1 13:56 modules.ieee1394map
-rw-r--r-- 1 root root 357 Окт 1 13:56 modules.inputmap
-rw-r--r-- 1 root root 16658 Окт 1 13:56 modules.isapnpmap
-rw-r--r-- 1 root root 85093 Окт 1 13:56 modules.pcimap
-rw-r--r-- 1 root root 68078 Окт 1 13:56 modules.symbols
-rw-r--r-- 1 root root 150781 Окт 1 13:56 modules.usbmap lrwxrwxrwx 1 root root 18 Окт 1 13:22 source -> /usr/src/linux
[root@viking dev]# find /lib/modules/2.6.8.1/kernel -type f | head
-20
/lib/modules/2.6.8.1/kernel/arch/i386/kernel/cpuid.ko
/lib/modules/2.6.8.1/kernel/arch/i386/kernel/microcode.ko
/lib/modules/2.6.8.1/kernel/arch/i386/kernel/msr.ko
/lib/modules/2.6.8.1/kernel/crypto/blowfish.ko
/lib/modules/2.6.8.1/kernel/crypto/deflate.ko
/lib/modules/2.6.8.1/kernel/crypto/md5.ko
/lib/modules/2.6.8.1/kernel/crypto/twofish.ko
/lib/modules/2.6.8.1/kernel/drivers/acpi/fan.ko
/lib/modules/2.6.8.1/kernel/drivers/acpi/processor.ko
/lib/modules/2.6.8.1/kernel/drivers/acpi/thermal.ko
/lib/modules/2.6.8.1/kernel/drivers/base/firmware_class.ko
/lib/modules/2.6.8.1/kernel/drivers/block/cryptoloop.ko
/lib/modules/2.6.8.1/kernel/drivers/block/loop.ko
/lib/modules/2.6.8.1/kernel/drivers/block/nbd.ko
/lib/modules/2.6.8.1/kernel/drivers/block/paride/epat.ko
/lib/modules/2.6.8.1/kernel/drivers/block/paride/paride.ko
/lib/modules/2.6.8.1/kernel/drivers/block/paride/pd.ko
/lib/modules/2.6.8.1/kernel/drivers/block/paride/pg.ko
/lib/modules/2.6.8.1/kernel/drivers/bluetooth/bcm203x.ko
/lib/modules/2.6.8.1/kernel/drivers/bluetooth/bfusb.ko
[root@viking dev]#
Бинарные файлы модулей содержатся в подкаталоге kernel, и имеют расширение
.o” для ядер линейки 2.4 и расширение “.ko” для ядер линейки 2.6. В файлах
modules.***map перечисляются символы (функции и переменные), экспортируемые модулями.
Часто в одном каталоге с модулями содержатся ссылки на каталоги, в которых хранились исходные тексты ядра и в котором производилась сборка ядра (это ссылки source и build, соответственно). Эти ссылки, как правило, используются для того, чтобы скомпилировать модули или программы, которые зависят от версии ядра
(например, эти ссылки используются при инсталляции модуля поддержки видеокарт nvidia).
Учет взаимосвязей между загруженными модулями производится с помощью счетчика ссылок – модуль увеличивает свой счетчик ссылок как только какой-либо его объект начинает использоваться другими драйверами. Когда объекты модуля освобождаются, счетчик ссылок уменьшается. Модуль может быть выгружен, если число ссылок на него станет равно 0. В ядрах версии 2.6 существует возможность произвести принудительную выгрузку модуля даже если он используется, но этим пользоваться без крайней необходимости не рекомендуется, поскольку очень возможно возникновение ошибок.
Ядро содержит множество переменных и функций, которые используются различными драйверами, и соответственно, если какой-либо драйвер должен обратиться к такому объекту, он должен знать его адрес. Некоторые драйверы также содержат переменные и функции, которые должны быть доступны другим
707
драйверам, и адреса таких объектов тоже размещаются в специальной таблице. При загрузке модуля ядро и программа загрузки модулей устанавливает адреса всех объектов, в которых нуждается загружаемый модуль, и только после этого модуль может начать инициализацию.
Загрузка модулей и их выгрузка осуществляются утилитами modprobe, insmod и
rmmod. Программы modinfo и depmod предназначены для получения служебной информации о загружаемых модулях. В процессе своей работы эти утилиты опираются на конфигурационные файлы /etc/modprobe.conf (для ядер 2.6.X) или modules.conf (для ядер 2.4.X).
Загрузка операционной системы
Для компьютеров архитектуры x86 последовательность загрузки хорошо описана в специализированной литературе, но мы все-таки кратко ее повторим. После включения компьютера первым загружается BIOS. Он тестирует аппаратуру и инициализирует устройства. После этого BIOS прочитывает начальный сектор загрузочного жесткого диска (MBR), убеждается что он содержит код первичного загрузчика, и передает управление прочитанному коду. Кроме кода первичного загрузчика, начальный сектор также может содержать таблицу разделов жесткого диска.
В задачи первичного загрузчика входит чтение основного кода загрузчика операционной системы и передача управления ему, после чего основная часть загрузчика может считать конфигурационный файл, загрузить ядро операционной системы, установить параметры для ядра и передать ядру управление. Ядро инициализирует драйверы, проверяет параметры и, опираясь на параметры, пытается смонтировать корневую файловую систему, после чего (если не было проинструктировано об ином) запускает программу /sbin/init. Дальнейшая работа init подробно описана во множестве книг и статей.
В настоящий момент в мире Linux наиболее распространен загрузчик GRUB. Этот загрузчик состоит из нескольких частей – первичного загрузчкиа, собственно основного кода который организует интерфейс пользователя, и набора мини-драверов различных файловых систем, позволяющих прочесть необходимые файлы с файловой системы в момент когда операционная система еще недоступна. Каждая из этих компонент работает на одноим из двух этапов загрузки.
Рассмотрим эти этапы:
Этап 0 – здесь срабатывает первичный загрузчик GRUB. Он компактен и умещается в один блок жесткого диска, что позволяет при желании разместить его в MBR. В задачи кода stage_0 входит прочтение кода необходимого на следующем этапе (собственно кода загрузчика и мини-драйвера файловой системы где расположены основные файлы загрузчика), и передача управления прочитанному коду.
Этап 1 – это на этом этапе первичным загрузчиком в память уже загружен основной код загрузчика, а также мини-драйвер файловой системы, на которой расположены конфигурационные файлы загрузчика, ядро и необходимые драйверы. Основной код, используя функции мини-драйвера, прочитывает конфигурационный файл и организует диалог с пользователем. В зависимости от выбора пользователя, используя мини-драйвер файловой системы, с диска прочитываются файлы ядра и необходимых драйверов, после чего управление передается ядру. Как вариант, пользователь может отказаться от загрузки Linux и инструктировать GRUB прочесть загрузочный сектор некоторого раздела жесткого диска и передать управление ему.
Первичный загрузчик из состава GRUB может быть расположен как в MBR, так и в загрузочном секторе какого-либо раздела жесткого диска – или даже храниться в файле и быть вызван из другого загрузчика (например NTLOADER).
Нередко случаются ситуации, когда корневая файловая система располагается на устройстве, чей драйвер скомпилирован в виде модуля, или драйвер корневой файловой системы скомпилирован в виде модуля. Получается замкнутый круг – чтобы смонтировать корневую файловую систему систему, необходимо прочесть драйвер, а чтобы прочесть драйвер – нужно смонтировать корневую файловую систему. Чтобы разорвать этот порочный круг, в Linux была введен поддержка initrd
– INITial RamDisk.
708

Initial ramdisk – это файл, который прочитывается загрузчиком ОС и загружается в память вместе с ядром. Ядро интерпретирует фрагмент памяти, куда загружен этот файл, как блочное устройство с помощью специального драйвера, статически вкомпилированного в ядро. После инициализации статически скомпилированных драйверов ядро монтирует файловую систему, хранящуюся в initrd и загружает с нее драйверы и запускает программы, необходимые для монтирования корневой файловой системы.
Обычно файл с образом ядра хранится в каталоге /boot и называется vmlinuz-
<версия>, там же располагается файл initrd-<версия>.img, содержащий образ файловой системы initrd. Для каждой версии ядра необходим свой образ initrd, в который включены модули для этой версии ядра. В большинстве случаев образ initrd поставляется в бинарном пакете вместе с ядром, или автоматически создается в процессе построения ядра из исходных текстов в момент выполнения команды make
install, если же возникает ситуация, когда необходимо повторно собрать образ initrd
(например, в сервере сменили SCSI-контроллер), можно воспользоваться специальной командой mkinitrd, позволяющей произвести повторную генерацию initrd:
[root@viking dalth]# mkinitrd /tmp/initrd-2.4.8.1.img 2.6.8.1
[root@viking dalth]# cp /tmp/initrd-2.4.8.1.img /boot
[root@viking dalth]# reboot
Для Linux существует два основных загрузчика – LILO и GRUB. Второй является более поздней разработкой и немного удобней в использовании, а LILO используется по историческим или личным причинам (например, он нравится системному администратору), либо в некоторых случаях, когда требуются специфичные для LILO функции. Для более подробной справки лучше обратиться к справочному руководству (man grub, man lilo).
Из интересных особенностей GRUB и LILO следует отметить то, что и оба этих загрузчика, и ядро оперируют термином корневой файловой системы – но если с точки зрения ядра эта та файловая система, которая содержит программу /sbin/init, то с точки зрения обоих загрузчиков корневой файловой системой является та, которая содержит образ ядра и файл initrd.
Организация памяти
Подсистема виртуальной памяти управляет распределением оперативной памяти между задачами (процессами). Каждая задача считает, что ей выделен непрерывный участок памяти максимального размера, поддерживаемого на соответствующей архитектуре (для архитектуры x86 это 4GB). Из них последний гигабайт резервируется для себя ядром, часть отдается под код программы и разделяемые библиотеки (оба этих фрагмента ядром защищаются), а оставшееся пространство отдается собственно программе под ее данные – но это только то, как видит это все программа.
На самом же деле программа занимает только тот объем памяти, с которым она реально работает. Большинство памяти существует только “на бумаге”, т.е. будет предоставлена программе в тот момент, когда она обратится в эту область. Ядро распределяет память страницами фиксированного размера. Процедура, когда страница оперативной памяти объявляется частью адресного пространства процесса, называется отображением этой страницы в адресное пространство процесса.
Соответственно, ядро отображает реально используемые страницы в виртуальное адресное пространство процесса. Когда процесс обращается к некоторой странице своего адресного пространства, ядро проверяет, имеет ли он право на доступ к этой странице, и если проверка пройдена и доступ получен, то ядро переадресовывает обращение на реальный адрес этой страницы. Если это первое обращение к
709
странице, ядро попытается найти свободную страницу и в случае успеха отобразит ее в адресное пространство соответствующего процесса. Размер страницы фиксирован архитектурой процессора, и для x86 ее размер составляет 4096 байт.
Если случается ситуация, когда свободных страниц больше нет, но существует файл подкачки, ядро может убрать одну из наиболее долго не использовавшихся страниц в файл подкачки, и освободившуюся физическую страницу отдать запросившему память процессу. Если же нет ни незанятого пространства в файле подкачки, ни свободных страниц RAM, то развитие событий может быть следующим: либо запросивший память процесс прерван и “убит” системой, либо какой-то другой из процессов (это определяется специфическими алгоритмами) будет “убит” ядром, и освободившаяся память будет передана запросившему память процессу.
На самом деле большинством действий занимается одна из подсистем процессора, называемая
MMU – Memory Management Unit, и в действительности ядро просто полагается на его работу и вмешивается в нее только для проведения операций пейджинга (подгрузки/выгрузки страниц в
SWAP-файл), или когда возникает ошибка доступа к странице.
Ограничение адресного пространства в 4GB не означает, что система не сможет адресовать более этого объема памяти. На платформе x86 ядро Linux может использовать до 64GB, а ограничение в 4GB накладывается лишь на размер адресного пространства процесса.
System V shared memory
Linux поддерживает стандартную для всех UNIX-подобных операционных систем организацию разделяемой памяти. Пользовательские приложения могут создавать
сегменты разделяемой памяти, которые могут быть присоединены к некоторому фрагменту адресного пространства процесса. Любой процесс, имеющий достаточные права доступа, может присоединиться к сегменту разделяемой памяти, и отобразить его в свое адресное пространство, начиная с некоторого адреса.
Если в приведенной схеме любой из процессов изменит содержимое памяти в области, занимаемой отображением одного из сегментов, то же самое изменение произойдет в адресном пространстве другого процесса, поскольку соответствующий сегмент существует в одном экземпляре и отображен в адресное пространство обоих процессов.
Кроме System V IPC ядро Linux также поддерживает другие объекты IPC, в частности семафоры и очереди сообщений. Каждый объект System V IPC идентифицируется уникальным ключом. Просмотреть список всех объектов IPC можно командой ipcs.
Команда ipcrm позволяет удалять объекты IPC, которые по каким-либо причинам остались не освобожденными после завершения создавшего их процесса – например, такая ситуация может возникнуть после аварийного завершения работы
СУБД Oracle, Informix или DB2.
Соответственно, перед перезапуском процесса системный администратор с помощью команды ipcrm должен освободить неиспользуемые объекты IPC, поскольку стартующее приложение не сможет их повторно создать и не будет корректно работать.
710
Адресное пространство процесса 1
Адресное пространство процесса 2
SHM - cегмент 1
SHM - cегмент 2
Отображение cегмента 1
Отображение cегмента 2
Отображение cегмента 1
Отображение cегмента 2

Для каждого объекта IPC система устанавливает права доступа, как если бы это был файл (т.е. для каждого объекта IPC можно устанавливать набор прав ugo/rwx, но в отличие от обычных файлов сменить права доступа для IPC-объектов можно только вызывая специализированные функции, предназначенные для работы с такими объектами.
[dalth@viking dalth]$ ipcs
------ Shared Memory Segments -------- key shmid owner perms bytes nattch status
0x00000000 0 oracle 640 4194304 10 0x00000000 32769 oracle 640 20971520 10 0x00000000 65538 oracle 640 29360128 10 0x0d3c24a0 98307 oracle 640 29360128 50 0x00000000 13697028 root 777 49152 1 0x00000000 13729797 root 777 16384 1 0x000004d2 13795334 dalth 666 1008 2 0x00000000 14286866 root 644 790528 2 dest
0x00000000 21823507 dalth 600 393216 2 dest
0x00000000 21921814 root 644 122880 2 dest
0x00000000 14516249 root 644 151552 1 dest
------ Semaphore Arrays -------- key semid owner perms nsems
0x0b4f657c 262147 oracle 640 154 0x000004d2 458756 dalth 666 1
------ Message Queues -------- key msqid owner perms used-bytes messages
[dalth@viking dalth]$
Поддержка System V IPC позволяет сравнительно легко переносить на Linux приложения, написанные для других UNIX-систем.
Файловая система
VFS и драйверы файловых систем являются одной из важнейших составляющих ядра. Для того, чтобы получить доступ к файлам, хранящимся на каком-либо устройстве хранения данных, необходимо, чтобы в ядро был загружен драйвер соответствующей файловой системы, и файловая система была смонтирована.
Драйвера всех файловых систем поддерживают набор стандартных функций: открыть файл по имени, записать данные в файл, прочитать данные из файла, закрыть файл, удалить файл и т.д.
Уточним, что драйвера файловых систем не занимаются кэшированием, этим занимается VFS.
При первоначальной загрузке драйвер файловой системы регистрирует в VFS имя файловой системы и те функции, которые предназначены для выполнения стандартных файловых операций. Впоследствии при обращении к файлу на какой- либо файловой системе VFS будет переадресовывать обращение на соответствующую функцию, если таковая была зарегистрирована драйвером.
Посмотреть список обслуживаемых ядром файловых систем можно в файле
/proc/filesystems:
711

[dalth@viking proc]$ cat /proc/filesystems
nodev sysfs nodev rootfs nodev bdev nodev proc nodev sockfs nodev usbfs nodev usbdevfs nodev futexfs nodev tmpfs nodev pipefs nodev eventpollfs nodev devpts ext2
nodev ramfs nodev hugetlbfs iso9660
nodev devfs nodev mqueue ext3
nodev rpc_pipefs nodev nfsd nodev smbfs
Операция монтирования предназначена для того, чтобы сделать доступной файловую систему, расположенную на каком-либо блочном устройстве. Суть операции монтирования заключается в том, что ядро ассоциирует некоторый каталог (называемый точкой монтирования) с блочным устройством и драйвером файловой системы. Для этого оно передает ссылку на блочное устройство драйверу файловой системы, и в случае, если драйвер успешно проидентифицировал эту файловую систему, ядро заносит в специальную таблицу монтирования информацию о том, что все файлы и каталоги, чей полный путь начинается с указанной точки монтирования, обслуживаются соответствующим драйвером файловой системы и расположены на указанном блочном устройстве.
Некоторые файловые системы не нуждаются в блочном устройстве, поскольку хранят свои данные исключительно в памяти, например файловая система procfs, через файлы которой можно получить доступ к различным системным параметрам и таблицам.
Очень часто при монтировании файловой системы системный администратор имеет возможность задать опции монтирования. Опции монтирования – это специальные параметры, которые влияют на работу драйвера файловой системы, когда он работает с файловой системой на соответствующем блочном устройстве – например, с помощью опций монтирования можно управлять режимом кэширования данных, преобразованиями имен файлов и данных, включать и отключать поддержку
ACL и т.д.
Посмотреть таблицу примонтированных файловых систем можно через файл
/proc/mounts:
712

[dalth@viking proc]$ cat /proc/mounts
rootfs / rootfs rw 0 0
/dev/root / ext3 rw 0 0
none /dev devfs rw 0 0
/proc /proc proc rw,nodiratime 0 0
/sys /sys sysfs rw 0 0
none /dev/pts devpts rw 0 0
usbdevfs /proc/bus/usb usbdevfs rw 0 0
/dev/chimera/var /var ext3 rw 0 0
/dev/chimera/temp /tmp ext3 rw 0 0
/dev/chimera/usr /usr ext3 rw 0 0
/dev/chimera/home /home ext3 rw 0 0
/dev/chimera/opt /opt ext3 rw 0 0
none /dev/shm tmpfs rw 0 0
Виртуальная файловая система Linux различает несколько типов файлов: каталоги, обычные файлы, именованные каналы, символьные ссылки, сокеты и специальные файлы. Каждая из этих разновидностей обрабатывается своим собственным образом:

Обычные файлы могут быть прочитаны, записаны, удалены или отображены в память.

Каталог можно представить как список имен файлов, и для этого списка определены операции добавления элемента в список, удаление элемента из списка, переименование элемента списка.

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

Сокеты являются вариантом именованных каналов, но если у именованного канала буфер только один, то есть нельзя определить какой процесс записал данные в канал, то у сокетов может быть несколько клиентов, один из которых осуществляет управление сокетом и может обмениваться данными с любым из остальных клиентов, а те в свою очередь могут обмениваться данными с диспетчером канала – то есть сокет поддерживает множество независимых буферов, по одному на каждую пару сервер+клиент

Специальные файлы предназначены для того, чтобы осуществлять прямой доступ к устройствам. Подробнее о специальных файлах будет говориться в главе
Каталог: pub -> docs books -> Linux -> Linux 2
pub -> Буланов С. В. Кудрявцева Е. Л. Развитие креативности билингвов: путь от интеркультурности к формированию «человека мира»
pub -> «октябрьский лицей»
pub -> Самообследование гоу сош «Школа надомного обучения» №196 по направлениям деятельности. Общие вопросы
pub -> Занятие для математического кружка. Задачи работы
pub -> Доклад муниципальное образовательное
pub -> Публичный доклад. 2013 год Общая характеристика образовательного учреждения. Место расположения
pub -> Публичный доклад муниципального общеобразовательного учреждения средней общеобразовательной школы №13


Поделитесь с Вашими друзьями:
1   ...   40   41   42   43   44   45   46   47   ...   50


База данных защищена авторским правом ©nethash.ru 2019
обратиться к администрации

войти | регистрация
    Главная страница


загрузить материал