Comments 97
как часто вы сегодня встречаете инженера, который способен без страха разобрать подобный механизм вручную?
Разобрать или разработать?
Я, например, каждый день такое делаю.
И сходу вижу в приведенном коде классическую уязвимость переполнения буфера.
Программисты 90-х, вы что заканчивали, или в программирование приходили даже после заборостроительного института?
Человеку, который в 80х летал на Луну на программируемом калькуляторе, не нужен был никакой институт в 90х. 😏
Прикладную математику, в основном. На тех факультетах еще учили говорить «оканчивать» применительно к учебным заведениям, а не использовать колхозное «заканчивать».
Думаю что большинство было самоучками (я из начала 2000-х, но тоже еще такой), при чем материалы для обучения тоже еще нужно было "добывать", что добавляло упорства в изучении: зря что-ли старался искал/качал - изучай теперь.
А теперь сравним с современными "программистами", прошедших модные курсы, которые обещают что после окончания за них сразу начнут бороться гугл/яндекс/амазон и прочие с зарплатами от +100500$ и которые для банального определения чет/нечет загружают сторонний пакет из интернета...
Материалом для обучения были талмуды на тыщу страниц, типа таких:

Кстати, тоже намного полезнее, чем большинство курсов.
Для Delphi32 (1994 год) — не было никаких книжек, тем более на русском…
В 1994 я ещё был в 10-м классе. А домашним компьютером имел 8086 с голым DOS, монохромным дисплеем и жёстким диском на 20 Мб. На такой конфигурации единственное что прекрасно и без тормозов работало - это русифицированный Turbo Pascal. И основным источником информации была очень удобная встроенная справка на русском языке. Но помимо этого в магазинах популярна была ещё и такая книга 1992 года издания:

Вы-таки будете смеяться, но в программирование в 90-х я с пришёл из приборостроительного техникума, который студенты называли «заборостроительным», и код писал уже тогда в точности по тем принципам, что в публикации. Вы думали, что пошутили? А вот оно как бывает...
Про заборостроительный институт я вычитал в одной автобиографической статье, человек описывал свой путь в 70-е годы, он окончил инженерно-строительный институт и писал, что в программирование откуда только не приходили.
Да и кто так не писал? Буквально пару недель назад окунался в свой код для микроконтроллера ATmega64. Этой штуке уже 20+ лет, последние изменения вносились году в 2008 примерно. С тех пор так и используется в железках. И ведь работает же, вот в чём фокус.
Программисты 90-х, вы что заканчивали
Я, например - Ленинградский Питерский Политех. Факультет Технической Кибернетики.
PS. Если что, специальность "Программное обеспечение" - с конца 1960х существовала в советских ВУЗах.
В конце 1980х по этой специальности в Ленинграде готовили как минимум в Универе (факультет ПМПУ), ЛЭТИ (ФАВТ, если не ошибаюсь), ЛПИ (Политех; причём на двух факультетах - ФТК и ФизМех), Бонче (имени Бруевича :), ЛИТМО... Наверно - много кого забыл перечислить...
Например, специализированных около-финансовых ЭВМщиков готовил Финансово-Экономический институт (ФинЭк)
Не, ну как, в 80-е ходили в комплюктерные кружки при всяких Станциях Юных Техникусов. Там у нас были мощные супер-компьютеры типа Правец-8Д, пара Спекки-48, ещё МС-0511 и пара каких-то Агатов. Потом, уже в 90-х, появился свой первый ПК, там всякие паскали/дельфи/msvs и т.д. SoftIce опять-таки, куда ж без этого. Без софтайса не работал WinAmp и WinZip, приходилось объяснять им что я куплю, обязательно куплю, но когда-нибудь потом, а сейчас хочу чтоб работало (и ведь так и не купил, засранец). Ну вот оно как-то таким коловоротом и происходило в те времена. А академиев мы не кончали, уж извините. Приходилось читать, изучать, пробовать, ошибаться.
Да ничего, это еще по богатому, я когда пришел в 1984 году в кружок пионерский в Астрахани, я уже давно тогда пионером не был, там у них никаких компьютеров не было, стояла коротковолновая радиостанция, а ЭВМ увидели в астраханском рыбвтузе, стояло что-то лампочкомигающее, не серия ЕС, и настольная типа Искра-226
Фреймворки всегда были, ну если абстрагироваться от самого смысла этого слова. Набор утилиток самописных, огромный файл с макросами для асма - это тоже были фреймворки своего рода. Часто их писал один и тот же человек.
А о чем пост то?
архивные фрагменты кода на C
Как уже написали, нет защиты от переполнения буфера. А ещё там нет проверки корректности входной строки (пример: "a==c"), не инициализируется буфер (пример: "a="), используется постоянное разименование ссылки вместо отдельной переменной и if/else вместо switch/case.
решал задачу поиска самоповторов в массиве без дополнительной памяти
Имея при этом квадратичную сложность.
Вот пример кода на C под Linux, который демонстрирует ручное управление файловым вводом и выводом
Нет контроля успешности чтения и записи файлов.
IMHO, самый главный навык тех времён - это подзабытое сейчас умение отлаживать свой код. Не просто менять его, пока не заработает, а именно строка за строкой, шаг за шагом смотреть, что именно происходит в коде, как меняются переменные, как вычисляются условия, куда идут переходы.
Не просто менять его, пока не заработает
А что, правда так делают? Где вы таких находите?
Построчная отладка кода "строка за строкой" крайне неэффективный способ разбираться в программе и/или исправлять ошибки. Бывает полезно иногда, но чаще рулят логи, тесты, декомпозиция и т.д
Построчная отладка кода "строка за строкой" крайне неэффективный способ
Не соглашусь. Не нужно же проходить по шагам всю программу – достаточно поставить брекпойнт в потенциально проблемном месте и посмотреть, что происходит именно там. Или несколько брекпойнтов – вместо того, чтобы дописывать в каждое подозрительное место лог и заново перекомпилять программу. Не говоря уж о том, что в дебаггере видно состояние каждой переменной, стек вызовов – много чего. В отладочный лог всего этого не напишешь. В конце концов, можно поставить брекпойнт по условию: переменная x приняла состояние y, и поймать таким образом в точности момент появления ошибки, без каких-либо усилий. Очень эффективно.
тесты, декомпозиция
Тесты никогда не выявляют всех ошибок, а декомпозиция вообще не про отладку.
Я считаю крайне неэффективным вот это
а именно строка за строкой, шаг за шагом смотреть, что именно происходит в коде, как меняются переменные, как вычисляются условия, куда идут переходы
Понятно, что в некоторых случаях поставить breakpoint вполне себе рабочий метод.
а декомпозиция вообще не про отладку.
если отладку понимать как "de-bugging", то есть способ локализовать, понять и исправить ошибку, то декомпозиция это про отладку. Система декомпозируется на компоненты. Смотрим на контракты, и сужаем поле поиска. В случае какого-нибудь хитрого плавающего повреждения данных отладка с брейкпойнтами не работает совсем и нужны тесты, логирование, построение мысленной модели, анализ контрактов подсистем и т.п.
Про эффективность повторяться не буду, уже всё написал. А вот это совершенно неверно:
если отладку понимать как "de-bugging", то есть способ локализовать, понять и исправить ошибку, то декомпозиция это про отладку.
Качественная декомпозиция помогает избежать ошибок в коде за счёт его лучшего структурирования, разбиения на простые логические единицы, вероятность совершить ошибку в которых меньше, а покрыть их качественными тестами проще. Соответственно, может исключить необходимость отладки как таковой. Если же локализация ошибки неочевидна, то понять, в какой из десятков модулей нужно вставить лог, может оказаться даже сложнее. Нет, декомпозиция – совсем не про отладку.
Давайте переведу то, что вам написали, — на тот язык, который вы понимаете:
Если отладку понимать как избавление от ошибок, то […]
В следующей же строке вы пишите «декомпозиция помогает избежать ошибок», выражая тем самым полное согласие с оппонентом. А потом сами с собой яростно дискутируете. Прежде чем бросаться спорить, имеет смысл понять, что вам говорят, даже если придется заглянуть в словарь.
Давайте переведу то, что вам написали, — на тот язык, который вы понимаете
Знаете, дискутировать в таком тоне нет ни малейшего желания. Вижу, Вам тут минусов понаставили – предположу, именно за это. Хотя и по сути тоже всё не так.
А с вами никто и не дискутирует. Я просто указал на то, что вы не способны понять простейший текст.
Вам тут минусов понаставили – предположу, именно за это
Вот уж на что мне всегда было насрать — так это на мнение хабрахомячков. За мной несколько альтернативно одарённых поленьев по всем темам ходят, минусы ставят.
В высококонкурентной распределенной среде — тесты с логами тоже так себе подмога.
Поэтому действительно, в простых случаях (когда помогут и логи и отладчик) ошибку можно легко увидеть, просто взглянув на код (инструментарий избыточен), а в сложных — вся эта лабуда типа точек останова, логов и тестов — тоже не помощник (инструментарий непригоден), и снова придется просто смотреть на код, строить мысленные модели и типа того.
Бывает полезно иногда, но чаще рулят логи, тесты, декомпозиция и т.д
Ну так логи и тесты вам во многих случаях не найдут ошибку, а просто вам подскажут, какое место в коде стоит пройти построчной отладкой.
Если человек, зная проблемный кусок, не может его «пройти отладчиком» мысленно, — ему место в дворниках, а не в разработке.
Бывает всякое, например может быть где-то выход за границы массива и перезапишет переменную которая вообще в другом месте используется. И тогда мысленный проход кода с этой переменной не помогает, но это имхо. А отладка поможет увидеть что в переменной данные странные. Да и других случаев тоже можно вспомнить.
Если человек, зная проблемный кусок, не может его «пройти отладчиком» мысленно, — ему место в дворниках, а не в разработке.
Это громкое заявление ни о чём. Посмотрю я, как вы мысленно найдёте, какое поле вызывает NullReferenceException в середине блока с кодом, не заглядывая туда отладчиком.
Я уже давно могу себе позволить не связываться с языками, в которых бывает NullReferenceException.
Я уже давно могу себе позволить не связываться с языками, в которых бывает
NullReferenceException
Вот про разработчика, у которого простота языка является критичным критерием его выбора, я тоже могу немало колкостей наговорить :)
И будете выглядеть довольно глупо, потому что простота здесь вообще ни при чем, речь исключительно про адекватность и продуманность.
Вот это не факт, но вы легко можете меня переубедить, назвав адекватный и продуманный язык, в котором нельзя ошибочно обратиться к полю несуществующего объекта.
Я даже стесняюсь выбрать. COBOL, F#, Erlang, Elixir, Closure, Haskell. Могу еще с десяток менее распространенных назвать.
Могу еще с десяток менее распространенных назвать.
Ну ок, тогда следующий вопрос: а в чём, как вы считаете, адекватность и продуманность КОБОЛа, например?
Или ладно, кто его сейчас помнит, вот в чём адекватность F#, который в таком случае выводит null:
let x : int option = None
x |> Option.map(fun x -> x.ToString()) |> printfn "%A"
Неявное удавливание рантаймовой ошибки, это вот вообще никак не адекватное поведение, это откровенный косяк языка.
COBOL — самое красивое, что случалось с CS (после SmallTalk и Erlang, наверное). Речь идет про 1959, когда он появился, но и сейчас — многие вещи наподобие структур данных и инструкции COPY вызывают восхищение. Эрланг — до сих пор с отрывом самый адекватный язык для распределнных систем, даже кубер попытался скопировать идеи Армстронга (но получилось криво и малоработоспособно, конечно).
который в таком случае выводит
null
У меня нет под рукой F#, но у вас есть None, который вы маппите через Option.map, получая назад, очевидно, None (функция fun x -> x.ToString() просто не должна вызваться). Классическая тривиальная монада. Потом печатаете этот самый None (адекватно было бы напечатать None, но я не помню, что означает модификатор %A, и как там тайпкласс Show вообще реализован — null прекрасно передает суть).
О каком удавливании какой рантайм ошибки вы говорите — мне невдомёк. Рантайм-ошибки — это то основное зло, ради которого люди строгую типизацию придумали, ваще-то. В хорошем коде не должно быть рантайм-ошибок вообще (это не всегда возможно, поэтому тот же Эрланг знает, как с ними правильно обращаться). Но тут-то всё прозрачно и кристально чисто: функция не вызвана (так задумано), на выходе None. Что не так-то?!
Вы напомнили мне замечательное руководство для начинающих программистов

Там про отладку, как не надо делать, рассказано довольно подробно.
IMHO, самый главный навык тех времён - это подзабытое сейчас умение отлаживать свой код.
Ну, давайте так - защита от переполнения буфера могла быть где-то раньше. Например размер буфера никак не мог превышать размер пакета. Неинициализируемый буфер "a="превратится в пару "a", "", а мусор на входе "a==c" превратится в мусор на выходе - в пару "","c" (если я правильно разобрал этот код - судя по всему второе и последующие равно просто перетрут ранее считанное значение ключа). И это вполне может быть совершенно нормальным поведением программы. Да, не очень масштабируемым, и не экстремально быстрым, но достаточно легко масштабируемым и дополняемым.
Как по мне - так мы проиграли террористам и наркоманам. Что в жизни (рамки, странные ограничения на объем жидкости в ручной клади, требование странных товарищей из "транспортной безопасности" включить экран и т.д. и т.п.), что в программировании. Из-за практически невероятного сценария "роста до мировых масштабов" или "DDoS атаки на стиральную машину" я вынужден делать множественные перепроверки всего и в них плодить уязвимости куда более страшные, нежели исходное "мусор на входе - мусор на выходе".
Например размер буфера никак не мог превышать размер пакета.
Может быть. И как учебный пример сойдёт. Но в реальном продукте может оказаться, что завтра размер входных данных увеличили, а размер буферов забыли.
Неинициализируемый буфер "a="превратится в пару "a", ""
Да, тут я пример неправильный подобрал. Вот если на входе просто "a", то на выходе в value может быть произвольная строка, поскольку в конце цикла j = 1, value[1] = 0, а вот что записано в value[0] мы не знаем.
Добавлю. Если на входе "a", то ещё и нет гарантии, что key завершается нулём.
Так это ровно то, о чем я и говорю.
Как-то незаметно, но очень уверенно цели проектирования системы изменились. Если изначально они были "максимально эффективно решать поставленную задачу", то теперь они стали "решить задачу, и быть готовым к ее росту до межгалактических масштабов". И плевать всем на то, что задача "протереть ложку" в принципе не очень подходит для масштабирования. Нет - мы обязательно сделаем возможность увеличения ложки до размеров созвездия, а тряпки - галактики. Так, на всякий случай... А вдруг да и действительно дорастем.
Все ваши примеры содержат исходно неправильную строку. Тот самый "мусор на входе". По сути тут есть три решения - первое самое простое. Мусор на входе - мусор на выходе. Неправильна строка может приводить к неправильным результатам. И вместо маскировки реальной проблемы - генерация неправильной строки, мы начинаем бороться с последствиями. Второй вариант сложнее. Мусор на входе - тишина на выходе. По сути просто игнорировать ошибочные входные данные. Но для этого нужны строгие критерии того, что допустимо а что нет. И в случае, подобном разбираемому, что делать с данными ДО ошибки и с какого момента до конца сообщения считать ошибку законченной. Третий вариант - самый лукавый. Придумать себе гору неправильных строк, метод их решения, все закодить и задокументировать. Программеры в работе, менеджеры отчитываются, код растет - но по сути это все не более, чем имитация бурной деятельности. Ибо пришедшая с ошибкой строка - в любом случае криминал и признак неисправности внутри системы. А дальше в зависимости от обстоятельств - от аварийной остановки до "погибаю но не сдаюсь". Исправно работать на неисправном оборудовании с некорректными входными данными - ну да, ну да... Верю-верю...
Что было в ТЗ у разработчика этого кода ни я, ни вы не знаем. Потому судить о том, выполнено оно точно, или не очень точно - никакой возможности нет. Если система заведомо проектируется так, что "завтра размер входных данных увеличили" - норма жизни, то вообще-то это должно быть отражено в ТЗ. И если это действительно так, то ТЗ не выполнено. Но давайте честно - а это правда норма жизни? И потом - ну хорошо, увеличили. Словили ошибки, словили даже SEGFAULT. Эка невидаль. Именно они нас моментально и приведут к проблеме. Которую ровно так же можно будет решить. Еще и разными способами. Ну и в любом случае - изменение размера входных данных это новая версия ПО или новая ревизия аппаратуры.
Вообще, симптом повышенной тревожности из реальной жизни так вот и перебирается в код. Там ключевые проблемы ровно те же. Всего-то надо принять простой факт - есть вещи, которые мы контролировать не можем. И они, так или иначе, будут на нас влиять. А перфекционизм далеко не всегда хорошо. Более того - он сильно чаще мешает, чем помогает.
Единственная оговорка, которую просто нельзя не сделать - что дозволено Юпитеру, то не дозволено быку. В том смысле, что подходы к проектированию отдельной конечной системы, безусловно отличаются от подходов к проектированию универсальных библиотек. И да, тогда все эти избыточные (местами параноидальные) проверки становятся адекватной ценой за универсальность. Впрочем, чего уж там - "пластмассовый мир победил" (с) И теперь, вместо соединенных шкантами деревянных кубиков с прибитыми к ним гвоздями деревянными колесами, мы, даже в самых малых проектах используем типа детских машиной, используем Лего. Со множеством его соединений, за одно утаскивая в проект завод по производству этих самых кубиков, хим. предприятие по производству пластика для них, ну и скважину с нефтью чтоб это все работало. И естественно контроль, контроль, контроль...
Все ваши примеры содержат исходно неправильную строку. Тот самый "мусор на входе".
Ну да. И защиту от SQL-инъекций делать не надо. Ведь никто не может назвать ребёнка Robert'); drop table *;--.
Если система заведомо проектируется так, что "завтра размер входных данных увеличили" - норма жизни,
А если сегодня стандарт HTTP говорит, что лимит URL 1024 символа, то должно ли в ТЗ быть предположение, что завтра новая версия стандарта сделает его 16384 символа?
Ох и тяжело, наверное, с таким именем будет ребенку (с) Впрочем, сути это не меняет. Главная причина - мусор на входе. И вопросы к тому, кто этот мусор допускает. А все три варианта решения ее описаны выше.
По второй части вопроса - о как давно он стал 1024 символа? А правда 16К - это уже вот прямо завтра? А какова вероятность того, что послезавтра будет 64К? С чем это связано? Не заставят ли эти изменения нас кардинально пересматривать архитектурные решения?
Одним словом проектируйте систему, выполняющую заявленные требования. И если каждый хорошо сделает свою работу - проблем не будет. Если в требованиях URL - 1K, то когда он станет 16K выйдет версия 2.0, в которой именно эта часть наверняка окажется самой неважной.
Числа просто для примера. HTTP изначально не вводил никаких ограничений на длину URL, оставляя вопрос разработчикам серверов. Позднее добавилась рекомендация поддерживать не менее 8000 символов. Ну и первые браузеры обижались уже на 512 символов в адресной строке, а сейчас и 16К может прийти.
Но в целом, увеличение размера входных данных это обычное дело. Те же имена файлов из 8.3 стали длиной 255/510 байт, в зависимости от ОС и ФС.
В этом примере практически все проблемы решаются добавлением нескольких строк
Hidden text
void parse(char* query) {
char key[256];
char value[256];
char ch;
int i = 0, j = 0, reading_value = 0;
key[0] = value[0] = '\0';
while (ch = *query) {
switch (ch) {
case '=':
if (reading_value) break;
key[j] = '\0';
j = 0;
reading_value = 1;
continue;
case '&':
value[j] = '\0';
printf("pair %s = %s\n", key, value);
key[0] = value[0] = '\0';
j = 0;
reading_value = 0;
continue;
}
if (j > 254) continue;
if (reading_value)
value[j++] = ch;
else
key[j++] = ch;
query++;
}
value[j] = '\0';
printf("pair %s = %s\n", key, value);
}
Еще раз - проектируйте систему.
Вам персонально сколько символов в URL надо для конкретной задачи? Это всех современных браузеров устроит? Что будем делать с теми, кого не устроит? Что будем делать с теми, кто превышает? Что с теми, кто недокладывает? А поняв это - поймем и что в ТЗ написать.
То же и 8.3. Что кто-то запретит мне прямо сейчас использовать этот формат? Если он всех устраивал, то какой смысл было рассчитывать на что-то другое? А если нет, то на уровне системы надо думать как быть с теми, кто может только 8.3. И вариант "запретить" далеко не всегда плохой.
Решайте проблемы заказчика, а не создавайте ему новые. Именно в этом залог хорошей системы. Критическая инфраструктура не должна контактировать с недостоверными данными. А любые избыточные действия - это потери. В деньгах, времени, скорости, или чем-то еще. Потому просто грамотно проектируйте систему в целом (если это в вашей власти), или просто хорошо выполняйте, но ровно то, что написано в вашем ТЗ (и не считайте себя единственным умным человеком на планете).
И что? Если это CGI, то код рухнет в своем треде и сервер вернет 503. На радость всем нормальным людям, которые умеют оценивать область применимости перед тем, как задрачивать никому не нужные проверки.
Если это CGI, то сервер должен вернуть 400 Bad Request. 503 Service Unavailable тут никоим образом не подходит.
Кому не подходит? Пуристам с ОКР?
И зачем тогда вообще нужны коды ошибок? Достаточно же при любой падать вообще ничего не возвращая. И сразу будет понятно - раз ничего не вернулось, значит кто-то где-то ошибся.
А вам подойдёт, например, компилятор, который при ошибке в коде будет не выдавать сообщение об этой ошибке, а просто падать в SEGFAULT. Зачем лишние проверки в компиляторе писать? Раз он упал - значит у вас ошибка. Или в нём ошибка. Или с железом проблемы. Ищите.
Коды ошибок в вебе для браузера не нужны никому. Источником спора является именно такой код. Оператору Мозаика даже нигде не показывали, 400 там, или 503. Даже сейчас, если вы «куда-то нажали и оно всё исчезло» — вам глубоко фиолетово, 417 там, или 500 (если, конечно, вы не чай заваривать собрались).
Коды ошибок нужны в автоматизированном общении, которого во времена показанного кода фактически не было (уж не по HTTP — точно).
Пример с ошибками в коде, который вы путаете с ошибками в самом компиляторе (в рамках этого примера) я даже обсуждать не вижу смысла.
Коды ошибок нужны в автоматизированном общении, которого во времена показанного кода фактически не было (уж не по HTTP — точно).
Те коды ошибок, которые были в самой первой имплементации HTTP, были как раз нужны для человека. Там не было ничего сокровенного:
Неправильный запрос
Нет такой страницы
Вы не аутентифицировались
У вас нет сюда доступа
Проблема со связью
Сервер выключен на обслуживание
Сервер глюкнул
Все они объясняют человеку, что ему надо делать - проверить синтаксис своего запроса, ввести логин/пароль, попросить у админа дать доступ, подождать или обратиться в техподдержку.
Вы такие правильные вещи говорите, аж зубы сводит. Ну и как Мозаик… Ладно, Нетскейп… Ладно, IE… Ладно, Chrome…
Как же они, любопытно было бы узнать, сообщают пользователю о том, что случилось «417 I am not a Teapot»? А? Скриншотик бы прям помог понять, дяденька.
Как же они, любопытно было бы узнать, сообщают пользователю о том, что случилось «417 I am not a Teapot»? А? Скриншотик бы прям помог понять, дяденька.
Так вы хотя бы определитесь, что вы хотите спросить, про то, как пользователю показывались ошибки CGI в Мозаике, или как показываются сейчас? Если именно про 417, то это уже про "сейчас", эту ошибку обработает что-то в недрах фронтэнда и выдаст юзеру адекватный ответ.
Это вы определитесь, мы по-прежнему обсуждаем код из текста, или у нас магическим образом появился какой-то еще «фронтэнд».
Никто не запрещал в 1969 году, когда только появился ARPANet, отдать 417. Н-и-к-т-о. Вы два комментария тому назад сообщили:
Те коды ошибок, которые были в самой первой имплементации HTTP, были как раз нужны для человека.
Так не стесняйтесь, ответьте на простой вопрос: как же именно осуществлялась коммуникация с тем самым мифическим человеком? Который с этой стороны браузера. А с той — код из текста, который (о! ужас!) вернул 503 вместо 400. Какую именно пользу извлечет человек из того, что куча громоздкого кода вернет кошерные 400? А?
Никто не запрещал в 1969 году, когда только появился ARPANet, отдать 417. Н-и-к-т-о. Вы два комментария тому назад сообщили:
Эм... ну как вам объяснить? Вот есть протокол. Это своего рода стандарт. Вы можете ему не следовать, и возвращать какие-то произвольные ответы, которые вам пришли в голову, но вас на другой стороне не поймут. Ответа 417 в протоколе хттп изначально банально не существовало. В 1969-м не не существовало, впрочем, ни хттп, ни даже оборудования, способного адекватно его реализовать, но это отдельный вопрос.
Так не стесняйтесь, ответьте на простой вопрос: как же именно осуществлялась коммуникация с тем самым мифическим человеком?
Так я вам объяснил. Ошибка 503 означает "сервер на обслуживании, посидите и подождите, пока не заработает". Ошибка 400 означает "проверьте параметры вашего запроса, там ошибка, исправьте и отправьте повторно". Что тут непонятного? Да, сейчас подростку в тиктоке это не очевидно. Но в интернете 1996 не было тиктока, там большинство пользователей были или ИТшники, или опытные пользователи ПК.
Я знаю, что означают эти ошибки. Я не понимаю, почему вы думаете, что пользователь отличит 503 от 400. Посмотрите хотя бы, что показывает энджин-экс в обоих случаях, или апач. Покажите вашей бабушке. Попросите объяснить, в чем разница.
Не нужно только опять рассказывать про «фронтэнд». Код на си из текста — он явно не с «фронтэндом» общался.
Покажите вашей бабушке.
Ну причём здесь моя бабушка? Бабушки и дедушки уже некоторые мои одноклассники/однокурсники, мне, правда, не довелось. И да, мы как раз прекрасно понимали, в чём разница между 400 и 503, пусть не в Мозаике, ну так в Нетскейпе.
Посмотрите хотя бы, что показывает энджин-экс в обоих случаях, или апач.
А нас тогда фирменными страницами ошибок не баловали. Браузер сам выводил свою страницу с информацией об ошибке крупными буквами.
У нас разные нетскейпы были, кажется. Код открыт, можете сходить да посмотреть, как нетскейпы до навигатора обрабатывали коды ответа (спойлер: никак).
как нетскейпы до навигатора обрабатывали коды ответа
Не уверен, что у меня получится посмотреть, как нескейп обрабатывал код ответа до навигатора, ввиду отсутствия каких-то других их публичных продуктов до навигатора. А навигатор - вполне себе обрабатывал, собственно, им же практически все и пользовались, у кого был инет в 1995-97 годах.
Вика же есть, господи. Первая версия называлась Mosaic Netscape.
им же практически все и пользовались
Щито? Все — это кто? Два с половиной гика, которым было не лень сходить купить CD с навигатором, и у которых был CD-привод? Серьёзно? Или они его качали с чёрных зеркал на 14400? Да, IE2–3 был тем еще подарочком, но никто не заморачивался. Я работал в конторе, у которой было оптоволокно под Атлантикой, но меня бы просто уволили за NS на компьютере, а стоил он, напомню, под полтинник. Многие люди брезговали воровать даже тогда.
Вика же есть, господи. Первая версия называлась Mosaic Netscape.
Ну да. Я же написал:
ввиду отсутствия каких-то других их публичных продуктов до навигатора
...а вы уже аж исходники её предлагали посмотреть :)
Щито? Все — это кто?
Все, у кого был интернет. У кого не было, не пользовались, естественно.
Да, IE2–3 был тем еще подарочком, но никто не заморачивался.
Вы, наверное, считаете, что IE2-3 был тогда прямо как сейчас, поставил винду, и вот тебе дефолтный браузер? Нет, такого не было, в винде не было никакого браузера, поэтому его надо было отдельно искать, и да, это был CD с навигатором. Предустановленный браузер в винде появился только в некоторых бандлах Вин 95 OSR2, и засел в стандартной поставке по дефолту и бесплатно уже в Вин 98.
Я работал в конторе, у которой было оптоволокно под Атлантикой, но меня бы просто уволили за NS на компьютере, а стоил он, напомню, под полтинник.
Ну ок, а за пиратский IE, который тогда тоже надо было покупать отдельно, почему вас не уволили?
Вы, наверное, считаете, что IE2-3 был тогда прямо как сейчас, поставил винду, и вот тебе дефолтный браузер?
Да, потому что я помню те времена, а не спрашиваю о них у кривой ллмки. IE3 был частью системы из коробки, IE2 — доступен для бесплатного скачивания. С конца 1995 — тоже входил в поставку (это я сейчас нагуглил, этого я не помню уже). Платным IE не был никогда. Микрософты по этому поводу почти десятилетие судились со всеми подряд.
а за пиратский IE, который тогда тоже надо было покупать отдельно, почему вас не уволили?
Если предположить, что за IE надо было бы платить (что не так), то программистская контора — это производство софта, выделенный канал — это аутсорс, а значит — у нас была полная подписка на MSDN. Я бету первого интердева даже ковырял оттуда, опыт, которым мало кто может похвастаться.
Да, потому что я помню те времена, а не спрашиваю о них у кривой ллмки. IE3 был частью системы из коробки,
Значит, совсем плохо помните. IE4 был частью системы из коробки. IE2 был платным, про цену IE3 не помню, но помню, что он либо отдельно ставился, либо надо было специально покупать винду с IE3 в бандле.
а значит — у нас была полная подписка на MSDN.
Вполне вероятно. Но представьте себе, подавляющее большинство пользователей Интернета ни тогда, ни сейчас не имели полной подписки MSDN. И поэтому у нас тогда не было IE. А пользовались тем браузером, который был у всех на слуху, был раскручен и поддержан пользователями. И это был отнюдь не IE.
UPD: вот я даже уже погуглил:

О, здравствуйте, — практикующий профессионал, это редкость на данном сайте. Но вам сейчас расскажут, как для сайта ларька, торгующего сигаретами в Химках, — надежнее поднять кубер в трех регионах.
Потому что тут так принято.
Был даже устойчивый подход: если можешь сделать задачу за линейное время, делай. Если не можешь, придумай как.
И тут же пример с квадратичной сложностью...
Что раньше, что сейчас инженеры решают задачи исходя из ТЗ. Если вопрос быстродействия не освещен в ТЗ, то критерием выступает субъективное ощущение быстродействия на локальной машине. Да раньше не было фреймворков, но были библиотеки со своей спецификой и особенностями.
вспоминается, когда появились первые PC чего только с ними люди не делали, до этого работал в основном на mainframe, было интересно сравнить, для начала написал переводчик с английского (по просьбам трудящихся), типа прямо на видео буфере, все команды меню и промпты на русский, на прерываниях по изменению экрана, позже через модем картинку в сжатом виде и команды на другой PC, типа чтобы удаленно управлять, по сравнению с работой на mainframe это было как развлечение
Был даже устойчивый подход: если можешь сделать задачу за линейное время, делай. Если не можешь, придумай как.
А в этом вашем магическом мире — всё еще ждут волшебника, который напишет сортировку списков, да?
Смешно, как в девяностых это был просто рабочий процесс, а сегодня такой код уже как музейный экспонат выглядит
Чистые программисты на PC — забавные ребята.
От них скрыта большая часть айсберга, а они думают, что стоят на вершине горы.
Эмбеддеры изо дня в день, сколько себя помнят, продолжают заниматься этим хардкором — и конца этому не видно.
Великий раскол случился, когда в процессорах придумали MMU.
Программисты на PC получили возможность писать глючные программы, которые уже не вешают всю систему, — и у них дела пошли в гору.
А эмбеддеры остались на голом железе с общей памятью, где каждое неосторожное движение ведёт к полному краху. Приходится биться за каждый килобайт памяти и каждый процент времени CPU.
И сказано было: «сойдем же и смешаем там язык их, так чтобы один не понимал речи другого» (Книга Бытия, 11:7) — и PC-программисты с эмбеддерами перестали понимать друг друга
Великий раскол случился, когда в процессорах придумали MMU.
Эй, коллега, MMU придумали до появления РС, в процессоре х86 он появился через год после выхода РС, а в самой линейке РС - через три года после её выхода :)
Тут больше вопрос не в том, можешь ли ты своим глюком приложить всю систему, или нет, а в том, вывезет ли процессор с частотой 33МГц твою программулину, или не вывезет.
Эмбедд идёт примерно к тому же на самом деле. Конфиг периферии теперь настраиваются зачастую с помощью какого-нибудь CubeMX или его аналога, библиотеки тоже уже стараются максимально отвязать разработчика от работы с регистрации напрямую. Да и само железо куда приятнее становится, просто сравните 8051, ОЗУ которых измеряется в лучшем случае сотнями байт, и ARM, где речь уже о десятках килобайт, а то и мегабайт. Асинхронная работа отдается на откуп готовых ОСРВ.
Плохо ли это? Скорее нет, чем да. Зрелые решения отлажены годами и вероятность сломать что-то, используя их, куда ниже, чем реализуя свой велосипед, хотя и такое нередко приходится делать. В конце концов это позволяет быстро поднять прототип и уже после пуститься в оптимизацию при необходимости.
Ну да, у меня на столе многоядерный микроконтроллер с тактовой частотой 1 ГГц и отдельным AI-ядром.
А я всё равно начинаю писать прикладной софт с конфигурирования таблицы векторов прерываний. Потом перехожу к драйверам, тщательно расписываю приоритеты этих прерываний и оптимизирую обработчики по тактам.
Конфигураторы по-прежнему остаются рекламной заманухой. Да, они сделают стартовую рабочую конфигурацию. Но как только дело доходит до использования всех возможностей периферии — таймерных модулей, цепочечных пересылок по DMA от одной периферии к другой по триггерам от третьих, — весь код конфигураторов летит в топку. ОСРВ тоже, по сути, голые. HAL от производителей никак не коррелирует с ОСРВ, тупо работает на программных задержках и требует тотальной переделки.
Да, у них теперь есть разделение памяти на защищённую и пользовательскую. Мне от этого ни холодно ни жарко — ещё один повод где-то накосячить. Ни одна доступная RTOS эту фичу толком не использует.
Да, у них теперь хороший отладочный движок с трассировкой. Но это не отменяет проход по шагам и скрупулёзный подсчёт тактов. Программа сама себя не оптимизирует.
Словом, кругом пот и нервы.
Ну с MPU я сам не заморачивайся ещё, не возникало необходимости, но теоретически она как раз для ОСРВ и задумана на первый взгляд.
DMA и часть HAL ответственная за периферию уже сейчас спокойно обслуживается в контексте прерываний как в baremetal так и в ОСРВ. FreeRTOS уже предлагает вполне удобное api аппаратных прерываний. Считать по тактам приходилось наверное всего пару раз, когда с DMA на F3xx серии понадобилось вводить задержки в транзакции DMA: нужно было выжать максимум из внешнего АЦП, а DMA там не даёт настроить задержки между пакетами как в H7 например, время оцифровки никто не отменял и по итогу АЦП просто нон-стопом заваливали транзакциями на запуск, из-за чего бедолага циклически перезапускал процесс и не отдавал прерывание готовности данных в FIFO.
Я не уверен, что сейчас в целом много разработчиков, которые осилят армовый асм.
API то оно предлагает, только реализацию не предлагает. И не может предложить, поскольку это глубоко железо-зависимая вещь.
Даже когда у STM32 есть CubeMX и в нем пожно включить RTOS, то все равно HAL не будет использовать сервисы RTOS. И в ISR не будут использоваться семафоры или флаги, а будет туча callback, реализовать которые надо самому программисту.
Разве это не слёзы ?!
А ведь могли бы за столько лет уже сделать HAL связанный с RTOS. Этакий фреймворк.
Что то похожее лепит Arduino. Но сразу теряет свою аудиторию из-за сложности концепции многозадачности. О синхронизации и тактах то все равно юзеру надо думать.
О тактах в embedded не думают только от того что это реально трудно и действуют на авось. Во многих случаях прокатывает. Но в нормальной системе разработчик наизусть должен знать сколько времени у него обрабатываются прерывания от каждой периферии и сколько у него мертвого времени, когда прерывания запрещены и какой джитер прерываний он может получить.
Про MPU тоже я еще не упоминал. А говорил о Trust Zone. А MPU не может дать существенного удобства поскольку ограничено количеством зон. это еще один головняк, и потеря ресурсов на переключение.
"будет туча callback, реализовать которые надо самому программисту"
Это далеко не слезы) Как минимум, коллбеки сами обрабатывают флаги большинства периферии (за исключением коллбеков ошибок, там да, надо ручками почитать стейты и обработать их). А, если прямо жмет, то лучше вообще обойтись без них и ручками написать свои, исходя из заложенного функционала, чтобы не тратить время на проверку флагов, которые никогда в данной конфигурации не появятся вовсе.
Количество тактов по-честному все равно нужно считать после трансляции в асм. Либо закладывать везде счетчики тиков и понимать, на чем реально они съедаются (пока опустим даже работу стека в режиме вложенных прерываний). Плюс не забываем про конвейер в АРМ, который с теми же nop ассемблерными вставками может вести себя по разному. А вот барьеры памяти, кеширование - действительно первое время вызывают боль, но это уже касается высокопроизводительных ядер, у того же Cortex-M4 кеш работает предсказуемее и иначе в отличии от М7 и надо понимать как организовать доступ к данным тому же DMA.
Так что жить стало определенно проще. Я начинал с 8051 и его ассемблера, и возвращаться к нему после АРМ и Си вообще не хочется)
Как минимум, коллбеки сами обрабатывают флаги большинства периферии (за исключением коллбеков ошибок, там да, надо ручками почитать стейты и обработать их).
Callback сами ничего не обрабатывают.
Это забота программиста их реализовать.
Просто эти реализации должны находиться внутри спец функций объявленных, но не реализованных в API.
Пишу так подробно для PC программистов, которые и не подозревают о таких артефактах, приносящих дополнительные страдания.
Самый треш в том, что для этих сallback никогда нет четкой спецификации - из каких именно мест они вызываются, из ISR или из обычного потока. И сколько там в нашем распоряжении будет стека и т.п.
Хорошо если HAL открыт в исходниках, хотя там встретит стена из макросов и косвенных вызовов. А если это закрытая либа какого-нибудь управления моторами от ST? И при этом она должна работать в жестком риалтайме.
Это что? Это боль!
Как смотреть тайминги ISR и их последоавательности, проблем нет. Для этого в C-Spy есть Timeline, где все они видны с точностью до долей микросекунд. Ничего дополнительно в код вставлять не надо. Если надо еще точнее, то есть трасировка в Ozone.
Проблема в огромном количестве номенклатуры прерываний.
Эт все равно что PC-шника заставить помнить все прерывания на PCI шине или хотя бы думать о них или хотя бы знать о такой шине.
Но конечно, есть embedded-ы, которые ни с чем в своей жизни кроме UART, I2C и SPI не работали. У них может быть свое представление о реальности. Тут не поспоришь. Вселенных есть много.
Не путайте callback и сам вызов обработчика прерывания, у STM callback'и это буквально функции вызывающиеся HAL кодом из ISR и они сами читают и сбрасывают флаги в большинстве своем, да по умолчанию они имеют weak атрибут и 0 нагрузки внутри, но логика проверки флагов присутствует всегда, а это те самые ветвления кушающие драгоценные такты. Поэтому при большом объеме прерываний их и невозможно нормально использовать, так как главное правило "обработчик должен быть компактным и достаточным" нарушается. Плюс расходы на перенос контекста в стек и обратно. Такое считается всегда для худшего варианта развития событий когда асинхронных вызовов может прилететь сразу пачкой и у каждого есть жёсткие требования по тайм-ауту. Изначальная ценность callback'ов и самого HAL - простота миграции кода между разными МК.
Все callback вызовы всегда прозрачны, независимо от того работаете вы с голым железом или через ОСРВ. Если требуется ковырять макросы ST, то это явный указатель того, что в данной части кода лучше вообще отказаться от callback'ов и написать обработчик прерывания самостоятельно, благо это не рокетсайнс. И номенклатура прерываний там не такая страшная, открываем описание NVIC и все на ладони.
Что значит
открываем описание NVIC и все на ладони.
Мануал что ли открываем? Или что открываем? Или панель в отладчике с месивом непонятных битов?
Я говорю от прерываниях в проекте, происходящих сейчас и кажду микросекунду.
А есть микрокнтроллеры где нет жесткой привязки векторов к периферии в NVIC. Там и мануал не поможет.
Про callback я не понял как вы умудляетесь обойтись дефолтными и разрулить все в RTOS. У вас их пару штук всего ? Как вы передаете ивенты в задачи?
Да имелась ввиду документация, NVIC упомянут естественно в контексте ARM, в других контроллерах контроллер прерываний может иначе именоваться.
В RTOS это разруливается отдельным api работы с примитивами синхронизации и очередями из ISR. Это конечно накладывает определенные затраты на время реакции системы и в каждом случае надо смотреть под конкретную задачу. Грубо говоря сам callback может быть в целом отвязан логикой от RTOS, но в нем все равно придется вернуть например семафор/очередь через api, чтобы дать контекст планировщику. Допустим есть несколько таймеров, callback они могут дергать один и тот же, внутри уже определяем кто конкретно его вызвал и для каждого отдельно дергаем семафоры RTOS. Только держим в уме, что помимо приоритетов самих прерываний нужно учитывать и приоритеты задач. Таким образом можно условно "параллельно" обработать несколько таймеров например, просто на каждом тике прыгая между отдельными задачами туда-обратно, естественно по итогу потратим и на один и на другой больше времени, но один при этом не будет ждать полного выполнения таски другого.
Кстати, буквально вчера случай был. Поскольку Gigadevice не очень подробно документирует свои микроконтроллеры, приходится что-то гуглить или сразу искать как там это описано у STM32. Искал настраивается MPU, перешёл по ссылке на какую-то страницу STM32, и там прямо с порога "откройте CubeMX и вот тут вот будет вкладка, там настройте чего вам надо".
Одному мне кажется, что этот чудесный "код на голых нервах" похож на кусок кала?
Сейчас и заказчик часто скажет, что не надо оптимизировать, проверять, настраивать - кати уже код на прод. Потом правда недоволен, что во время акций и распродаж сайт падает, но никогда не признает, что нужны более продуманные решения. Отсюда всё больше решений без привязки к железу, к сожалению.
Сейчас можно встретить и более погруженных в железо инженеров, но скорее всего там, где это супер критично для бизнеса/заказчика.
Спасибо за пост. Немного понастольгировала <3
Отсутствие привязки к железу - это благо. Индустрия прошла большой путь чтобы это стало возможным.
Как вариативность это хорошо. Но и совсем забывать про объемы памяти на машинах и возможности развертки приложения на имеющемся железе такое себе. Есть компании и роли, где и правда об этом думать не надо по тем или иным причинам.
Думается, что привязка к железу - это нечто другое, чем забота о расходе памяти и процессорного времени. Ведь память расходуется везде, и на любом железе может оказаться что память скушана другими процессами и её не так много как хотелось бы. А в случаях ошибок в виде утечек памяти и висячих ссылок проблема вылезет, даже если физических ресурсов вроде как было много.
Такой код сегодня назвали бы слишком приземленным.
я бы назвал такой небезопасным. Оно конечно работает в данном конкретном случае, но вот у вас появляется пользовательский ввод и понеслась.
разобрать подобный механизм вручную
не очень понятно что вы под этим подразумеваете. без использования стандарной библиотеки посплитить строчку гарантированного формата? Вроде не такой уж и rocket science. Или вы подразумеваете пояснить какого рожна этот код работает?
оптимизация была не плюсом, а необходимостью
зато безопасность была плюсом, а не необходимостью - вирусов ещё толком не было, интернета тоже и ни о каких атаках на железо тогда и не думали. Не segfault и ладно.
Вот пример кода на Pascal, который я когда-то нашел в старой подборке. Он решал задачу поиска самоповторов в массиве без дополнительной памяти. В современном мире мы бы использовали хэш, дерево, любой контейнер. Тогда же это был почти спортивный подвиг:
на 10 элементах и пузырьком сортировать не страшно. проблемы начнутся, когда у вас элементов внезапно окажется, например, пару тысяч. Не говоря уже про множественные повторы одного и того же элемента по нескольку раз, когда повторов окажется больше одного.
Нужно было думать, что произойдет, если буфер не заполнится, как себя поведет дескриптор, что будет, если операция прервется посередине
То есть о читаемом файле вы позаботились, а о дескрипторе stdout почему-то решили не беспокоиться?
Проблема с таким подходом, что вне уровня ядра такие подробности редко кому нужны, поэтому современные ЯП абстрагируют обработку подобных низкоуровневых API за некоторый высокоуровневый интерфейс, где тебе нет необходимости об этом задумываться и можно сделать то же самое более понятным кодом. И инженерное мышление тут совершенно не при чём.
Сейчас всё упирается на потребности бизнеса. А многим бизнесам просто нужен стабильно функционирующий проект и, чтобы было потрачено по возможности минимум денег на разработку. Поэтому есть такая острая необходимость в фреймворках. Понятное дело, что автоматизация часто имеет свои побочные эффекты.
Иногда я ловлю себя на странном ощущении: будто многие современные инструменты исполняют роль такого мягкого пуха, через который мы перестали чувствовать остроту реального программирования.
Не знаю кто там что перестал чувствовать, но я сейчас настраиваю Memory Protection Unit на GD32H7. А до этого разбирался как работает EXMC, TLI и IPA. И всё руками, всё руками. Так что остроту чувствую в полной мере и каждый день.
как часто вы сегодня встречаете инженера, который способен без страха разобрать подобный механизм вручную?
Ваще то яндекс просит именно такую шляпу писать на собеседования в блокнотике :)
Люди, которые писали код на голых нервах. О практике девяностых и нулевых, которая до сих пор работает