Во-первых, видео открытого урока
подготовить знания
Посмотрите, как я снял NIO с алтаря
Пользовательский режим и режим ядра
Прежде чем говорить о IO, нам нужно понять системную архитектуру Linux. Одна из философий дизайна Linux заключается в предоставлении разных разрешений на выполнение разных операций, то есть в привилегированном режиме. То есть некоторые ключевые операции, связанные с системой, должны выполняться программой с наивысшим уровнем привилегий. Например: ресурсы ЦП, ресурсы хранения, ресурсы ввода-вывода и т. д.
Процессор Intel с архитектурой X86 обеспечивает четыре уровня привилегий от 0 до 3, чем меньше число, тем выше привилегия. Операционная система Linux в основном использует два уровня привилегий, 0 и 3, которые соответствуют режиму ядра и режиму пользователя соответственно.
пользовательский режим: Только ограниченный доступ к памяти, без доступа к периферийным устройствам (диск, сетевая карта), переключение процессов не допускается.
Состояние ядра: ЦП может получить доступ ко всем данным в памяти, включая периферийные устройства (жесткие диски, сетевые карты), что позволяет переключать процессы.
Когда создается любой пользовательский процесс в Linux, он содержит два стека: стек ядра и пользовательский стек, которые являются частными для процесса, и процесс начинает работать из пользовательского режима. Когда процесс выполняет собственный код пользователя, говорят, что он находится в пользовательском рабочем состоянии (пользовательском состоянии). То есть в этот момент процессор работает в наименее привилегированном (уровень 3) пользовательском коде. Когда задача (процесс) выполняет системный вызов и обнаруживается при выполнении кода ядра, мы говорим, что процесс находится в состоянии выполнения ядра (или просто называется состоянием ядра). Теперь процессор выполняет код ядра с самым привилегированным доступом (уровень 0). Когда процесс находится в состоянии ядра, исполняемый код ядра использует стек ядра текущего процесса.
Переключение из пользовательского режима в режим ядра
-
Системный вызов (мягкое прерывание)
Все пользовательские программы запускаются в пользовательском режиме, но иногда программа должна выполнять некоторые вещи режима ядра, такие как данные чтения с жесткого диска и т. Д. Единственное, что может сделать эти вещи, это операционная система, поэтому программа должна сначала запросить операционную систему для выполнения этой операции во имя программы. В это время необходимо механизм: программа пользовательского режима переключается в режим ядра, но не может управлять инструкциями, выполненными в режиме ядра. Этот механизм называется системным вызовом, а его реализация в процессоре называется инструкцией ловушки.
-
Периферийные прерывания (жесткие прерывания)
Когда периферийное устройство завершает операцию запроса пользователя, оно посылает сигнал прерывания, как и ЦП.В это время ЦП приостанавливает выполнение следующей инструкции, которая должна быть выполнена, и переходит к выполнению программы обработки, соответствующей сигналу прерывания. , Если ранее выполненная инструкция находится в пользовательском режиме, переход из пользовательского режима в режим ядра происходит естественным образом.
-
ненормальное событие
-
Когда ЦП выполняет программу, работающую в пользовательском режиме, внезапно происходят некоторые неожиданные аномальные события.В это время будут инициированы аномальные события, связанные с текущим процессом выполнения в пользовательском режиме для выполнения в режиме ядра, такие как исключение ошибки страницы.
Приложения пользовательского пространства входят в пространство ядра через системные вызовы. В это время процесс пользовательского пространства должен передать ядру много переменных и значений параметров.При запуске режима ядра некоторые значения регистров и переменные пользовательского процесса также сохраняются. Так называемый «контекст процесса» можно рассматривать как эти параметры, передаваемые пользовательским процессом ядру, а также набор переменных и значений регистров, которые ядро хочет сохранить, и окружение в это время.
Аппаратное обеспечение инициирует сигнал, в результате чего ядро вызывает обработчик прерывания и входит в пространство ядра. В этом процессе ядру также передаются некоторые переменные и параметры оборудования, и ядро выполняет обработку прерывания через эти параметры. Так называемый «контекст прерывания» на самом деле можно рассматривать как эти параметры, передаваемые аппаратным обеспечением и некоторыми другими средами, которые необходимо сохранить ядру (в основном, среда процесса прерванного в данный момент выполнения).
Можно увидеть пользовательский режим к переключению режима ядра является значительным накладным расходом.
Вернемся к началу статьи, если наше приложение для чтения сети данных через сокет, обязательно включает системный вызов (функция чтения), переключается из режима пользователя в режим ядра, карта данных читается ядром, обратно в режим пользователя процесс .
ввод-вывод из ядра
step1
использоватьstrace -ff -o ./log java BioServer
Команда запускает программу Java, а команда strace может помочь нам отследить все системные вызовы во время работы программы.
public class BioServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8091);
System.out.println("step1: bind 8091");
while (true) {
Socket socket = serverSocket.accept();
System.out.println("step2: accept " + socket.getPort());
new Thread(() -> {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
out.println("Server recv:" + line);
}
} catch (Exception e) {
}
}).start();
}
}
}
Глядя на журнал, напечатанный strace, сокет 5 вызывает функции ядра bind и listen, затем выполняет стандартный вывод 1 и, наконец, выполняет функцию опроса для блокировки.
step2
-
jps
Посмотреть pid BioServer
-
/proc/pid/fd
Все fds (файловые дескрипторы) процесса хранятся в каталоге
- 0: стандартный ввод
- 1: Стандартный выход
- 2: вывод ошибки
- 3: библиотека jdk
- 4, 5: сокет ipv4, ipv6
-
/proc/pid/task
В каталоге хранятся все потоки процесса
- Установите два соединения, используя порт nc ip
Снова взглянув на файловый дескриптор процесса с pid=27290, сокетов 6 и 7 действительно больше.
- использовать
netstat -natp
Команда для просмотра порта 8091 также устанавливает доступное TCP-соединение.
- Просмотр журналов трассировки
Сначала вызовите функцию accept, чтобы создать новый сокет fd=7, а затем вызовите функцию clone, чтобы создать новый поток с pid=7616.
- Просмотр журнала pid=7616
Поток 7616 заблокирован в функции recvfrom(fd=7).
Эволюция ИО
Итак, суть BIO заключается в том, что поскольку функция recvfrom(fd) системного вызова заблокирована, для подключения и чтения сокетов необходимо использовать многопоточность.
В чем проблема с этой моделью? Создается слишком много потоков, суть в том, что①Функция recvfrom ядра блокируется.
Если функция recvfrom не блокируется, то проблема обработки N клиентов одним потоком может быть решена.
Но если только функция recvfrom неблокирующая, если клиентов более миллиона, то каждый soekct будет вызывать функцию recvfrom один раз в каждом цикле,② Независимо от того, есть ли в сокете данные для чтения, произойдет переключение из пользовательского режима в режим ядра (системный вызов), а временная сложность будет равна O(n)..
Если можно выбрать только сокеты, доступные для чтения и записи, временная сложность может быть снижена до постоянного уровня.
Так ядро обеспечивает системный вызов select (мультиплексирование), который может вернуть дескриптор файла доступного для чтения сокета, а затем программа вызывает функцию recvfrom, что значительно сокращает количество системных вызовов.
Что не так с этой моделью? То есть③ Каждый раз, когда выбор цикла будет передавать файловые дескрипторы всех сокетов ядру, а затем ядро будет проходить по всем файловым дескрипторам..
Как решить эту проблему — epoll.
Приложение проходит сначалаepoll_create
Системный вызов создает область памяти, а затем вызывает过epoll_ctl
Добавляет дескриптор файла в открытую область и является событием accpet. Когда клиент подключен, он отправляет жесткое прерывание ядру через сетевую карту. Доступный для чтения сокет будет обрабатываться приложением. Таким образом, ядро epollуправляемый событиями. В java соответствует selectKey.
Два режима запуска epoll
-
Горизонтальный триггер (LT)
-
Срабатывание по фронту (ET)
Обсуждение ET и LT
Технология нулевого копирования Linux
Ссылаться на: