Что такое системный вызов
Перейти к содержимому

Что такое системный вызов

  • автор:

Один, системный вызов

На следующем рисунке мы можем увидеть отношения между пользователями и операционными системами:

(1) Что такое системный вызов

Системный вызов — это своего рода выход из ядра, это набор «специальных» интерфейсов, которые операционная система предоставляет для вызова пользовательским программам. Логически системный вызов можно рассматривать как интерфейс между ядром и программой пользовательского пространства.

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

(В режим ядра 0x80)

(2) Системные вызовы и API

Многие люди неправильно понимают системные вызовы и API. Мы знаем, что такое системные вызовы из приведенного выше описания, так что же такое API?

API(Application Programming Interface): Интерфейс прикладной программы — это некоторые предопределенные функции, которые не обязательно связаны с ядром. Он предоставляет приложениям и разработчикам возможность доступа к набору процедур, основанных на определенном программном или аппаратном обеспечении, без необходимости доступа к исходному коду или понимания конкретных деталей реализации.

Linux API соответствует стандарту POSIX. Когда наши прикладные программы программируются, они программируются через интерфейс прикладного программирования (API), предоставляемый операционной системой, а не напрямую через системные вызовы. Иногда формы API и системного вызова совпадают.

На рисунке ниже мы видим взаимосвязь между системными вызовами и API. Все посередине — это API-интерфейсы, а состояние ядра — это все системные вызовы.Мы можем обнаружить, что некоторые API-интерфейсы не вызывают системные вызовы, а некоторые API-интерфейсы вызывают системные вызовы.

различия: Api — это определение функции, которое определяет функцию этой функции и не имеет прямого отношения к ядру. Системный вызов заключается в отправке запроса ядру через прерывания для реализации определенных служб, предоставляемых ядром.

контакт: API может потребовать один или несколько системных вызовов для выполнения определенных функций. С точки зрения непрофессионала, если этот api должен иметь дело с ядром, ему нужен системный вызов, в противном случае он не нужен.
Программист вызывает API (функцию API), а затем выполняет функцию функции вместе с системным вызовом.

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

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

Не все функции API соответствуют системному вызову одна за другой. Иногда функции API требуется несколько системных вызовов для совместного выполнения функции функции, и даже некоторым функциям API не требуется вызывать соответствующий системный вызов (поэтому он завершает Не является службой ядра)

(3) Системные вызовы и системные команды

Какая связь между системными командами и системными вызовами?

Системная команда представляет собой более высокий уровень, чем интерфейс прикладного программирования. Каждый системный вызов может рассматриваться как исполняемая программа, например ls, hostname и другие команды, которые эквивалентны инкапсуляции и вызову системного вызова. Мы можем просмотреть его с помощью команды strace Системный вызов, вызываемый определенной командой, ниже следует системный вызов, вызываемый командой ls (он очень длинный, поэтому список не завершен):
О команде strace мы можем прочитать в следующей статье:http://man.linuxde.net/strace

(3) Системные вызовы и функции ядра

Давайте сначала посмотрим, что такое функция ядра.

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

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

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

Например, следующие два примера:
Соответствующей служебной программой для системного вызова getpid в ядре является sys_getpid (), а конкретная реализация sys_getpid () в ядре:

Поскольку эта программа работает в пользовательском режиме, нам нужно добавить файл заголовка <syscall.h>
, где SYS_getpid — это функция ядра, а все остальные вызовы — это библиотечные функции.

(4) Как реализован системный вызов

В системном вызове это в основном реализуется двумя вещами: одна — это номер системного вызова, а другая — таблица системных вызовов.

Что касается системных вызовов, мы можем прочитать следующие две статьи, эти две статьи подробно объясняют, как реализованы системные вызовы:
https://blog.csdn.net/zxxssdsd/article/details/17025747
https://blog.csdn.net/chosen0ne/article/details/7721550

(5) Как вызвать системный вызов

Два, библиотечные функции

Ниже приводится краткое введение в библиотечные функции:
Библиотечная функция (библиотечная функция) — это способ помещать функции в библиотеку для использования другими. Метод состоит в том, чтобы скомпилировать некоторые часто используемые функции и поместить их в файл для вызова разными людьми. Используйте #include <>, чтобы добавить имя файла, в котором он находится при его вызове. Обычно кладут в файл lib.

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

Три, думая

(1) В чем разница между библиотечными функциями и системными вызовами?

