Команда загрузка ядра linux

Настройка параметров ядра в Grub

Ядру Linux можно передавать различные параметры для изменения стандартного поведения или информирования о нестандартной конфигурации оборудования. Делать это можно непосредственно перед загрузкой ядра или уже во время выполнения. Во время выполнения параметры ядра можно установить отредактировав файлы в подсистеме /proc или /sys. Перед загрузкой параметры передаются с помощью загрузчика Grub.

Если вы хотите изменить или добавить параметры ядра Linux с помощью Grub вам необходимо отредактировать файл шаблона конфигурации или конфигурационный файл загрузчика. Расположение и название файла шаблона зависит от конкретного дистрибутива.

Настройка параметров ядра Grub в системе

Файл шаблона конфигурации Grub в Debian подобных системах находится по адресу /etc/default/grub. Добавьте параметр ядра в виде имя = значение в переменную GRUB_CMDLINE_LINUX_DEFAULT:

sudo vi /etc/default/grub

Здесь же вы можете удалить нежелательные параметры ядра Grub установленные по умолчанию, например, заменить quiet на verbose для подробного вывода информации о процессе загрузки. Затем выполните следующее чтобы обновить конфигурацию загрузчика:

Если такой команды не существует нужно установить пакет grub2-common:

sudo apt install grub2-common

В Fedora и CentOS скрипта update-grub для обновления конфигурации загрузчика нет, поэтому надо вручную сгенерировать новый конфигурационный файл:

sudo grub2-mkconfig -o /boot/grub2/grub.cfg

Лучше всего добавлять параметры загрузки ядра linux именно этим способом, но давайте рассмотрим ещё несколько альтернативных вариантов

Параметры загрузки ядра в grub.cfg

Если вы не можете воспользоваться способом, описанным выше можно отредактировать непосредственно конфигурационный файл загрузчика который находится по пути /boot/grub/grub.conf. В этом файле нужно найти секцию отвечающую за стандартную загрузку Linux. Обычно, это первая секция menuentry:

Найдите её, а затем в этой секции найдите строчку linux /boot/vmlinuz. в конец этой строки вы и можете дописать свои параметры, в таком же формате. Затем сохраните изменения в файле и перезагрузите компьютер. Ядро загрузится с новыми параметрами. Если вас интересовал вопрос какой командой загружается ядро linux в grub, то это именно команда linux. Синтаксис её такой:

linux параметры_ядра

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

Параметры ядра Grub при загрузке

Загрузчик Grub позволяет также редактировать пункты меню перед запуском системы. Для этого в меню Grub выберите нужный пункт и нажмите клавишу E. Откроется редактор текущей секции меню. Здесь надо, аналогично предыдущему способу найти строчку linux /boot/vmlinuz. и дописать в конец ваши параметры ядра Linux, например, init=/bin/bash:

Затем нажмите кнопку F10 или Ctrl+X чтобы продолжить загрузку с новыми параметрами. Эти параметры будут актуальны до следующей перезагрузки. На этом всё. Надеюсь информация из статьи была для вас полезной.

Источник

6 шагов загрузки Linux на пальцах

Нажмите кнопку включения питания на вашем системнике, и спустя несколько секунд вы увидите окно входа в систему.

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

Я предлагаю вам познакомиться со следующими уровнями типичной загрузки Linux:

1. BIOS

2. MBR

3. GRUB

#boot=/dev/sda
default=0
timeout=5
splashimage=(hd0,0)/boot/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.18-194.el5PAE)
root (hd0,0)
kernel /boot/vmlinuz-2.6.18-194.el5PAE ro root=LABEL=/
initrd /boot/initrd-2.6.18-194.el5PAE.img

4. Ядро или Kernel

5. Init

