Search
Write a publication
Pull to refresh
43
0.2
Гончаров Вадим @nuclight

Программист Си | Perl | Tcl/Tk

Send message

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

Хм, не думал с этой стороны, аргумент (правда, среди fd этот учет самому надо вести, нет ведь енумераторных сисколлов). Однако тут, конечно, уши примитивности юниксового подхода торчат (доведенного в Plan9 до абсурда) - в винде были бы соответствующие API для получения, они не связаны абстракцией файла, ну и какой-нибудь WaitForMultipleObjects в качестве образца для kqueue мог бы еще и мьютексы ждать, например. С другой стороны, местами изначальные архитектурные косяки юникса всё же потихоньку правят - то переход на libxo начали, то вот недавно расширенные errno втащили (второй аргумент конечно лимитирован размером, но всё же большой шаг вперёд)...

То есть мало того что переложили на юзера, заставив его агрегировать чанки, так ещё и всё равно не дали регулировки по потокам.

Ну а что ты хочешь, backward compatibility, я ж сказал - ничего выдающегося не добавили, просто из "совсем жопы" сделали "так себе" - по крайней мере HoL от потери пакетов оно в ряде случаев устранит.

Я тут, конечно, ужален проблемами одного почти провального проекта, где из-за аналогичной глупости в Erlang оказалось практически нереально обеспечить стабильный поток проходящих данных, и дую на воду, но для меня теперь вариант "хлебай что дают из пожарного шланга", который реализуется таким подходом, не подходит для чего-то серьёзнее песочницы.

О, я тут последние полмесяца постом «Что не так с ООП в 2025» и комментами под ним (жаркий срач вышел) сподвигся наконец осилить туториал по Эрлангу. Честно говоря, он меня разочаровал - написано плохо, без объяснения многих моментов, даже строк нет, функицональщина (иммутабельность) непонятно зачем, как-то коряво выглядит. Так что дочитывал я уже без запуска примеров. Меня просто привлекает акторная модель (на самом деле изначальное ООП с сообщениями, да хоть netgraph, ну ты в курсе), а автор поста заявил, что этот позволяет обходиться без мьютексов. А у меня в мета-проекте, кроме протокола, еще задача специализированного безопасного языка, который можно было бы сунуть в ядро или передать на удаленный сервер - и вот познакомившись с eBPF/XDP, долго плевался, багу в шланге заводил, нифига оно толком непригодное было для задачи системы от защиты от DDoS-атак, которая у меня стояла (там даже чистку conntrack единым таймером не сделать, ни к черту вообще). Даже начинал собственный BPF64, но потом бросил - подход ассемблеров, похоже, в корне ошибочный.

В общем, я спросил там автора поста, как бы он делал систему на сто миллионов в conntrack'е акторной моделью (у меня в голове, конечно, кластер машин по типу pfsync/carp но с И отказоустойчивостью, И балансировкой нагрузки), челлендж ему понравился, но ответа пока нет :)

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

(Там было ещё хуже: один вход у "процесса" на всё включая канал регулировки. Тут хотя бы можно назад передавать сообщения типа "добавляю окно на 100кбайт для потока 8" и они могут быть прочтены вперёд собственно данных.)

Что, и тут всё плохо? Там рекламировали Akka.NET, я посмотрел их сайт, бросил как раз после описаний TCP-взаимодействия, имён мэйлбоксов и т.д. Мне-то больше подход MQTT по душе (он кстати в 5.0 вполне взрослый для хайлоадного RPC - response topics, все дела). Видимо, опять придется посмотреть у функциональщиков (в данном случае эрлангив) способы реализации и делать на более мейнстримных языках фреймворки...

Если один буфер на все потоки - это уже не то, что нужно. Нужны раздельные.

Нет, это зависит от приложения. Даже в QUIC у тебя не один, а два типа присылаемых апдейтов буфера - не только на конкретный поток, но и на всё соединение. Ну ниже рассмотрим.

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

Это для программиста приложения разницы нет, и то с точки зрения написания кода, а не поведения - а с точки зрения транспортного уровня разница колоссальная. Перенос стримов и фрейминга с уровня приложения на L4 позволяет добиться очень многого - во-первых, тот самый HoL, во-вторых - и это куда более важно - транспортный уровень может оптимизировать доставку, зная, сколько в этом сообщении будет байт, и тот факт, что получателю нет смысла получать сообщение по кускам. Первым делом, это multipath - сколько с ним бьются в TCP, всё без толку, QUIC не стал и пытаться. А с делением на сообщения мы просто берем - при установившихся стабильных SRTT, RTTvar, cwnd - и по алгоритму из Multilink PPP отправляем куски по разным путям с их скоростями так, что они приезжают вместе. Далее, извечная проблема tcp reordering, которой вообще-то быть не должно в принципе, а мы должны иметь возможность делать round-robin balancer'ы - дык вот, я пока что думаю, что она решается очень простым алгоритмом получателя "если есть дыра, и самый свежий прибывший чанк не последний в сообщении (нет флага E), то задержать SACK на величину RTTvar отправителя". А свой RTTvar он нам регулярно сообщает (обо всём этот в QUIC, конечно, не подумали, да там даже unordered нет...). Уже эти две вещи дадут гигантский прогресс, кмк.

А вот реально раздельное управление потоком по всем потокам (русский тут чего-то зажат в омонимию, per-stream flow control) ты на одном соединении не сделаешь, тут что TCP что SCTP надо сейчас несколько порождать, а тогда ещё и заботиться, чтобы пришло именно на нужный серверный процесс, указать серверу, что это соединение надо ставить в комплект вот этому... марудно. Вот потому, я так понимаю, и стали запускать SPDY с потомками - когда поняли, что от последней попытки (SCTP) ждать хорошего всё равно нельзя.

Да, это иронично (впрочем чего ожидать от телефонистов?..), только Гугл показал, что не умеет в протоколы. Оно как бы не хуже SCTP вышло-то. Особенно вот этот цирк с неумением в sequence number arithmentics с компрессией номеров пакетов, которым легко устроить DoS пира (гугл толстый, ему всё равно, а вот остальным...)

Я честно не понял ещё смысла в этом навороте. Поищу при случае.

Ну см. на https://pdos.csail.mit.edu/archive/uia/sst/ картинку - она красивая :) Хочу, например, костыль SSH для stderr обобщить просто дочерними потоками в дереве. Как минимум, вновь открываемый поток должен borrow'ить окно у своего родителя - иначе нам что, бесконечно буфера добавлять? Правда, дальше этой идеи они не специфицировали, увы :( Да, в имеющихся системах, что QUIC, что SSH2, имеют на каждый поток отдельное окно - потому что так проще в реализации. Но моя техническая интуиция говорит мне, что нужно более комплексное решение - и в HTTP/2 например пытались же делать дерево потоков. В конце концов, если в названии протокола Stream Control, надо соответствовать :) а "просто как пачка tcp" на это не тянет - в конце концов, при общем congestion control мы можем использовать эффективнее, хоть WFQ, и должны это делать в условиях ограничений, на которых мой протокол пытается выживать, например... собственно, вот да - допустим у тебя сто или тысяча потоков, обновлять rwnd каждого будет неприлично chatty, если этого не требуется, а полоса scarce.

Попробую придумать пример... Ну, поскольку делается оно для помеси мессенджера с соцсетями, возьмем в пример Telegram. Допустим, мы чатимся, и в это время слушается музыка и качаются файлы. Понятно, приоритет отдадим тексту, но музыке незачем ограничивать окно вообще (чтоб заикалась, если ACK потеряется), а файлам всем дать общее окно - всё равно пишущий их тред упирается в скорость HDD, а какой из них быстрее скачается, какая разница. Тут, понятно, и дескрипторы бы отдельные, да не все (то есть треду наверное хватит одного для сразу нескольких потоков), но тут наверное сложно в реализации, не придумаю сходу (что-то вроде peeloff из поддерева, штоль).

Заметки тоже почитаю.

Да, меня пока еще никто толком не поревьювил, к сожалению. Правда, тамошняя свалка этому способствует :) Я бы вот, например, всё хотел объединить все типы потоков в единый - сейчас у меня как бы три типа, bytestream (tcp-like) channel'ы (наподобие SSH) и два делящихся на сообщения, ordered и unordered (как в SCTP), плюс id'ы сообщений чтоб опциональная докачка сообщений при обрыве (если QoS>0 как в MQTT - да, мы сессионый протокол, L5). Но взаимно управлять потоками удобнее, когда вот у него просто номер: 1, 2... и эти номера не делятся дополнительно на типы (но суть unordered в том, что он именно что в том же потоке логически, как решение URG/^C telnet). Как это втиснуть в SOCK_STREAM/SOCK_DGRAM, если выделять отдельные fd вообще не представляю. Если нет.. всё вопрос красивости единого API стоит. В SSH2 интересно сделали, там channel'ы byte-oriented, хотя и делятся на чанки ниже уровнем, и в них возможны request'ы/reply синхронные - то есть дочитали до байта, тут раз, сообщение целиком (как бы переключение, но нет, эдакий URG отдельный большого размера), потом снова просто байты будут. Но там просто луп в коде парсит сообщения и сразу обрабаттывает, им там до теоретической вынесенности и красивости API нет дела, они уже и так на L7...

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

Так и про мьютексы можно написать.

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

А как у нас с многопоточностью этого огромного switch/case ?Вроде с этого требования начинали.

А, я забыл, что после коммента про Go надо было не отвечать. Как можно было забыть, что каждый "огромный switch/case", то есть актор, живёт в своем отдельном (green) треде, не понимаю, если уж "с этого начинали".

И что в ней сильного, если Erlang предлагает гораздо лучше, причем задолго до появления Go? Типа хоть что-то в языке не убогое, а "норм" ? Ну если только так.

Его незачем "спасать", он специально для быдло-кодеров и проектировался. Автору именно такую задачу поставили.

Про IoT на самом деле не так, конечно - там до сих пор байты считают (эффективный MTU 33-70 байт - как тебе такое, Илон Маск?).

Тем не менее, ядра пишутся, работают.

Ну да, ядра пишем на Си и работает.

"Есть многое на свете, друг Горацио, что и не снилось нашим мудрецам" (с)
Perl это уж очень сильно на любителя, имхо.
Go выглядит значительно привлекательнее по многим параметрам.

Убогий Go - и привлекателен? Ну понятно, обсуждение вышло за рамки средних умов, продолжать нет смысла. Тут только мем с гауссианой и капюшономом подходит.

Не лучше, а во многом хуже как раз.

Про struct sockbuf я не в курсе, что там не так. Или можно было бы сделать, например, дочерние fd, привязанные к конкретному потоку, чтобы над ними уже вызывать хоть простейший read().

Неточно выразился. В структуре сокета в ядре предусмотрено только два sockbuf - на прием и отправку. Возможно, именно поэтому SCTP и не стал делать раздельного чтения потоков, ибо дочерние fd выглядят коряво - да и делался он еше когда существовали еще только лишь select() и poll(). Впрочем, с kqueue() оно тоже в уже существующих не ложится идеально в семантику, как раз вот только обсуждал проблему:
https://lists.freebsd.org/archives/freebsd-hackers/2025-July/004749.html

ещё не прочитал RFC, если там что-то не понравится - выскажу отдельно.

Да там ничего особо выдающегося, просто устранили эту проблему архитектуры.

А она тут в принципе может быть, эта "правильная" реализация, если у нас вот сейчас настроение выбрать всё из потока 9 не выбирая остальных, даже если для них приёмные буфера забиты?

Так это ж разные уровни - ACK оно по факту приема пакета, который будет положен в буферы сокета (и его нельзя не акнуть, ибо иначе congestion control вскинется), а уже потом, позже, приложение будет решать, какой вычитать. Допустим, оно дало общий буфер сокета на много мегабайтов, а сообщения по пять кило, так что для отправителя можно акать дальше. Ах да, стримам-то необязательно быть либо "как в SCTP" либо "как отдельные TCP" - на практике-то захочется иметь иерархию стримов (даже в HTTP/2 её делали), как в SST, где ребенок borrow'ит окно у родителя, а тот у бабушки... В общем, большая сложная тема, я уже давно пишу черновики https://github.com/nuclight/musctp

не даёт возможности регулировать отдельные потоки и потому не защищает от HOL blocking. Всё, что дают его потоки, это тегирование

Пофикшено в RFC 8260 чанками I-DATA. Это 2017 год, после SPDY, но всё же до QUIC.

С остальным верно, поэтому я начал разрабатывать свой muSCTP :) Однако, "выбрать только из конкретного потока" плохо ложится как на BSD Sockets API, так и на struct sockbuf в ядре...

что acknowledge присылается на TSN, а не по каждому потоку раздельно.

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

Кобол - он не только мейнфреймы, в нашем биллинге у одного желто-полосатого оператора был миллион строк на нём (и десять на Си) на вполне себе Solaris/SPARC/Oracle.

Все эти транспиляторные решения - ну такое себе. Компаниям обычно дешевле оказывается научить новых людей Коболу, чем поддерживать сгенерированный "переведённый" код. А некоторые - мы вот GNU COBOL использовали - вообще почти нечитаемое генерируют

Ну такие же фетчи из курсоров в Oracle Pro*C, только работать это может уже (в нашем случае) на Solaris на SPARC, которое хоть и железо дорогое и ось старая, но всё-таки Unix и достаточно современное. В отличие от вендорлока на мейнфреймы...

И тут тоже не про Кобол. А множество бинарников (в нашем биллинге было пару тысяч) и подобие микросервисов (например Tuxedo) тоже типовой паттерн в таких системах, независимо от языка.

В данном примере как-то валидация не видна - вон, INN такой же char, а не PIC 9 хотя бы.

А что там с работой с БД? Только SQL, причем только динамический? Без возможности подготовки запроса на этапе компиляции? И даже по одной таблице по индексированным полям доступ к БД все равно только скулем? Задач, где нужно из одной таблицы прочитать десяток записей по известному значению ключа в реальной жизни достаточно много. И гонять ради этого динамический скуль очень расточительно.

Вообще-то под "динамическим" SQL понимается, когда запрос собирается на ходу из кусков в зависимости от параметров, и это в таких (да наверное и вообще) системах редкость. А для типовых будет стадия PREPARE, так что какая разница-то.

код был страшен

Вот в этом-то и проблема обычно, иначе б давно все мигрировали.

Другой вопрос кому такие низкие скорости нужны. Сейчас практически любой полезный сайт сразу заставляет загрузить мегабайты информации только для одной страницы. Просто замучаешься ждать даже на самой высокой скорости.
Лично меня аж передёргивает от воспоминаний, когда интернет был по дайлапу. Да даже ADSL вспоминать неприятно.

Вот когда РКН начнет любой неизвестный трафик шейпить до 20 кбит/с, сразу станут нужны системы, которые на этом работать в состоянии. Не современный Web, да. Но и в те времена Fido эффективнее на модемах работало, чем сайты - на моем 14400 за 1600 байт/с разгонялось, а в интернете едва больше килобайта скачка файлов.

1
23 ...

Information

Rating
4,533-rd
Location
Москва и Московская обл., Россия
Date of birth
Registered
Activity

Specialization

Specialist
FreeBSD
Perl