Что именно происходит, когда пользователь набирает в адресной строке google.com? Часть 1

    Перевод первой части материала с github, обстоятельно объясняющего работу интернета: что именно происходит, когда пользователь набирает в адресной строке google.com?

    Кнопка «ввод» возвращается в исходное положение


    Для начала отсчёта выберем момент, когда кнопка «ввод» утоплена. В этот момент замыкается контур, отвечающий за эту кнопку. Небольшой ток проходит по логическим контурам клавиатуры. Они сканируют состояние всех переключателей, гасят паразитные электрические импульсы, и преобразовывают нажатие в код клавиши 13. Контроллер кодирует код для передачи в компьютер. Теперь это почти всегда делается через USB или Bluetooth, а раньше в процессе участвовали PS/2 или ADB.

    Если это USB


    USB в клавиатуре запитано с напряжением в 5В по первому штырьку контроллера хоста USB в компьютере. Код клавиши сохраняется в памяти клавиатуры в регистре «endpoint». Каждые 10 миллисекунд контроллер USB запрашивает данные из этого регистра. Так он получает сохранённые коды. Код передаётся в USB SIE (Serial Interface Engine) и преобразуется в один или несколько пакетов низкоуровневого USB-протокола. Пакеты отправляются посредством дифференциального электрического сигнала по штырькам D+ и D- с максимальной скоростью в 1.5 Мб/с, поскольку HID (Human Interface Device) считается низкоскоростным устройством.

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

    Если это виртуальная клавиатура (сенсорный экран)


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

    Мобильная ОС передаёт сообщение в текущее приложение по поводу клика на одном из GUI-элементов (в данном случае это клавиши виртуальной клавиатуры). Клавиатура подымает прерывание для отправки сообщения о нажатии клавиши в ОС.

    Возникновение прерывания (не на USB-клавиатуре)


    Клавиатура отправляет запрос прерывания (IRQ), который контроллер прерываний сопоставляет с вектором прерываний. CPU использует таблицу описаний прерываний IDT, чтобы сопоставить вектора с функциями-обработчиками прерываний, которые предоставляет ядро. По приходу прерывания CPU запускает нужный обработчик. Так мы попадаем в ядро.

    (Windows) В приложение отправляется сообщение WM_KEYDOWN


    HID передаёт событие нажатия в драйвер KBDHID.sys, который преобразовывает его в сканкод. В нашем случае он равен VK_RETURN (0x0D). Этот драйвер общается с драйвером KBDCLASS.sys. Последний отвечает за обработку ввода клавиатуры безопасным способом. Он вызывает Win32K.sys (возможно, после передачи сообщения через разные фильтры клавиатуры). Это происходит в режиме ядра.

    Win32K.sys узнаёт, какое окно активно, через API GetForegroundWindow(). Это API предоставляет хэндлер для строки ввода браузера. Затем система обработки сообщений Windows вызывает SendMessage(hWnd, WM_KEYDOWN, VK_RETURN, lParam). lParam – битовая маска, содержащая дополнительную информацию о нажатии – повторения, сканкод, нажаты ли дополнительные клавиши и др.

    SendMessage API добавляет сообщение в очередь для заданного хэндлера окна (hWnd). Позже для обработки очереди вызывается функция обработки сообщений WindowProc.

    Активное окно hWnd – окно редактирования, и в этом случае у WindowProc есть обработчик сообщений для WM_KEYDOWN. Поскольку был передан код VK_RETURN, он знает, что пользователь нажал Enter.

    (OS X) В приложение передаётся KeyDown NSEvent


    Сигнал прерывания вызывает событие в драйвере I/O Kit. Он преобразовывает сигнал в код клавиши и передаёт его в процесс WindowServer. Тот создаёт событие для активных приложений через их порт Mach. События помещаются в очереди этих приложений. Прочитываются они оттуда тредами, имеющими соответствующий уровень доступа при помощи функции mach_ipc_dispatch. Чаще всего это делается через главный цикл NSApplication при помощи NSEvent / NSEventType KeyDown.

    (GNU/Linux) Сервер Xorg отслеживает коды


    При использовании графического сервера X для получения кода будет задействован драйвер evdev. По имеющимся правилам код клавиши будет преобразован в сканкод. После этого символ передаётся в менеджер окон (DWM, metacity, i3, и т.д.), который в свою очередь передаёт символ окну, находящемуся в фокусе. Графический API окна получает символ и выводит соответствующий символ в том окне, в котором находится фокус.

    Разбор URL


    Теперь у браузера есть следующая информация из URL (Uniform Resource Locator):

    Протокол «http» — используем 'Hyper Text Transfer Protocol'
    Ресурс "/" – запросить главную страницу


    Это URL или поисковый запрос?


    Если не задан протокол и строка не является допустимым доменным именем, браузер передаёт этот текст поисковой системе по умолчанию.

    Проверка списка HSTS


    Браузер проверяет список HSTS (HTTP Strict Transport Security). Это список сайтов, к которым нужно обращаться только по HTTPS. Если сайт в списке, то браузер отправляет запрос через HTTPS. Иначе – через HTTP.

    Преобразуем символы в имени сервера, которые не относятся к таблице ASCII.

    Браузер проверяет, есть ли символы не из диапазонов a-z, A-Z, 0-9, -,.
    Поскольку у нас google.com, таких символов не будет. Иначе к имени сервера будет применено кодирование по системе Punycode.

    Запрос DNS


    Браузер проверяет, есть ли домен в КЭШе. Если нет, вызывается библиотечная функция gethostbyname (в зависимости от ОС). Она проверяет, можно ли узнать адрес сервера по имени на основании информации из локального файла hosts. Если это не помогает, происходит запрос к DNS-серверу, который указан в настройках сети. Это либо локальный роутер, либо DNS-сервер провайдера. Если DNS-сервер в той же подсети, библиотека работает по протоколу ARP с сервером. Иначе запрос отправляется на IP-адрес стандартного шлюза.

    Протокол ARP (Address Resolution Protocol)


    Для отправки широковещательного запроса ARP, сетевому стеку нужно узнать IP-адрес получателя и MAC-адрес интерфейса, который будет для этого использован.

    Сначала проверяется кэш ARP на предмет наличия IP получателя. Если он есть в кэше, возвращается результат «IP получателя = MAC».

    Если её нет в кэше, то таблица роутинга просматривается на предмет наличия ip-адреса в какой-либо из локальных подсетей. Если он там есть, используется интерфейс, присвоенный этой подсети. Если нет, библиотека использует интерфейс подсети основного шлюза. Затем ищется MAC-адрес выбранного интерфейса и отправляется Layer 2 ARP запрос:

    Sender MAC: interface:mac:address:here
    Sender IP: interface.ip.goes.here
    Target MAC: FF:FF:FF:FF:FF:FF (Broadcast)
    Target IP: target.ip.goes.here
    


    Если компьютер подключён к роутеру напрямую, роутер даёт ответ ARP Reply. Если подключение идёт через хаб, тот передаёт запрос по всем портам. Если там будет роутер, он даст ответ. Если подключение через свитч, тот определит по своей таблице CAM/MAC, у какого порта есть нужный MAC-адрес. Если не найдёт, то распространит запрос по всем портам. А если найдёт, то отправит запрос по тому порту, где есть нужный MAC.

    Ответ ARP Reply:

    Sender MAC: target:mac:address:here
    Sender IP: target.ip.goes.here
    Target MAC: interface:mac:address:here
    Target IP: interface.ip.goes.here
    


    Теперь у библиотеки есть ip-адрес либо DNS-сервера, либо основного шлюза. Можно продолжить процесс распознавания домена. Открывается 53 порт и на сервер отправляется UDP-запрос (в случае больших запросов используется TCP). Если информации у DNS-сервера не оказывается, то запрашивается рекурсивный поиск, который проходит по списку DNS-серверов, пока не доходит до SOA и не находится нужный ответ.

    Открытие сокета


    Когда браузер получает ip-адрес сервера назначения, он вместе с портом (для HTTP порт по умолчанию – 80, для HTTPS – 443) использует их как параметры вызова функции socket и запрашивает поток TCP socket stream — AF_INET и SOCK_STREAM.

    Сначала этот запрос передаётся в транспортный слой, где создаётся сегмент TCP. Порт назначения добавляется в заголовок, а порт источника выбирается динамически из списка портов ядра (в Linux это ip_local_port_range).

    Сегмент отправляется в сетевой слой, где ему добавляют ip-заголовок, в котором содержится ip-адрес сервера назначения и ip-адрес нашего компьютера. Затем пакет попадает в Link Layer. К нему добавляется заголовок фрейма, в котором содержится MAC-адрес компьютера и шлюза (локального роутера). Если ядру неизвестен MAC-адрес шлюза, для его выяснения отправляется ARP-броадкаст. И теперь наш пакет готов к отправке через Ethernet, WiFi или мобильную связь.

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

    Иногда пакет отправляется сразу через Ethernet или оптику, тогда он остаётся цифровым и доходит до следующего узла сети. В конце концов сигнал доходит до роутера локальной подсети. Оттуда он идёт через пограничные роутеры AS, другие AS, и доходит до сервера назначения. Каждый роутер по пути извлекает ip-адрес назначения и передаёт пакет следующему хопу. При этом TTL в пакете уменьшается на единичку. Пакет отбрасывается, если оно достигает нуля, или если у текущего роутера закончилось место в очереди. Отправка и получение пакетов происходят многократно в рамках соединения по TCP.

    Сначала клиент выбирает изначальный номер последовательности (ISN) и отправляет пакет на сервер, установив бит SYN так, чтобы было ясно, что это ISN.

    Если сервер получает SYN и находится в благоприятном расположении духа, тогда он выбирает свой ISN, устанавливает SYN для индикации того, что в пакете содержится ISN, копирует в поле ACK клиентский ISN+1 и добавляет флаг ACK, чтобы подтвердить получение первого пакета.

    Клиент подтверждает соединение, отправляя пакет, где увеличивается свой ISN, увеличивается ISN отправителя и установлено поле ACK.

    Данные передаются так: когда одна сторона отправляет N байт, она увеличивает SEQ на это число. Когда другая сторона подтверждает получение пакета (или их цепочки), она отправляет пакет ACK, где значение ACK равняется последней полученной от другой стороны последовательности.

    Для закрытия соединения закрывающая сторона отправляет пакет FIN, другая сторона подтверждает получение пакета и отправляет свой FIN, а первая подтверждает его получение.
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 40
    • +15
      для кого это?
      • +11
        для собеседований
        • +3
          Я с удовольствием прочитал. Но формат больше geektimes'овский, да
          • +4
            Для познающих мир IT
            • +2
              Для познающих слишком много специфичных терминов, надо бы более простым языком всё описывать. Для тех, кто эти термины знает — статья бесполезна, в общем случае всё это им известно, а частности всегда смогут найти при желании.
              • +7
                Бегло пробежался снова по тексту. Специфичные термины разбавлены общим контекстом. Речь получилось энциклопедической. Так что речь вполне полезна именно для тех, кто познаёт. Начинаешь понимать что искать и имеешь контекст применения. Это ценно)
                • 0
                  Как вариант — для хорошо разбирающихся в терминах, однако не совсем знакомых с данной темой в следствии ее достаточной специфичности и сомнительной применимости в реальной жизни. Но мне понравилось)
              • +2
                Для нас с вами. Ведь все мы прочитали это до конца )
                • 0
                  К счастью, что бы читать комментарии не надо читать статью до конца.
                  Это очень полезно, так как есть статьи, которые порождают комментарии намного интереснее самой статьи.
              • +23
                Кнопка «ввод» возвращается в исходное положение

                Это если клавиатуру не залили предварительно сладким чаем :) Потому что тогда в цепочку процессов вклинится химия, физика, механика, психология и большое количество лингвистики в области обсценной лексики.
                • +16
                  На самом деле все еще проще: электрон туда, электрон сюда…
                  • 0
                    Это до тех пор пока вы не учитываете гравитацию.
                  • +1
                    Рекомендую подписаться на обновления репозитория. Он существует уже давно, но в последнее время внезапно активировался, очень многое добавляют/правят. Вряд ли возможно описать совсем всё происходящее, но подробностей будет больше.
                    • +2
                      Недостаточно подробно. :-(
                      Работу с оперативной памятью пропустили, как на логическом, так и на физическом уровнях… Хотя, это же только первая часть.
                      • +1
                        Да ладно память, там даже файлы сами по себе читаются: "...можно ли узнать адрес сервера по имени на основании информации из локального файла hosts". Ни в какие ворота.
                        • 0
                          Так надо ещё код побайтово разложить на уровне машинного языка.
                          • 0
                            Да всю статью в машинных кодах написать, всего делов-то…
                      • +2
                        Только вот не понял насчёт «Иначе запрос отправляется на IP-адрес стандартного шлюза.» в разделе Запрос DNS.

                        Зачем шлюзу ДНС-запрос? Запрос отправляется на IP-адрес ДНС-сервера, но в dst мак-адресе указывается мак-адрес шлюза. IP-адрес шлюза используется только для определения его мак-адреса.

                        Можно забавный эксперимент провести. Занести в arp статическую запись произвольный ip из текущей подсети + mac адрес шлюза. Далее поменять в настройках сети ip адрес шлюза, сделав его равным ранее занесённому в arp ip-адресу и всё будет работать.
                        • +13
                          По-моему забыли описать движение дырок в PNP-переходе и вынужденное излучение оптического квантового генератора.
                          • +4
                            И запрос на ближайшую электростанцию на порцию электронов ))
                            • +3
                              Фундаментальный подход — это правильно. Большое спасибо автору и переводчику.
                              • –2
                                про Enter в начале совсем противно написано
                                • +1
                                  Начали хорошо и более-менее подробно, но на стадии отправки запроса как-то все скомкано и свернуто :( А ведь там тоже все очень интересно.
                                  • +2
                                    Было же это на Хабре, разве нет?
                                    • +1
                                      Возникновение прерывания (не на USB-клавиатуре)

                                      Клавиатура отправляет запрос прерывания (IRQ),


                                      Если речь о PS/2, то запрос отправляет не клавиатура, а контроллер клавиатуры на материнской плате. Например, в ноутбуках IBM это делает Embedded Controller через LPC-шину.
                                      • +4
                                        Для отправки широковещательного запроса ARP, сетевому стеку нужно узнать IP-адрес получателя и MAC-адрес интерфейса, который будет для этого использован.

                                        чушь какая! все совсем наоборот — для отправки unicat-пакета сетевому стеку, а конкретно той его части которая отвечает за формирование пакетов-ethernet, нужно отправить широковещательный ARP-запрос, чтоб узнать mac-адрес next-hop. А до того как это произойдет, то еще сработает процесс маршрутизации, чтоб узнать какой ip-адрес спрашивать у ARP. Есть таки большая разница в прохождении трафика от хоста 192.168.0.10/24 до хоста 192.168.0.1/24 и от хоста 192.168.0.10/24 до хоста 8.8.8.8/32.
                                        • 0
                                          Как происходит преобразование нажатия в код клавиши 13?
                                          • 0
                                            Этим занимается контроллер на борту клавиатуры.
                                            • 0
                                              Не совсем так. Контроллер на борту клавиатуры посылает компьютеру скан-код нажатой клавиши. У PS/2-клавиатур этот код зависит от выбранного в данный момент способа кодирования (всего их три). У USB-клавиатур свой отличный набор кодов.
                                              Преобразование в 13 производит либо контроллер клавиатуры на материнской плате (для PS/2), либо драйвер (для USB).
                                              • 0
                                                Если я ничего не путаю, на физическом — scan code, потом key code (в случае с 13 может быть еще второй энтер на нампэде, с другим скан кодом, но таким-же key code), затем идет char code (не уверен, генерируются ли они для всяких мультимедийных и функциональных кнопок, или только для ascii символов). А еще может быть дополнительная обработка на уровне ОС — в виндовс можно ремапить клавиши через реестр, например.

                                                Надо бы найти где-нибудь структурированную информацию по этой теме, для повышения образованности.
                                          • +3
                                            XKCD в тему: xkcd.ru/i/676_v1.png
                                            • +1
                                              DNS пропустил. А на собеседованиях частенько хотят услышать подробности про это.
                                              • 0
                                                Даже если к сайту недавно обращались, и он есть в DNS кеше, то прописав в hosts другое значение, оно будет иметь преимущество.
                                                Подскажите что происходит — сперва hosts, затем DNS кеш, или просто отслеживается изменение hosts файла? Всегда хотел уточнить этот момент.
                                                • +1
                                                  Зависит от настроек в /etc/nsswitch.conf
                                                  • +1
                                                    Файл hosts автоматически подгружается в кэш DNS Resolver. Так что для Windows ответ на ваш вопрос — «происходит одновременно».
                                                    Пруф
                                                  • –1
                                                    Кнопка «ввод» возвращается в исходное положение
                                                    WM_KEYDOWN
                                                    При возврате уже WM_KEYUP будет, вообще-то.
                                                    • 0
                                                      И почему минус? В чём я неправ?
                                                      • 0
                                                        Потому что ошибка состояла, не в коде события, а в том что кнопка «ввод» зажата, а не возвращается в исходное положение.
                                                    • 0
                                                      Стандартный вопрос на собеседование в Akamai.

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