6. Уровень выполнения программ (Runlevel)

  • Когда Линукс выполняет свою загрузку, вы можете наблюдать загрузку различных служб. К примеру, это могут быть сообщения типа «starting Postfix … OK» (запускается Postfix). Эти службы — и называются программами уровня выполнения, выполняемые из директории, которая соответствует нужному уровню выполнения.
  • Исходя из настроек по умолчанию, система будет выполнять файлы в соответствии с нижеприведенными директориями.
    • Выполнение уровня 0 – /etc/rc.d/rc0.d/
    • Выполнение уровня 1 – /etc/rc.d/rc1.d/
    • Выполнение уровня 2 – /etc/rc.d/rc2.d/
    • Выполнение уровня 3 – /etc/rc.d/rc3.d/
    • Выполнение уровня 4 – /etc/rc.d/rc4.d/
    • Выполнение уровня 5 – /etc/rc.d/rc5.d/
    • Выполнение уровня 6 – /etc/rc.d/rc6.d/
  • Но имейте ввиду, что еще в каталоге /etc могут быть символические ссылки. Например, /etc/rc0.d залинкован на /etc/rc.d/rc0.d.
  • В каталогах /etc/rc.d/rc*.d/ вы можете увидеть список программ, имя которых начинается из букв S и K.
  • Программы, начинающиеся на S используются для запуска. S, потому что startup.
  • Программы, которые начинаются с литеры K используются — правильно — для завершения работы. K, потому что kill.
  • Еще есть номера рядом с буквами S и K в именах программ. Эти номера используются для определения порядка запуска этих программ.
  • К примеру, S12syslog предназначен для запуска демона syslog, его порядковый номер 12. S80sendmail — для запуска демона sendmail, имеющего порядковый номер 80. Таким образом, программа syslog будет запущена перед sendmail.

Вот и все. Возможно, некоторым из вас это не ново и особого интереса не было при чтении статью, поскольку она более ориентирована на начально-средний уровень знакомства з Линуксом.
В таком случае могу лишь сказать, что «повторение — мать учения» (с).

Дополнения, исправления, уточнения

В комментариях неоднократно было апеллировано к тексту статьи, поэтому, думаю, стоит учесть некоторые важные комментарии хабрасообщества. (спасибо artemlight, 3al, Tishka17, HhyperH, Next_Alex, Ilya_Shmelykh, Aux, soomrack, Xpeh )

  • artemlight:: «Ну скажем прямо — так грузятся далеко не все дистры». С ним согласилось большинство, отмечая и bsd-style init, u-boot, и хоть initrd в статье пропущен, стоить заметить, что он нужен ядру не во всех дистрибутивах. Также отмечено, что в slackware поддержка rc.d осуществляется только в качестве совместимости, а встраиваемые системы грузятся иначе. На декстопах иногда бывает EFI, а кроме того Linux популярен в мире embedded и там ещё куча разных платформ. Линукс в телефоне вообще иначе грузится.
  • soomrack, ссылая на википедию: Еще хочется сделать замечание по поводу MBR, первого сектора и пр. Все несколько усложнилось за последние годы. Сейчас уместней говорить о EFI.

Источник

Процесс загрузки ядра. Часть 1.

От загрузчика к ядру

От автора:

Если вы читали предыдущие статьи моего блога, то могли заметить, что некоторое время назад я начал увлекаться низкоуровневым программированием. Я написал несколько статей о программировании под x86_64 в Linux. В то же время я начал «погружаться» в исходный код Linux. Я испытываю большой интерес к пониманию того, как работают низкоуровневые вещи, как запускаются программы на моем компьютере, как они расположены в памяти, как ядро управляет процессами и памятью, как работает сетевой стек на низком уровне и многие другие вещи. Поэтому я решил написать ещё одну серию статей о ядре Linux для x86_64.

Замечу, что я не профессиональный хакер ядра и не пишу под него код на работе. Это просто хобби. Мне просто нравятся низкоуровневые вещи и мне интересно наблюдать за тем, как они работают. Так что, если вас что-то будет смущать или у вас появятся вопросы или замечания, пишите мне в твиттер 0xAX, присылайте письма на email или просто создавайте issue. Я ценю это.

Все статьи также будут доступны в репозитории Github, и, если вы обнаружите какую-нибудь ошибку в содержимом статьи, не стесняйтесь присылать pull request.

Заметьте, что это не официальная документация, а просто материал для обучения и обмена знаниями.

Требуемые знания

  • Понимание кода на языке C
  • Понимание кода на языке ассемблера (AT&T синтаксис)

В любом случае, если вы только начинаете изучать подобные инструменты, я постараюсь объяснить некоторые детали в этой и последующих частях. Ладно, простое введение закончилось, и теперь можно начать «погружение» в ядро и низкоуровневые вещи.