Библиотека представляет собой многоразовый модуль в пользовательском режиме.
Системный вызов — это служба, предоставляемая os. Он находится в состоянии ядра и не может быть вызван напрямую. Вместо этого вам нужно использовать мягкое прерывание, такое как int 0x80, чтобы попасть в ядро. Таким образом, большая часть функций библиотеки инкапсулирует системные вызовы.

Интеллектуальная рекомендация

Реализация JavaScript Hashtable

причина Недавно я смотрю на «Структуру данных и алгоритм — JavaScript», затем перейдите в NPMJS.ORG для поиска, я хочу найти подходящую ссылку на библиотеку и записывать его, я могу исполь.

MySQL общие операции

jdbc Транзакция: транзакция, truncate SQL заявление Transaction 100 000 хранимая процедура mysql msyql> -определить новый терминатор,Пробелов нет mysql>delimiter // mysql> -создание хранимой .

Используйте Ansible для установки и развертывания TiDB

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

Последняя версия в 2019 году: использование nvm под Windows для переключения между несколькими версиями Node.js.

С использованием различных интерфейсных сред вы можете переключаться между разными версиями в любое время для разработки. Например, развитие 2018 года основано наNode.js 7x версия разработана. Тебе эт.

Шаблон проектирования — Создать тип — Заводской шаблон

Заводская модель фабрикиPattern Решать проблему: Решен вопрос, какой интерфейс использовать принципСоздайте интерфейс объекта, класс фабрики которого реализуется его подклассом, чтобы процесс создания.

10) Системный вызов в ОС

Что такое системный вызов в операционной системе?

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

Системный вызов предлагает услуги операционной системы пользовательским программам через API (интерфейс прикладного программирования). Системные вызовы являются единственными точками входа в систему ядра.

Из этого руководства по операционной системе вы узнаете:

Пример системного вызова

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

В интерактивной системе этот тип выполнения программы требует некоторых системных вызовов ОС.

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

Как работает системный вызов?

Вот шаги для системного вызова:

Как вы можете видеть на приведенной выше диаграмме.

Шаг 1) Процессы, выполняемые в пользовательском режиме до тех пор, пока системный вызов не прервет его.

Шаг 2) После этого системный вызов выполняется в режиме ядра в приоритетном порядке.

Шаг 3) По завершении выполнения системного вызова управление возвращается в режим пользователя.,

Шаг 4) Выполнение пользовательских процессов возобновляется в режиме ядра.

Зачем вам системные вызовы в ОС?

Ниже приведены ситуации, которые требуют системных вызовов в ОС:

  • Чтение и запись из файлов требуют системных вызовов.
  • Если файловая система хочет создать или удалить файлы, требуются системные вызовы.
  • Системные вызовы используются для создания и управления новыми процессами.
  • Сетевые подключения требуют системных вызовов для отправки и получения пакетов.
  • Доступ к аппаратным устройствам, таким как сканер, принтер, требуется системный вызов.

Типы системных вызовов

Вот пять типов системных вызовов, используемых в ОС:

  • Контроль процесса
  • Управление файлами
  • Управление устройством
  • Информационное обслуживание
  • связи

Контроль процесса

Эти системные вызовы выполняют задачу создания процесса, завершения процесса и т. Д.

  • Конец и Прервать
  • Загрузить и выполнить
  • Создать процесс и завершить процесс
  • Ожидание и подписанное событие
  • Выделить и освободить память

Управление файлами

Системные вызовы управления файлами обрабатывают задания по обработке файлов, такие как создание файла, чтение, запись и т. Д.

  • Создать файл
  • Удалить файл
  • Открыть и закрыть файл
  • Читать, писать и перемещать
  • Получить и установить атрибуты файла

Управление устройством

Управление устройствами выполняет работу с устройствами, такими как чтение из буферов устройств, запись в буферы устройств и т. Д.

  • Запрос и релиз устройства
  • Логически подключать / отключать устройства
  • Получить и установить атрибуты устройства

Информационное обслуживание

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

  • Получить или установить время и дату
  • Получить атрибуты процесса и устройства

Связь:

Эти типы системных вызовов специально используются для межпроцессного взаимодействия.

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

Правила передачи параметров для системного вызова

Вот общие общие правила для передачи параметров в системный вызов:

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

Важные системные вызовы, используемые в ОС

Подождите()

В некоторых системах процесс должен ждать, пока другой процесс завершит свое выполнение. Этот тип ситуации возникает, когда родительский процесс создает дочерний процесс, и выполнение родительского процесса остается приостановленным, пока не выполнится его дочерний процесс.