Я начал писать эту книгу, когда актуальной версией ядра Linux была 3.18, и с этого момента многое могло измениться. При возникновении изменений я буду соответствующим образом обновлять статьи.

Магическая кнопка включения, что происходит дальше?

Несмотря на то, что это серия статей о ядре Linux, мы не будем начинать непосредственно с его исходного кода (по крайней мере в этом параграфе). Как только вы нажмёте магическую кнопку включения на вашем ноутбуке или настольном компьютере, он начинает работать. Материнская плата посылает сигнал к источнику питания. После получения сигнала, источник питания обеспечивает компьютер надлежащим количеством электричества. После того как материнская плата получает сигнал «питание в норме» (Power good signal), она пытается запустить CPU. CPU сбрасывает все остаточные данные в регистрах и записывает предустановленные значения каждого из них.

CPU серии Intel 80386 и старше после перезапуска компьютера заполняют регистры следующими предустановленными значениями:

Процессор начинает свою работу в режиме реальных адресов. Давайте немного задержимся и попытаемся понять сегментацию памяти в этом режиме. Режим реальных адресов поддерживается всеми x86-совместимыми процессорами, от 8086 до самых новых 64-битных CPU Intel. Процессор 8086 имел 20-битную шину адреса, т.е. он мог работать с адресным пространством в диапазоне 0-0xFFFFF (1 мегабайт). Но регистры у него были только 16-битные, а в таком случае максимальный размер адресуемой памяти составляет 2^16 — 1 или 0xffff (64 килобайта).

Сегментация памяти используется для того, чтобы задействовать всё доступное адресное пространство. Вся память делится на небольшие, фиксированного размера сегменты по 65536 байт (64 Кб) каждый. Поскольку мы не можем адресовать память свыше 64 Кб с помощью 16-битных регистров, был придуман альтернативный метод.

Адрес состоит из двух частей: селектора сегмента, который содержит базовый адрес, и смещение от этого базового адреса. В режиме реальных адресов базовый адрес селектора сегмента это Селектор Сегмента * 16 . Таким образом, чтобы получить физический адрес в памяти, нужно умножить селектор сегмента на 16 и прибавить к нему смещение:

Например, если CS:IP содержит 0x2000:0x0010 , то соответствующий физический адрес будет:

Но если мы возьмём максимально доступный селектор сегментов и смещение: 0xffff:0xffff , то итоговый адрес будет:

что больше первого мегабайта на 65520 байт. Поскольку в режиме реальных адресов доступен только один мегабайт, с отключённой адресной линией A20 0x10ffef становится 0x00ffef .

Хорошо, теперь мы немного знаем о режиме реальных адресов и адресации памяти в этом режиме. Давайте вернёмся к обсуждению значений регистров после сброса.

Регистр CS состоит из двух частей: видимый селектор сегмента и скрытый базовый адрес. В то время как базовый адрес, как правило, формируется путём умножения значения селектора сегмента на 16, во время аппаратного перезапуска в селектор сегмента в регистре CS записывается 0xf000 , а в базовый адрес — 0xffff0000 . Процессор использует этот специальный базовый адрес, пока регистр CS не изменится.

Начальный адрес формируется путём добавления базового адреса к значению в регистре EIP :

Мы получили 0xfffffff0 , т.е. 16 байт ниже 4 Гб. По этому адресу располагается так называемый вектор прерываний. Это область памяти, в которой CPU ожидает найти первую инструкцию для выполнения после сброса. Она содержит инструкцию jump ( jmp ), которая обычно указывает на точку входа в BIOS. Например, если мы взглянем на исходный код coreboot ( src/cpu/x86/16bit/reset16.inc ), то увидим следующее:

Здесь мы можем видеть опкод инструкции jmp — 0xe9 , и его адрес назначения _start16bit — ( . + 2) .

Мы также можем видеть, что секция reset занимает 16 байт и начинается с 0xfffffff0 ( src/cpu/x86/16bit/reset16.ld ):

Теперь запускается BIOS; после инициализации и проверки оборудования, BIOS нужно найти загрузочное устройство. Порядок загрузки хранится в конфигурации BIOS, которая определяет, с каких устройств BIOS пытается загрузиться. При попытке загрузиться с жёсткого диска, BIOS пытается найти загрузочный сектор. На размеченных жёстких дисках со схемой разделов MBR, загрузочный сектор расположен в первых 446 байтах первого сектора, размер которого 512 байт. Последние два байта первого сектора — 0x55 и 0xaa , которые оповещают BIOS о том, что устройство является загрузочным.

Собрать и запустить этот код можно таким образом:

Команда оповещает эмулятор QEMU о необходимости использовать в качестве образа диска созданный только что бинарный файл. Пока последний проверяет, удовлетворяет ли загрузочный сектор всем необходимым требованиям (в origin записывается 0x7c00 , а в конце магическая последовательность), QEMU будет работать с бинарным файлом как с главной загрузочной записью (MBR) образа диска.

В этом примере мы можем видеть, что код будет выполнен в 16-битном режиме реальных адресов и начнёт выполнение с адреса 0x7c00 . После запуска он вызывает прерывание 0x10, которое просто печатает символ ! . Оставшиеся 510 байт заполняются нулями, и код заканчивается двумя магическими байтами 0xaa и 0x55 .

Вы можете увидеть бинарный дамп с помощью утилиты objdump :

Реальный загрузочный сектор имеет код для продолжения процесса загрузки и таблицу разделов вместо кучи нулей и восклицательного знака 🙂 С этого момента BIOS передаёт управление загрузчику.

ЗАМЕЧАНИЕ: Как уже было упомянуто ранее, CPU находится в режиме реальных адресов; в этом режиме вычисление физического адреса в памяти выполняется следующим образом:

так же, как было упомянуто выше. У нас есть только 16-битные регистры общего назначения; максимальное значение 16-битного регистра — 0xffff . Поэтому, если мы возьмём максимальное возможное значение, то результат будет следующий:

где 0x10ffef равен 1 Мб + 64 Кб — 16 байт . Процессор 8086 (который был первым процессором с режимом реальных адресов), в отличии от этого, имеет 20-битную шину адресации. Поскольку 2^20 = 1048576 это 1 Мб, получается, что фактический объём доступной памяти составляет 1 Мб.

В целом, карта разделов в режиме реальных адресов выглядит следующим образом:

В начале статьи я написал, что первая инструкция, выполняемая CPU, расположена по адресу 0xFFFFFFF0 , значение которого намного больше, чем 0xFFFFF (1 Мб). Каким образом CPU получает доступ к этому участку в режиме реальных адресов? Ответ на этот вопрос находится в документации coreboot:

В начале выполнения BIOS находится не в RAM, а в ROM.

Загрузчик

Существует несколько загрузчиков Linux, такие как GRUB 2 и syslinux. Ядро Linux имеет протокол загрузки, который определяет требования к загрузчику для реализации поддержки Linux. В этом примере будет описан GRUB 2.

Теперь, когда BIOS выбрал загрузочное устройство и передал контроль управления коду в загрузочном секторе, начинается выполнение boot.img. Этот код очень простой в связи с ограниченным количеством свободного пространства и содержит указатель, который используется для перехода к основному образу GRUB 2. Основной образ начинается с diskboot.img, который обычно располагается сразу после первого сектора в неиспользуемой области перед первым разделом. Приведённый выше код загружает оставшуюся часть основного образа, который содержит ядро и драйверы GRUB 2 для управления файловой системой. После загрузки остальной части основного образа, код выполняет функция grub_main.

Функция grub_main инициализирует консоль, получает базовый адрес для модулей, устанавливает корневое устройство, загружает/обрабатывает файл настроек grub, загружает модули и т.д. В конце выполнения, grub_main переводит grub обратно в нормальный режим. Функция grub_normal_execute (из grub-core/normal/main.c ) завершает последние приготовления и отображает меню выбора операционной системы. Когда мы выбираем один из пунктов меню, запускается функция grub_menu_execute_entry , которая в свою очередь запускает команду grub boot , загружающую выбранную операционную систему.