Приостановка родительского процесса автоматически происходит с помощью системного вызова wait (). Когда дочерний процесс завершает выполнение, элемент управления возвращается к родительскому процессу.

вилка ()

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

Этот системный вызов выполняется, когда исполняемый файл в контексте уже запущенного процесса заменяет старый исполняемый файл. Однако исходный идентификатор процесса остается, поскольку новый процесс не создается, но стек, данные, заголовок, данные и т. Д. Заменяются новым процессом.

убийство():

Системный вызов kill () используется ОС для отправки сигнала завершения процессу, который побуждает процесс завершиться. Однако системный вызов kill не обязательно означает уничтожение процесса и может иметь различные значения.

Выход():

Системный вызов exit () используется для прекращения выполнения программы. Этот вызов, особенно в многопоточной среде, определяет, что выполнение потока завершено. ОС восстанавливает ресурсы, которые использовались процессом после использования системного вызова exit ().

Unix2019b/Системные вызовы

При программировании на C мы используем средства стандартной библиотеки языка, такие как тип FILE*, функции fopen(), fread(), fwrite(), fclose(). Эти функции кроссплатформенные и основываются внутри на более низкоуровневых функциях.

Например, C-функция fread и fwrite:

На POSIX-совместимых системах (в том числе Linux) она сводится к функции

На Windows она сводится к функциям из WinAPI:

Если использовать только функции из библиотеки C, то можно писать переносимые программы, которые будут работать под разными ОС. Но если использовать API операционной системы, то можно получить больше возможностей и несколько лучшую производительность за счёт меньшего числа копирований данных в промежуточные буферы.

Системные вызовы

Как же устроены те самые POSIX-функции read() и write()?

Они реализуются посредством системных вызовов. Системный вызов (англ. system call) — обращение прикладной программы к ядру операционной системы для выполнения какой-либо операции.

AppInVat.png

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

Syscall.png

Обычное пользовательское приложение работает в непривилегированном режиме в своём виртуальном адресном пространстве. В обычном случае для выполнения вычислительной работы, для доступа к памяти и пр. не требуются системные вызовы. Например, функции библиотеки C, такие как strlen() и memcpy(), не имеют ничего общего с ядром и всегда выполняются целиком в приложении. Однако такие функции, как malloc() и printf(), могут делать внутри системные вызовы.

Набор системных вызовов разный в разных операционных системах. Итого в ядре Linux около 310 системных вызовов. С ними можно познакомиться в таблице. Для сравнения, в ОС Windows системных вызовов около 460.

GNU C Library

Когда мы компилируем обычную программу на C под Linux, она автоматически линкуется с библиотекой glibc.

Библиотека GNU C Library (часто используется название glibc) — это вариант реализации стандартной библиотеки С от проекта GNU. Является одним из основных компонентов операционной системы GNU/Linux.

Реализует как стандартные C-функции типа malloc(), strcpy(), fopen() (они являются частью стандарта языка программирования C и доступны на всех платформах), так и POSIX-функции типа getpid(), open() (эти функции не входят в стандарт C и, как правило, скажем, под Windows не реализованы).

Библиотека GNU C Library предоставляет программисту удобный интерфейс для работы с ОС в виде интерфейсных функций. Многие функции в libc являются тонкими обёртками над системными вызовами. Однако не каждая POSIX-функция является системным вызовом. Так и наоборот, не для каждого системного вызова есть соответствующая C-функция.

Размещение

Библиотека libc является одним файлом (динамическая so и статическая a), размещается в каталоге /usr/lib. Кроме того, в состав glibc (GNU libc) входят ещё несколько библиотек:

  • libm — математическая библиотека (там реализованы функции вида sin(), cos(). )
  • libpthread — POSIX Threads — библиотека для работы с потоками (мы обратимся к ней на следующих занятиях)

Всего функций в glibc много, мы рассмотрим только несколько.

Пример: fwrite

Вот шаги, которые включает в себя вызов C-функции fwrite:

Image002.png

  1. fwrite вместе с остальной частью стандартной библиотеки C реализован в glibc.
  2. fwrite вызывает более низкоуровневую функцию write.
  3. write загружает идентификатор системного вызова (который равен 1 для write) и аргументы в регистры процессора, а затем заставит процессор переключиться на уровень ядра. То, как это делается, зависит от архитектуры процессора, а иногда и от модели процессора. Например, процессоры x86 обычно вызывают прерывание 80, а процессоры x86-64 используют инструкцию процессора syscall.
  4. Процессор, который теперь работает в режиме ядра, передает идентификатор системного вызова в таблицу системных вызовов, извлекает указатель функции со смещением 1 и вызывает функцию. Эта функция, sys_write, является реализацией записи в файл.