Из протокола загрузки видно, что загрузчик должен читать и заполнять некоторые поля в заголовке ядра, который начинается со смещения 0x01f1 в коде настроек. Вы можете посмотреть загрузочный скрипт компоновщика, чтобы убедиться в этом. Заголовок ядра arch/x86/boot/header.S начинается с:

Загрузчик должен заполнить этот и другие заголовки (которые помеченные как тип write в протоколе загрузки Linux, например, в данном примере) значениями, которые он получил из командной строки или значениями, вычисленными во время загрузки. (Мы не будет вдаваться в подробности и описывать все поля заголовка ядра, но мы сделаем это, когда будем обсуждать как их использует ядро; тем не менее вы можете найти полное описание всех полей в протоколе загрузки.)

Как мы видим из протокола, после загрузки ядра карта распределения памяти будет выглядеть следующим образом:

Итак, когда загрузчик передаёт управление ядру, он запускается с:

где X — это адрес загруженного сектора загрузки ядра. В моем случае X это 0x10000 , как мы можем видеть в дампе памяти:

Сейчас загрузчик поместил ядро Linux в память, заполнил поля заголовка, а затем переключился на него. Теперь мы можем перейти непосредственно к коду настройки ядра.

Начальный этап настройки ядра

Наконец, мы находимся в ядре! Технически, ядро ещё не работает; во-первых, часть ядра, отвественная за настройку, должна подготовить декомпрессор, вещи связанные с управлением памятью и т.д. После всех подготовок код настройки ядра должен распаковывать фактическое ядро и совершить переход на него. Выполнение настройки начинается в файле arch/x86/boot/header.S, начиная с метки _start. Это немного странно на первый взгляд, так как перед этим есть ещё несколько инструкций.

Давным-давно у Linux был свой загрузчик, но сейчас, если вы запустите, например:

На самом деле, header.S начинается с MZ (см. картинку выше), вывода сообщения об ошибке и PE заголовка:

Это нужно, чтобы загрузить операционную систему с поддержкой UEFI. Мы не будем рассматривать его внутреннюю работу прямо сейчас; мы сделаем это в одной из следующих глав.

Настоящая настройка ядра начинается с:

Загрузчик (grub2 или другой) знает об этой метке (при смещении 0x200 от MZ ) и сразу переходит на неё, несмотря на то, что header.S начинается с секции .bstext , который выводит сообщение об ошибке:

Точка входа настройки ядра:

Здесь мы можем видеть опкод инструкции jmp ( 0xeb ) к метке start_of_setup-1f . Нотация Nf означает, что 2f ссылается на следующую локальную метку 2: ; в нашем случае это метка 1 , которая расположена сразу после инструкции jump и содержит оставшуюся часть заголовка. Сразу после заголовка настроек мы видим секцию .entrytext , которая начинается с метки start_of_setup .

Это первый код, который на самом деле запускается (отдельно от предыдущей инструкции jump, конечно). После того как настройщик ядра получил управление от загрузчика, первая инструкция jmp располагается по смещению 0x200 от начала реальных адресов, т.е после первых 512 байт. Об этом можно узнать из протокола загрузки ядра Linux, а также увидеть в исходном коде grub2:

В моём случае, ядро загружается по физическому адресу 0x10000 . Это означает, что после начала настройки ядра регистры сегмента будут иметь следующие значения:

После перехода на метку start_of_setup , необходимо соблюсти следующие условия:

  • Убедиться, что все значения всех сегментных регистров равны
  • Правильно настроить стек, если это необходимо
  • Настроить BSS
  • Перейти к C-коду в main.c

Давайте посмотрим, как эти условия выполняются.

Выравнивание сегментных регистров

Прежде всего, ядро гарантирует, что сегментные регистры ds и es указывают на один и тот же адрес. Затем оно сбрасывает флаг направления с помощью инструкции cld :

Как я уже писал ранее, grub2 загружает код настройки ядра по адресу 0x1000 (адрес по умолчанию) и cs по адресу 0x1020 , потому что выполнение не начинается с начала файла, а с метки _start :

расположеной в 512 байтах от 4d 5a. Нам также необходимо выровнять cs с 0x1020 на 0x1000 и остальные сегментные регистры. После этого мы настраиваем стек:

кладём значение ds в стек по адресу метки 6 и выполняем инструкцию lretw . Когда мы вызываем lretw , она загружает адрес метки 6 в регистр счётчика команд (IP), и загружает cs со значением ds . После этого ds и cs будут иметь одинаковые значения.

Настройка стека

Почти весь код настройки — это подготовка для среды языка C в режиме реальных адресов. Следующим шагом является проверка значения регистра ss и создание корректного стека, если значение ss неверно:

Это может привести к трём различны сценариям:

  • ss имеет верное значение 0x1000 (как и все остальные сегментные регистры рядом с cs )
  • ss является некорректным и установлен флаг CAN_USE_HEAP (см. ниже)
  • ss является некорректным и флаг CAN_USE_HEAP не установлен (см. ниже)

Давайте рассмотрим все три сценария:

  • ss имеет верный адрес ( 0x1000 ). В этом случае мы переходим на метку 2:

Здесь мы видим выравнивание сегмента dx (содержащего значение sp , полученное загрузчиком) до 4 байт и проверку — является ли полученное значение нулём. Если ноль, то помещаем 0xfffx (выровненный до 4 байт адрес до максимального значения сегмента в 64 Кб) в dx . Если не ноль, продолжаем использовать sp , полученный от загрузчика (в моём случае 0xf7f4 ). После этого мы помещаем значение ax в ss , который хранит корректный адрес сегмента 0x1000 и устанавливает корректное значение sp . Теперь мы имеем корректный стек:

  • Второй сценарий (когда ss != ds ). Во-первых, помещаем значение _end (адрес окончания кода настройки) в dx и проверяем поле заголовка loadflags инструкцией testb , чтобы понять, можем ли мы использовать кучу (heap). loadflags является заголовком с битовой маской, который определён как:

и, как мы можем узнать из протокола загрузки:

Если бит CAN_USE_HEAP установлен, мы помещаем heap_end_ptr в dx (который указывает на _end ) и добавляем к нему STACK_SIZE (минимальный размер стека, 512 байт). После этого, если dx без переноса (будет без переноса, поскольку dx = _end + 512 ), переходим на метку 2 (как в предыдущем случае) и создаём корректный стек.

  • Если флаг CAN_USE_HEAP не установлен, мы просто используем минимальный стек от _end до _end + STACK_SIZE :

Настройка BSS

Последние два шага, которые нужно выполнить перед тем, как мы сможем перейти к основному коду на C, это настройка BSS и проверка «магических» чисел. Сначала проверка чисел:

Это простое сравнение setup_sig с магическим числом 0x5a5aaa55 . Если они не равны, сообщается о фатальной ошибке.

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

Секция BSS используется для хранения статически выделенных, неинициализированных данных. Linux тщательно обнуляет эту область памяти, используя следующий код:

Во-первых, адрес __bss_start помещается в di . Далее, адрес _end + 3 (+3 — выравнивает до 4 байт) помещается в cx . Регистр eax очищается (с помощью инструкции xor ), а размер секции BSS ( cx — di ) вычисляется и помещается в cx . Затем cx делится на 4 (размер ‘слова’ (англ. word)), а инструкция stosl используется повторно, сохраняя значение eax (ноль) в адрес, на который указывает di , автоматически увеличивая di на 4 (это продолжается до тех пор, пока cx не достигнет нуля). Эффект от этого кода в том, что теперь все ‘слова’ в памяти от __bss_start до _end заполнены нулями:

Переход к основному коду

Вот и все, теперь у нас есть стек и BSS, поэтому мы можем перейти к C-функции main() :

Функция main() находится в файле arch/x86/boot/main.c. О том, что она делает, вы сможете узнать в следующей части.

Заключение

Это конец первой части о внутренностях ядра Linux. В следующей части мы увидим первый код на языке C, который выполняется при настройке ядра Linux, реализацию процедур для работы с памятью, таких как memset , memcpy , earlyprintk , инициализацию консоли и многое другое.

От переводчика: пожалуйста, имейте в виду, что английский — не мой родной язык, и я очень извиняюсь за возможные неудобства. Если вы найдёте какие-либо ошибки или неточности в переводе, пожалуйста, пришлите pull request в linux-insides-ru.

Источник

Читайте также:  Не работает запуск от имени администратора windows
Оцените статью