Как работают системные вызовы

Из пользовательского пространства (ring 3) нельзя просто так вызвать функцию из ядра (ring 0), как обычную функцию. На шаге №3 в предыдущем примере используется тот или иной механизм перехода в режим ядра в зависимости от архитектуры компьютера. На компьютерах самой популярной архитектуры x86 для системный вызов делается тем или иным методом:

  • через программное прерывание,
  • через инструкцию sysenter,
  • через инструкцию syscall.

Программное прерывание

Прерывания (англ. interrupts) — это как бы сигнал процессору, что надо прервать выполнение (их поэтому и назвали прерываниями) текущего кода и срочно сделать то, что указано в обработчике.

Прерывание извещает процессор о наступлении высокоприоритетного события, требующего прерывания текущего кода, выполняемого процессором. Процессор отвечает приостановкой своей текущей активности, сохраняя свое состояние, и выполняя функцию, называемую обработчиком прерывания (или программой обработки прерывания), который реагирует на событие и обслуживает его, после чего возвращает управление в прерванный код.

Программное прерывание — синхронное прерывание, которое может осуществить программа с помощью специальной инструкции.

В процессорах архитектуры x86 для явного вызова синхронного прерывания имеется инструкция int, аргументом которой является номер прерывания (от 0 до 255). В защищённом и длинном режиме обычные программы не могут обслуживать прерывания, эта функция доступна только системному коду (операционной системе).

В ОС Linux номер прерывания 0x80 (в десятичной системе — 128) используется для выполнения системных вызовов. Обработчиком прерывания 0x80 является ядро Linux. Программа перед выполнением прерывания помещает в регистр eax номер системного вызова, который нужно выполнить. Когда управление переходит в ring 0, то ядро считывает этот номер и вызывает нужную функцию.

Метод этот широко применялся на 32-битных системах, на 64-битных он считается устаревшим и не применяется, но тоже работает, хотя с целым рядом ограничений (например, нельзя в качестве параметра передать 64-битный указатель).

  • Поместить номер системного вызова в eax.
  • Поместить аргументы в регистры ebx, ecx, edx, esi, edi, ebp.
  • Вызвать инструкцию int 0x80.
  • Получить результат из eax.

Пример реализации mygetpid() (получение PID текущего процесса) на ассемблере (для системного вызова getpid используется номер 20):

Инструкция sysenter

Спустя некоторое время, ещё когда не было x86-64, в Intel поняли, что можно ускорить системные вызовы, если создать специальную инструкцию системного вызова, тем самым минуя некоторые издержки прерывания. Ускорение достигается за счёт того, что на аппаратном уровне при выполнении инструкции sysenter опускается множество проверок на валидность дескрипторов, а так же проверок, зависящих от уровня привилегий.

На сегодня эти инструкции (sysenter и sysexit) поддерживаются процессорами Intel в 32- и 64-битных режимах, процессорами AMD — только в 32-битном (на 64-битном приводит к исключению неизвестного опкода).

Поскольку 32-битные архитектуры теряют популярность, рассматривать не будем.

Инструкция syscall

Так как именно AMD разработали x86-64 архитектуру, которая и называется AMD64, то они решили создать свою собственную инструкцию для системных вызовов.

Эти инструкции (syscall и парная sysret) поддерживаются процессорами Intel только в 64-битном режиме, процессорами AMD — во всех режимах.

Системные вызовы при помощи этой инструкции делаются в современных версиях 64-битного Linux.

  • Номер системного вызова помещается в rax.
  • Аргументы записываются в rdi, rsi, rdx, r10, r8 и r9.
  • Затем вызывается syscall.
  • Когда управление возвращается, результат находится в rax.
  • Значения всех регистров, кроме r11, rcx и rax, системным вызовом не изменяются, дополнительно сохранять их не требуется.

Пример реализации mygetpid() (получение PID текущего процесса) на ассемблере (для системного вызова getpid по таблице используется номер 39):

Производительность

Системные вызовы требуют переключения контекста и перехода процессора в режим с высоким уровнем привилегий. Поэтому системный вызов выполняется относительно медленно по сравнению с вызовом обычной C-функции. Ещё хуже стало после обнаружения уязвимости Meltdown: патч KPTI (kernel page-table isolation), помогающий против уязвимости, приводит к сбросу TLB-кешей и дополнительному падению производительности.

Ориентировочные цифры, сколько занимает системный вызов на конкретном процессоре [1]:

  • int 80h — 500 нс,
  • sysenter — 340 нс,
  • syscall — 280 нс,
  • патч KPTI увеличивает эти числа на 180 нс,
  • вызов обычной C-функции — единицы нс.

Есть приёмы под названием vsyscall (уже устарел) и vDSO (virtual dynamic shared object) [2], которые позволяют в некоторых случаях избежать переключения контекста и ускорить выполнение. Помогает для системных вызовов, которым реально не нужны высокие привилегии, например gettimeofday. Удобно, если надо часто получать таймстемпы, например, для логов.

Мониторинг системных вызовов

Существует несколько инструментов, которые можно использовать для просмотра системных вызовов, которые выполняются программами. Самый известный из них, strace, доступен во многих операционных системах, и, вероятно, он уже установлен на вашем компьютере.

strace может запустить новый процесс или подключиться к уже запущенному. Вы можете многое узнать, подглядывая за системными вызовами, сделанными различными программами.

Использование в коде на C

Заголовочный файл unistd.h — основной, его наличие и содержимое обеспечивается стандартом POSIX.1. Видимо, «uni» пошло от UNIX.

Типы данных

Примеры типов данных POSIX (тем не менее, они не являются стандартными типами в языке C).

  • pid_t — идентификаторы процессов.
  • ssize_t — аналогичен size_t, но обязан быть знаковым и, главное, обязан уметь хранить минус единицу (это число используется как возвращаемое значение в случае ошибки во многих функциях).
  • off_t и off64_t — знаковый тип для хранения смещения в файле.

Получение справки

Сигнатуры функций и информацию об использовании можно почерпнуть из man-страниц:

Бонус: пишем «Hello, world!» на ассемблере

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

Что такое системный вызов и почему он используется?

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

В этой статье вы узнаете больше о системных вызовах и их типах в системе.

Когда необходимы системные вызовы

Ниже приведены некоторые ситуации, когда требуется системный вызов:

  • Чтение и запись из файлов
  • Если файловая система требует создания или удаления файла
  • Создание и управление новыми процессами в системе
  • Сетевые подключения требуют системных вызовов для отправки и получения пакетов
  • Доступ к аппаратной службе, такой как доступ к жесткому диску, принтеру, сканеру и т. д., требует системного вызова.

Примеры системного вызова в Unix-подобной системе

Некоторыми системными вызовами Linux/Unix являются read(), write(), send(), socket(), open(), close() и т. д. Когда вы пытаетесь записать в файл с помощью программы или прочитать ввод клавиатура требует системного вызова.

Типы системных вызовов

В целом мы можем разделить системные вызовы на шесть категорий, которые приведены ниже.

Контроль над процессом

Этот тип системных вызовов связан с созданием процессов и управлением ими.

Функции контроля процесса:

  • создать процесс (например, fork в системах Unix или NtCreateProcess в Windows)
  • завершить процесс
  • загрузить и выполнить
  • получить/установить атрибуты процесса
  • ожидание и сигнал события
  • выделить и освободить память

Управление файлами

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

Функции управления файлами:

  • создать файл
  • удалить файл
  • открыть и закрыть файл
  • читать, писать файл
  • получить/установить атрибуты файла

Управление устройствами

Эти системные вызовы отвечают за манипулирование устройством, например за чтение и запись в буфер устройства.

Функции управления устройством:

  • устройство запроса
  • выпускное устройство
  • логически подключить или отсоединить устройство
  • читать писать переставлять
  • получить/установить атрибуты устройства

Информационное обслуживание

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

Функция обслуживания информации:

  • получить/установить системную информацию, такую ​​как время, дата, имя компьютера и т. д.
  • получить/установить процесс, файл или метаданные устройства

Коммуникация

Эти системные вызовы используются специально для межпроцессного взаимодействия.

Коммуникационные функции:

  • Создать коммуникационное соединение для удаления
  • отправлять получать сообщения
  • информация о статусе передачи
  • подключать или отключать удаленные устройства

Protection

Эти системные вызовы отвечают за защиту файлов.

Защитные функции –

  • приготовься права доступа к файлу

Важные системные вызовы в Unix-подобных и Windows ОС

Системные вызовы в Unix-подобных системах и их эквиваленты в операционной системе Windows приведены ниже.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *