Большинству пользователей известно, почему возник Вомбат. На этом я не стану заострять внимание.
А хочу обратить внимание на сложность разработки подобных сайтов. Даже на уровне MVP (минимально жизнеспособный продукт).
Даже если есть готовые наработки, то в любом случае надо продумать: модель, то есть схему базы данных, дизайн веб-морды, общее взаимодействие, то есть бизнес-модель,...
Независимо от всего разрабатываю своего бобра (это скорее будет что-то вроде Хабра, а не очередной аналог Пикабу). И уже раз десять сносил БД из-за невозможности внести изменения (миграции). Ну, может я такой рукожоп и просто не знаю как правильно сделать. 🤷♂️
Нужно продумать внешний вид сайта. Тут сильно помогает такой инструмент как Figma. Да, дизайнеры Figm-ой занимаются. 🙃После пары настроек пользоваться одно удовольствие. Но мало задизайнить. Надо это ещё и в код перенести. А это уже вёрстка. Довольно нудное занятие.
В общем, чего хотел сказать, захотите сделать свой Пикабу с блекджеком и плюхами, то готовьтесь к тому, что заебётесь...
Всем доброго времени! Наткнулся я не так давно на вот такое изображение:
И решил порассуждать не тему того, а плоха ли такая ситуация и как вообще бывает, как идет разработка backend и frontend в вещах которыми я занимался, возможно кому то будет интересно, а кому то захочется подискутировать со мной)
Во первых о понятиях, что такое "Бекенд" и "Фронтенд", я работал с разными разработчиками и даже студиями и понял что иногда эти понятия отличаются у разных людей (особенно когда речь идет о том как HR их понимает), по этому поясню что я в них вкладываю:
Frontend - "передний конец", это весь визуал который видит человек пользуясь программным обеспечением, а так же скрипты которые исполняются на стороне (на устройстве) пользователя.
на примере сайта, это его верстка, а так же javascript который перелистывает слайды, раскрывает выплывающие окна и т.д.
Backend - "задний конец", это всё что исполняется "под капотом" программы, в случае сайта это на сервере, в случае какой то настольной программы, внутрипрограмные механизмы, например обращающиеся к каким то функциям операционной системы и производя какие то расчеты и действия
на примере сайта, это его серверная часть, механизмы которые к примеру работают с базой данных, получают из неё пост для страницы на которой находится пользователь, и к примеру комментарии для этого поста и т.д.
А теперь как раз о том, что разрабатывается первее и почему.
Возьмём вебсайт, вы например хотите что бы вам сделали интернет магазин, обычно этапы разработки выглядят так:
- Создание и утверждение дизайна
- Верстка дизайна
- Подключение к верстке т.н. "Движка" или cms или "системы управления контентом", или же что очень редко и индивидуально, написания этой самой системы с нуля в порядке индивидуальной разработки и внедрения её в вёрстку.
т.е. по сути тут выходит что разработка Бекенда идёт после Фронтенда - и получается что как будто то что показано в меме это естественный ход событий и ни каких вопросов это не может вызывать?
Стоит сказать что последовательность работ которую я описал, это обычно усреденная разработка, где люди не сильно запаиваются составлением подробных ТЗ. По ней выходит то что когда готов дизайн, и на основе него вёрстка, это всё передается программисту работающему над Бэекендом, и он сразу видит, что и где ему нужно будет подключать и в каком виде: "Здесь у нас поиск, с возможностью сортировки и выбору диапазона дат постов", "Тут мы выводим чат, который обновляется каждые пару секунд" и т.д. Программист сразу визуально видит какие данные в какие элементы интерфейса ему нужно выводить, от чего ему более понятен план работ, по тому что он "визуален".
Но предположим что сроки разработки сжаты, и требуется что бы разработка обоих частей проходила одновременно, в таком случае необходимо очень точное техническое задание, где будет подробно прописано то как будет работать бэекенд, какова будет структура базы данных, какие данные при каких обстоятельствах будут из неё извлекаться, каким образом обрабатываться, в каком виде перезаписываться обратно и в какой части условного ещё не созданного даже в виде дизайна интерфейса отображаться.
Это довольно фантастический план, так как обычно не бывает заказчиков которые чётко знают как должен выглядеть их сервис\сайт\по и даже не всегда представляют себе точный его функционал.
Это всё зачастую формируется как раз во время разработки и утверждения дизайна, и по этому программист который с самого старта одновременно с дизайном начал разрабатывать свой бекенд, чаще всего обречен по ходу появления дизайна и вёрстки вносить коррективы в свою работу, а иногда даже переписывать её часть.
Но есть например аспекты которые можно на мой взгляд проработать и без дизайна, например структуру базы данных и её таблиц, создать какие то общие функции, провести базовую настройку движка или фреймфорка с которым будет работа.
Но всё же когда дизайн утверждён и верстка окончена и принята заказчиком, это уже гарантирует стабильность разработки. (при условии конечно если заказчику не взбредёт в голову что то на ходу изменять, но такие вещи должны быть учтены в договоре и проходить за дополнительную оплату)
Примерно то же самое происходит и при разработке приложения для телефона.
Программисту всегда проще подключать бекенд к чему то готовому, чем прорабатывать так называемые API (простым языком объясняя это точки доступа через которые интерфейс получает данные из бекенда) почти в слепую или по скупому ТЗ (я просто ещё не видел по настоящему подробных ТЗ за исключением тех которые в своей педантично душной манере составлял).
По этому становится очевидно что это вполне себе нормальная ситуация, когда Фронтенд давно готов, на него можно посмотреть, потыкать, а Бекенд ещё только подключается, а то ещё и вовсе в стадии разработки.
Но есть один сценарий когда бекенд идёт первее фронтенда - это когда разрабатывается какое то техническое программное обеспечение у которого изначально вовсе нет интерфейса, например какой нибудь локальный сервер - пишется собственно сама програмина, а её настройка и в целом взаимодействие с ней происходит через терминал\консоль\командную строку.
В данном случае интерфейс появляется уже после того как ПО было разработано, зачастую таким программным обеспечением можно пользоваться и без интерфейса через всё тот же терминал, отправляя команды которые ты вводишь в него вручную (прям как хакер из кинофильма), а графический интерфейс создаётся уже потом(иногда очень сильно потом) просто для удобства пользователя и отображает все стандартные и часто используемые опции (для более тонких настроек за частую используется всё равно командная строка) и по сути графический интерфейс, отправляет те же самые команды что вы бы вводили в терминал, просто делает это по нажатию на кнопочки.
На этом всё, надеюсь кому то было интересно почитать эти возможно сумбурные рассуждения, сейчас попробую выловить все грамматические ошибки в этом опусе, перед тем как нажать кнопку "опубликовать пост"😅
В посте про "наставление" накидали кучу годноты + в телегу. По всему этому я пройдусь, буду иногда постить об успехах.
Некоторые глянул мельком. Поэтому если там есть продукт-плейсмент - отпишите и я удалю. На первый взгляд очень даже хорошо. Искал с нуля т.к. лучше повторить, чем вспоминать и ошибиться.
Если что-то хотите предложить - пишите в комментарии.
UPD: даже в начале написал, что я не в курсе ни о какой рекламе на тех ресурсах. И первый же начал разводить вонь о том, что я продажный и сайт вообще мой. Ууух я конечно злой гений.
Предлагайте ваши варианты, что сами пробовали. Обязательно попробую.
UPD2: я без понятия почему "это" вылетело в горячие. Не бейте блин тапком.
Всем привет! Решил немного рассказать про "рандом" (генерацию случайности) в компьютерных программах и играх.
Если вас попросить загадать случайное число в диапазоне от и до, то по видимому вы действительно выпалите первое что придет вам в голову, или второе что придёт, но в любом случае результат будет случайным и по большей части не предсказуемым.
С компьютером всё не так, компьютер не может в случайности, машина выполняет программу, у машины всё чётко, либо 0 либо 1, либо true либо false. Чего то третьего не дано, не может ни каких "может быть", "наверно" и т.д. Но тем не менее каким то образом по большей части в играх программа как то генерирует случайные числа? Если не ходить далеко то взять "броски кубика" в Baldurs Gate 3, как это происходит?
Всё просто! Генерация случайного числа, не случайна, это тоже программа! Самая простейшая программа по генерации случайных чисел, для получения момента случайности использует миллисекунды времени. По терминологии это называется "сид". (отправная точка-число для математической операции)
Грубо говоря если представить что вы нажали на кнопку "Бросить кубик", то программа в момент нажатия записывает в переменную значение миллисекунд в момент нажатия кнопки и произведя с этим числом не хитрые математические операции, выдает случайное число.
простейший пример генерации псевдослучайного числа
В приведенном коде выше, мы получаем текущее время, затем отделяем из него микросекунды, после чего приводим его к целому числу, так как оно в системе является изначально дробным. Далее операция % 100 возвращает остаток от деления на 100, что дает число в диапазоне от 0 до 99 к которому в конечном итоге мы добавляем +1 и получаем от 1 до 100!
Благодаря такой функции например можно рассчитывать вероятность выпадения предметов в игре. Если вам нужно что бы предмет падал с вероятностью в 10% то вы генерируете вышеприведенным образом случайное число и проверяете
if ($случайное <= 10) - "случайное число меньше или равно 10ти" и если условие выполнено то предмет добавляется персонажу.
Однако всё это приведено просто для примера, на самом деле в любом языке программирования, всегда есть встроенная готовая функция генерации случайного числа, при этом алгоритм генерации там ещё хитрей! Например стоят защиты от повторений, битовые сдвиги и побитовые операции и т.д. Но если упростить то всё сведется примерно тому что я показал выше.
Именно по этому нельзя назвать такую генерацию "случайной" так как в теории зная точный момент запуска генератора и его алгоритм, можно просчитать что он выдаст! С этим к слову связан один забавный случай: https://habr.com/ru/news/818415/
Вкратце: Чувак сгенерировал пароль для криптокошелька с биткойнами через генератор паролей, и по классике прошляпил пароль. Обратился к белому хакеру, который определил что генератор который он использовал генерирует случайный пароль на основе даты на ПК пользователя, откатив дату на нужное время, он начал генерировать пароли, и пробовать их, в итоге получилось подобрать верный пароль!
Когда я ради интереса делал одну мини-игрульку, очень примитивную и глупую, в ней механика поиска предметов и механика "случайных встреч" была построена как раз на случайности, много предметов и шанс выпадения высчитывается случайно. Я очень удивился тому что предметы и встречи с низким шансом выпадения начали выпадать на много чаще чем им был заявлен шанс в скрипте! Связано это было как раз с тем что генератор случайных чисел внутри компьютера, может работать не просто не случайно, а порой с довольно сильной погрешностью. Результат статистики 100 бросков 6тигранного кубика в реальной жизни, может отличаться от тех же "бросков" посредством программы. Из-за чего порой разработчики игры вынуждены по мимо всего, из "псевдослучайного числа" делать за счет "подкрутки" ещё и "псевдослучайный шанс".
Например нечто подобное есть в играх fallout 1-2 когда персонаж мажет по врагу 4 раза подряд при 75+ вероятности попадания!
На этом у меня всё, прошу прощения за возможные ошибки, надеюсь вам было интересно!
Нуссс. Начну из далека. На заре вычислительной техники народ довольствовался механическими вычислительными машинками. Не буду на них останавливаться - это тема для длинющего поста, начиная с камешков на берегу и заканчивая машинкой "Энигма".
Потом, почти одновременно, появились аналоговые вычислительные машины - АВМ. Они имели крайне узкую направленность, и грубо говоря пытались электронными, а зачастую и физическими методами повторить некий физ. процесс и получить результат/расчёт того или иного физ. процесса. Головоломка уже не для программистов, а для физиков и математеков.
штЫрьками собираем схему для нужного вычислеия - соединения подключают различные блоки интеграторов, делителей, усилителей пр. Всё должно быть выполнено очень точно. Это пик развития аналоговой электроники
Про квантовые компы я промолчу тихи в тряпочку - сколько ни читал - муть мутная и мне непонятная. Стар я видать стал, а может стал и СуперСтар...
Говорить я буду о привычных компах - которые работают с единичками и ноликами (троичные погибли, хотя в СССР были нехилые наработки). Вроде всё норм, но комп, процессор, понимает как раз енти нули и единички. Ещё на этапе проектирования железяки народ понял енту проблему и начал объединять их в группы, кратные степени двойки. Так появился байт (2^3 = 8), а потом и слово (2^4 = 16). И тут же различные производители железа придумали первый геморрой для программистов - порядок следования байт в слове. Little-Endian и Big-Endial - эти слова знакомы каждому сетевику и привычно вызывают когнитивный диссонанс...
Но ладно, вернёмся к ассемблеру. На заре программирования под цифровые процессора народ вбивал программу тумблерами - битик за битиком. Производительность труда была на наименьшейшем уровне, а вероятность щёлкнуть не тот битик самой большой.
Нервы не выдержали и мисс Кэтлин Бут решила облегчить себе работу, создав первый в мире ассемблер для ARC2 (военный авиационный вычислитель).
Эту идею подхватили остальные. Что самое интересное - на языке Ассемблера можно добиваться максимальной производительности (самая популярная библиотека ffmpeg имеет множество ассемблерного кода под разные архитектуры) - выжать из процессора все соки. Такое же происходит в различных математических библиотеках.
Вроде всё с ассемблером хорошо, кроме одного - там команды процессору заменены на мнемокоды - краткие символьные представления команды процессора. Писать программу стало удобно, понятно. Появились кросс-ассемблеры (низкий уровень llvm)
Но оснавная засада осталась - трудоёмкость программирования и половина башки программиста занята архитектурой компа (да-да, там не только про проц надо помнить, но и про периферию).
спёрто с https://blog.skillfactory.ru/glossary/assembler/
А что итого? Я понимаю, что питонисты нифига не поймут - они даже не понимают, что каждая питон-либа это просто обёртка под высокоэффективным кодом на Си. Ассемблер жив и будет жив, те же "тысяча строк" ассемблера под линуксом - это и есть основа ядра ОС. Потом уже можно писать на Си, Си++, даже Расте (чёт ОС растоманов зачахла - видать пришло понимание, что язык высокого уровня не может использоваться наравне с Ассемблером или СИ).
Язык Ассемблера, как продился, так и будет жив. Есть яыки программирования близкие к АСМу - тот же Си, или Форт, но никто не даст программисту той мощи и контроля за компом как АСМ. Тот же Си в своих диалектах везде имеет возможность АСМ-вставок.
Что же до программирования на АСМе - мне он нравится, но... Всегда надо иметь баланс в голове. Поверьте strncpy() из Си будет более оптимизирована чем ваша реализаций на АСМе, а вот к-либо формула, вычисляющая интеграл, которого нет в clib - лучше уже на асме, особенно если оно вычисляется в цикле..
Одна из частых ошибок молодых программистов — экономия времени на чтении. Читают текст недостаточно внимательно, не понимают, что требуется, как именно должен быть оформлен результат — и создают вообще не ту программу, тратя кучу времени.
Читать нужно раз 5. Даже если там 2 строчки — 5 раз хотя бы. Тогда шансов так ошибиться гораздо меньше.
Мальчик с задержками в развитии. 9 лет и кое-как умеет читать, проблемы в социальном плане, в других — я не стал вдаваться в подробности.
Пришёл этот мальчик попробоваться на кружок программирования, так как большинство остальных не подошли. Даю ему среду, где читать не надо — стрелочки, кружочки только. Задачи в духе «напиши роботу, как ему двигаться, чтобы пройти через лабиринт». Как пошёл, как пошёл! Дошёл до довольно сложного уровня. Уставал только быстрее, чем большинство, но за время работы успевал куда больше.
В общем, задержка задержкой, читает плохо, но алгоритмическое мышление огого как работало уже в 9 лет. Сейчас ему лет 13, наверное, родители любящие и понимающие. Надеюсь, всё у него хорошо поэтому, что-то технологическое или интеллектуальное осваивает
Вот говорят, что чтобы научиться программированию, надо программировать. И не задачи решать, а именно проекты какие-то реальные. А как такое начать делать, когда не понимаешь ещё ничего?
Да очень просто. Делайте прототипы реальных проектов. Вот банкомат. Ну возьмите и сделайте программу, которая пишет, что у вас на счету 5000, и спрашивает, сколько снять. Потом пишет, сколько сняли и сколько осталось. Вполне себе банкомат. Даже конструкцию if... знать не надо.
А потом можно, с получением знаний уже брать создавать более продвинутый прототип. Добавить другие варианты действий, сохранение информации, чтобы операции предыдущих запусков учитывались.
Банкомат, корзина интернет-магазина, чат-боты всякие, менеджер задач, лета социальной сети и т.д. и т.д. — любое, что автоматизировано, берёте и пишете примитивные прототипы. Потом усложняет. Так и нарабатывайте нужные навыки.
— Мой муж буквально помешался на каких-то странных теориях, - крайне раздраженным голосом заявила клиентка. – Буквально за пару лет он изменился до неузнаваемости и мне начало казаться что я живу с совершенно незнакомым человеком.
— Расскажите подробнее, пожалуйста.
— Все началось несколько лет назад, когда он посмотрел этих проклятых Мстителей, где они там дрались с каким-то мужиком с большой золотой перчаткой. Он там по фильму утверждал что вселенная перенаселена и совершенно необходимо контролировать число живущих. Щелк пальчиками и половина людей исчезла.
— И как это соотносится с вашим мужем? – спросила я, когда женщина внезапно осеклась.
— Как сейчас помню: Рома после фильма заявил что тот злодей в принципе прав, и ничего плохого в его решении нет. Мол, как минимум наша планета стала бы лучше если бы в ней не было половины жителей. Он тогда еще посмеялся, что и сам бы с радостью избавился от обочечников и риэлторов. Тогда я напомнила ему, что тот мужик не выбирал кому жить, а кому нет – он просто уничтожил случайных людей, лишь бы получилась половина от того что имелось сначала. Как бы он заговорил, если бы я бы внезапно обернулась облачком пыли.
— И что он ответил?
— Он промолчал, но видать этот разговор запал ему в душу, что после этого он то и дело начал залипать в интернете. Раньше муж нет-нет да играл в игрушки или смотрел фильмы, то теперь он начал что-то читать. Поначалу я даже обрадовалась – бросил игрушки и слава богу, даже решила подглядеть что он там читает и ничего особо не поняла. Какие-то экономические термины, цифры и расчеты – причем в таких количествах, что я бросила эту затею и на какое-то время забыла о происходящем.
— А потом муж сам сообщил вам о том, над чем работает?
— Я сидела на кухне, красила ногти – готовилась к началу рабочей недели и тут заходит Рома и предлагает попить чаю. Ну я и согласилась, а он как бы невзначай начал рассказывать про умершего несколько веков назад английского ученого по фамилии Мальтус, который разработал теорию о перенаселении нашей планеты. Вы с ней знакомы?
— В общих чертах - она гласит, что человечество прирастает в геометрической прогрессии, а производство необходимых для него ресурсов – в арифметической. Поэтому рано или поздно мы столкнемся с дефицитом и последующими за ним кризисами самого разного типа.
— Замечательно, - кивнула Ирина и немного расслабилась. – Рада что мне не придется объяснить вам ее значение. Короче, Рома заявил что теперь собирается сделать все что может чтобы мы вошли в тот самый пресловутый «Золотой миллиард». Типа теперь мы должны выложиться на максимум чтобы начать зарабатывать большие деньги и, желательно, переехать в Европу или Америку. Я подумала, что это что-то вроде шутки – куда уж нам такое: я - обычный педагог, а он – целыми днями копается в Камазах. Мы не знаем языка, да и не обладаем сколько-нибудь значимой профессией. А он только покачал головой и заявил, что у него есть план…
— Дайте догадаюсь – он решил «вкатиться» в ИТ?
— Хуже, - криво усмехнулась она. – Он решил, что вкатиться должна я, просто потому что у меня мозги лучше работают. Типа года хватит, а он уж как-нибудь прокормит нас за это время.
— И что вы решили?
— Поначалу я приняла эту идею в штыки - ведь все уже налажено: дети меня любят, коллеги ценят, да и работа мне нравится. Плюс к этому мне не особо по душе программирование, и зачем менять любимое занятие на что-то подобное, не особо понятно. Деньги, конечно, там платят хорошие – но какой в них смысл, если я через полтора года выгорю и снова вернусь к тому, с чего начала. Но потом Рома меня уговорил, пообещав золотые горы и цветные небеса. Сдавшись под его напором, я взялась учиться и, к его чести, Рома действительно окружил меня всевозможной заботой: взял на себя домашние дела и заботу о ребенке, причем так, что я даже не слышала и не видела их обоих.
— Но вы все равно не выдержали и бросили?
— Как я уже говорила, это – не мое. Через месяц я прекратила заниматься, извинилась перед мужем, что не оправдала его надежд и предложила вернуть все как было. И знаете, что он мне ответил? Ни за что не догадаетесь… Что у него есть новый план, по которому не нужно становиться программистом – оказывается, в Германии, сейчас жуткая нехватка учителей и людей рабочих специальностей. Дело за малым – выучить язык и вперед. То есть предполагалось, что теперь, я буду вместо программирования учить немецкий, а потом мой педагогический опыт будет использован в качестве возможности для переезда. Я хотела сказать ему что план не очень-то и хорош, но промолчала.
— Почему?
— Да потому что этот чертяка, несмотря на рабочую профессию – крайне языкастый! Он кого угодно уговорит что белое – это черное и наоборот. Вот и я сдалась и пошла учиться. Правда потом началась пандемия и все накрылось медным тазом и мне пришлось выйти на работу, поскольку свою Рома потерял. Но он все равно продолжает создавать все новые и новые варианты как бы нам попасть в число «избранных» и выжить в будущей катастрофе. А меня это уже серьезно бесит! Муж только и делает что говорит об этом: о том, что индусы с двухтысячного года приросли на четыреста миллионов, а в Бангладеше и Египте народу хватит на две России, и так далее и тому подобное… Ужас просто!
— А вам просто хочется спокойно жить и ничего не менять?
— Вот именно.
— Но вы же понимаете, что он делает все это ради вас и ребенка?
— Понимаю, но мы же не всесильны – может хватит уже пытаться?
— Что же, в таком случае могу порекомендовать или откровенно поговорить с ним самостоятельно или привести сюда для того чтобы я могла организовать беседу на троих. Впрочем, есть и еще один способ, довольно быстрый и эффективный, правда последствия могут оказаться ничем не лучше чем то, что есть сейчас.
— О каком способе вы говорите?
— Подарите ему пару книжек Карла Маркса – он там в пух и прах разносит идеи Мальтуса. Я сама читала, поэтому знаю.
— А что за последствия?
— Он вполне может проникнуться новыми идеями и удариться в социализм. В этом нет ничего плохого, но, возможно, конкретно в вашем случае это будет лишним.
— Да я лучше буду жить с человеком, который будет петь Интернационал, нежели с тем, что уговаривает уехать за бугор в неизвестность, - рассмеялась она. - В какой, говорите, книге это все написано…?
Живу в Америке. Ребенок в школе записался на уроки программирования. Пришел на собрание этого класса по поводу начал занятий. Огляделся. 30 человек в классе. Один азиат и мой из России, остальные индусы. Почувствовал себя нац. меньшинством.
На всех других предметах этнический состав гораздо разнообразнее.
Всем доброго здравия, уважаемые вомбатовцы (я не знаю, как у вас тут принято обращаться друг к другу, надеюсь, никого не обидел).
Кто-то из вас знает, кто-то нет, но где-то месяц тому назад мы весьма немаленькой компанией были вынуждены покинуть насиженное место. Некоторых из нас, в частности меня, заклеймили злодеем вором сайтов, но я человек простой, мне во всей этой грязи возиться лень, я пару раз какашкой кинулся. да самоустранился. «Собака лает, караван идёт».
В итоге что получилось? Решили мы написать новый проект с нуля. Когда-то давно, ещё тогда, когда был большой исход с Пикабу (как раз когда и Вомбат появился на свет). Я решил написать свою площадку. В итоге довёл её за пару месяцев до состояния, что этим можно было пользоваться, но потом работа отъела ресурсы, а когда решил вернуться опять к проекту, уже вышли в открытый доступ варианты Того же Вомбата и Капибары. Свой я отложил в долгий ящик (искренне был уверен, что забросил навсегда).
Но тут вот как получилось.
Использовать код того проекта мне показалось не самой хорошей идеей, так как и технологии устарели и я чуть умнее стал, поэтому начал писать с нуля, но оглядываясь на опыт прошлой писанины.
В качестве основы был выбрал фреймворк Laravel. Почему? Да потому что я его знаю вдоль и поперёк и на нём подобные штуки поднимаются достаточно легко и быстро.
Большой вопрос по фронтэнду. Я вроде как фулстек, но JS фреймворки, аля VUE и React считаю излишеством, тем паче, что открытого АПИ у проекта всё равно нет и не планируется. Поэтому решил совместить удобное с быстрым. И тут мне под руки попалась такая интересная фигня (ну как попалась, чат ГПТ мне её посоветовал. Говорит: «Потыкай в неё палочкой, перспективная фигня»), как Livewire.
И вот, совместив стандартный ларавелевский шаблонизатор, вот эту вот вундервафлу и обычный ванильный JS, удалось собрать проект буквально за пару недель. Пришлось правда отложить все прочие свои поделки, но тут уж как получается 🤷♂️
Ещё две недели активный тестов небольшой, но очень умелой группой пользователей. И вот сегодня я открыл сайт для общего польования.
Согласно пункту правил 10, я, вроде как могу тут делиться ссылками на свои ресурсы, но с другой стороны, этот ресурс не полностью мой, там за ним целая толпа стоит. Поэтому не буду гневить ВомбатоМодераторов. Тем более, что цели переманить людей отсюда к нам у меня нет, я просто хвастаюсь ☺
Что могу сказать по итогу: задачка интересная. И работы предстоит по её доделыванию уйма, но и опыт это прям хороший. Особенно по работе с БД. Так что, готов ответить на вопросы, поделиться, если у кого-то есть что спросить. А если кто-то вдруг ещё и шарит в описанных мной технологиях, буду раз помощи)
Не рекомендуется принимать данную статью близко к сердцу или как истину в последней инстанции. Скорее как научпоп-грелку для мозгов. Тут довольно много упрощений, упущений или просто странно написанных моментов. Если что-то объяснено совсем криво, то добро пожаловать в комментарии.
Все сказанное далее применимо везде, но детали описаны для защищенного режима x86 (только я опустил префиксEдля регистров) и языков семейства C (в основном C и C++, местами C#). Для понимания рекомендуется знать про указатели и базово представлять, что происходит в процессоре.
Приятного чтения.
Думаю все помнят, что такое функция в математике
Начнем со сложного
Глоссарий
Чувствую, что большинство не имеет ни малейшего представления об указателях
В части про x86 я упоминал о том, что процессор представляет из себя бешенный самоуправляемый калькулятор, которому указом может быть разве что хранилище кода, аппаратный сброс и немаскируемое прерывание. И что у него есть системная шина, состоящая из шины адреса, шины данных и шины управления, к которой подключена память, хранящая данные и код.
А память это огромная одномерная полоска, в которой каждая ячейка имеет свой порядковый номер от 0 до 2^(разрядность шины адреса)-1. И указатель представляет из себя самую обычную численную переменную, хранящую номер (адрес) любой ячейки.
Я не понимаю, почему для многих это настолько сложная тема, а существенное количество новичков забрасывают изучение C после встречи с ними. Они в своей сути максимально элементарны (до тех пор, пока не нужно исправлять уязвимости и ошибки, которые появились в результате злоупотребления или неполного понимания нюансов).
Также существуют ссылки, так или иначе представляющие из себя подвид указателей, но обычно с запретом на арифметику с ними (основная сложность и опасность указателей) и/или подсчетом количества активных ссылок для сборки мусора.
Все языки или имеют указатели (Pascal, C, C++, блоки unsafe в C# и Rust), или являются ссылочными (Java, C#, Python, JS, т.д. (почти все современные языки)). С++ тоже имеет ссылки, но как синтаксический сахар над указателями, призванный упростить их передачу в функции и избавиться от проблемы нулевых указателей.
Ассемблер - если вы даже примерно не знаете, что это такое, то первую часть статьи вероятно можно пролистать и перейти на обсуждение парадигм.
Регистр - именованная численная переменная внутри процессора. Может иметь особое предназначение, а может просто использоваться для хранения любых чисел и арифметики. Их немного.
Инструкция - одиночная команда, представляет из себя последовательность из нескольких байт со специальным значением. Любая программа представляет из себя последовательность инструкций внутри памяти. Указатель на инструкцию, которая будет выполнена следующей, содержится в регистре IP, который сам увеличивается после выполнения каждой инструкции. По-хорошему стоило нарисовать пошаговую наглядную анимацию с демонстрацией всех регистров и куском ассемблерного кода, но мне лень, а в гугле я ничего толкового не нашел.
Метка - константа, содержащая адрес чего-либо. К примеру процедуры или глобальной переменной. Примерно как метка для goto в высокоуровневых языках, но универсальнее. Применяются при написании на ассемблере, в процессоре как таковые не существуют и разрушаются до обычных чисел при ассемблировании и линковке.
Разыменование - операция над указателем, когда тот превращается в переменную с адресом, который был записан в указателе (своеобразный пульт Д/У для переменной).
Парадигма - "стиль" написания и постройки архитектуры программы, частично определяется языком. Технически на том же C можно писать в практически любой парадигме (через костыли можно писать в стиле ООП, через макросы реализовать метапрограммирование, а через нестандартные расширения вообще ядерный бред), но родная для него - процедурная. А Go, к примеру, хоть и имеет недоклассы и методы, но лишен практически всех благ ООП и не сильно далеко ушел от C.
Синтаксический сахар - необязательная возможность, которая сокращает количество кода, повышает его читаемость или удобство поддержки
Стек
Большинство процессоров Фон-Неймановской архитектуры в своей конструкции предлагают механизмы стека. Кто играл в покер должны вспомнить стеки фишек. То есть некие значения, сложенные друг на друга (обычно это переменные, в частности адреса в памяти). При этом основными операциями являются добавление фишки на вершину (инструкция PUSH) и ее снятие (инструкция POP). Еще можно косвенно читать и заменять (перезаписывать) фишки относительно вершины (на вершину указывает регистр SP, неявно обновляется через POP и PUSH) или основания (указывает регистр BP) вглубь.
Стеки можно переключать (но не в MOS6502), это нужно для многозадачности
Помимо хранения локальных переменных стек позволяет делать довольно интересную вещь: мы можем положить на стек все необходимые аргументы (x в математике, но их может быть несколько), адрес следующей инструкции (взяв из указателя инструкции IP, это будет адрес возврата), после чего совершить прыжок на какой-нибудь другой адрес (сохранение адреса и прыжок делаются инструкцией CALL).
А на этом адресе может быть функция. Сначала она кладет текущий BP на стек, после чего приравнивает основание к вершине (BP к SP), тем самым создав для себя "новый стек" сразу после предыдущего (это еще называется стековым кадром), в котором якобы лежит только значение старого основания стека (BP), чтобы можно было восстановить его перед возвратом.
Серое это стековый кадр от предыдущей функции
После чего функция может прочитать переданные ей аргументы относительно основания своего стека вниз (технически это будет выход за границы текущего стека), выполнить с этим какие-либо действия (записать в файл, вывести в консоль, просто перемножить) и сохранить результат (обычно результат сохраняется не на стек, а в регистр AX).
После чего функция восстанавливает старое значение BP, сняв его со стека, и выполняет команду RET, которая снимает со стека адрес возврата и совершает переход на него, тем самым переключившись на инструкцию сразу после CALL.
И эта система так или иначе перекочевала в большинство высокоуровневых языков начиная с FORTRAN. К примеру в C CALL превратился в круглые скобки, RET в return, а адреса и метки в имена (при этом без скобок они все еще являются указателями, то есть адресами).
Пример кода (к сожалению, штатного форматирования не предусмотрено):
#include <stdio.h> int pow2(int x) { return x * x; } int main() { int y = pow2(16); // Вызов функции. В y будет сохранено число 256 printf("%p", pow2); // Без скобок вместо вызова просто выведет адрес функции return 0; // Возврат нуля из главной функции означает отсутствие ошибок. }
Такой стиль программирования называется процедурным (выделение кода в блоки называется структурным). А вот называть процедурный язык функциональным совершенно неправильно, ибо функциональное программирование ≠ процедурное, они даже в разных категориях (императивное и декларативное).
А теперь скомпилируем этот код и разберем ассемблерный листинг
Важно понимать, что стек в большинстве архитектур традиционно растет от больших адресов к меньшим (и в x86). То есть для того, чтобы отодвинуть его вершину вверх, от регистра SP нужно отнимать значения, а вот для ужимания и съедания ненужных значений к указателю на вершину значения прибавляют. И для доступа к значениям относительно основания или вершины это тоже важно учитывать. Это может звучать запутанно, но через время привыкаешь и всё становится очевидным.
Надеюсь это возможно будет разобрать. Код скомпилирован MSVC v19.28 для x86 со стандартными настройками в Godbolt
Post scriptum
Это всё довольно упрощенно. Как минимум, в защищенном режиме используется больше 5 разных соглашений о вызове, которые отличаются деталями реализации. Это было описание для cdecl, обычно используемого в C. Еще часто используются соглашения pascal, fastcall, thiscall, winapi и другие. Fastcall, к примеру, избегает хранения аргументов на стеке, если их возможно передать через регистры, что улучшает производительность. А winapi отличается от cdecl тем, что функция сама очищает стек от аргументов для себя при возврате. А еще я упустил, к примеру, сохранение регистров, которые функция может перезаписать и испортить, а потому обязана предварительно сохранить и перед возвратом восстановить, передачу переменного количества аргументов (как в printf) и возврат значений шире 32 бит (которые не влезут в EAX).
Плюс сейчас мало кто компилирует ПО под защищенный 32-битный режим, а в длинном режиме (AMD64) используется пара других соглашений, основанных на fastcall и имеющих несколько отличий друг от друга.
Так процедура или функция? Или подпрограмма?
Процедурное программирование предлагает делить код на подпрограммы, которые принято называть функциями и процедурами (функция обычно является наиболее понятным, частым и обобщенным названием, поэтому я его использую).
Процедура от функции отличается только тем, что функция возвращает какое-то значение (как в математике), а вот процедура этого не делает. Не во всех языках явно есть процедуры (в Pascal есть, но не в C). В таком случае их заменяют функции, возвращающие ничего (void, Unit, undefined, None).
Хотя и тут есть свои особенности. К примеру функция, возвращающая void в C и Java является прямым аналогом процедур, как-либо использовать возвращенное значение из такой функции невозможно, ибо его нет физически. А вот Unit в Kotlin это синглтон (а-ля единственная и уникальная константа уникального типа), ссылку на который можно присвоить в переменную, но в этом особого смысла нет. Undefined в JS и None в Python тоже уникальные константы специальных типов.
Но не тут-то было
Вроде бы процедура никогда не может ничего вернуть...
Только она этого никогда не делает напрямую. При она этом может записать результат в глобальную переменную, а еще часто принимает в себя указатели или ссылки, по которым может записать результат. Это еще удобно тем, что можно "вернуть" несколько значений. Пример:
void procedure(int x1, int *x2, int *x3) { // Функция ничего не возвращает, то есть это процедура *x2 = x1 * x1; // Разыменовываем указатель и записываем по его адресу результат. *x3 = x1 * x1 * x1; // Разыменовываем другой указатель и записываем по его адресу результат. } int y1, y2; procedure(16, &y1, &y2); // В y1 оказался результат, аналогичный прошлому примеру. А в y2 куб числа.
PS: оператор звездочка при указании типа превращает его в тип-указатель, а при применении на переменную-указатель разыменовывает ее до изначальной переменной. Амперсанд превращает переменную в указатель на нее (иногда еще называется оператором получения адреса).
То есть мы вернули сразу 2 разных значения из процедуры, которая якобы ничего не возвращает. Чудеса. Подобные чудеса есть в том числе в Pascal с явным делением на процедуры и функции (плюс там это сделано немного удобнее). Хотя механизм тут отличается от того, который используется в возврате значения из функции и совпадает с механизмом передачи обычных аргументов, поэтому никакой магии.
Еще про связь с математикой
Главное отличие функций в программировании от функций в математике в том, что они могут делать что-то на стороне и не обязаны возвращать одинаковый результат при одинаковых аргументах.
К примеру функция получения случайного числа по определению не может существовать в математике, если она не принимает в себя предыдущее случайное число или зерно для его видоизменения. Или функция записи в файл, возвращающая 0 в случае успеха и другое число при провале. Ко всему прочему, такая функция имеет побочный нематематический эффект, то есть запись в файл, что тоже недопустимо традиционной математикой без высоких абстракций.
Поэтому придумали чистые функции. По сути это ограничитель, которые делают функцию полным отражением таковой в математике. Им запрещено возвращать разные значения при одинаковых аргументах (точнее запрещено всё, что может такое позволить сделать), запрещено обращаться к нечистым функциям, запрещено обращаться к тому, что не является аргументом или локальной переменной, запрещены вообще любые действия, которые могут сделать что-то на стороне (даже функция sin() в C не всегда является чистой, ибо может зависеть от состояния FPU).
Чистые функции через ключевое слово pure явно есть в D и FORTRAN (проверка на чистоту во время компиляции), а также являются основой функционального программирования.
Чистая процедура тоже имеет право на жизнь, используя механизм со ссылками (на счет указателей не уверен из-за возможности арифметики над ними).
Функциональное программирование
Это очень сложная категория, которую постоянно путают с процедурным программированием. А еще это де-факто противоположный стиль: декларативный. Традиционное императивное программирование детально описывает процесс получения результата, а декларативное сам результат, без деталей реализации (хотя разделение обычно довольно нечеткое). При этом второй типичен для языков разметки типа HTML и CSS. То есть, условно, как одна и та же операция могла бы выглядеть в императивном и декларативном стиле:
document.tags.A.color = "blue" /* Императивный (JSSS). Сделать ссылки синими */
a { color: blue } /* Декларативный (CSS). Ссылки должны быть синими */
Почувствуйте разницу.
И функциональное программирование я никогда не изучал и слишком мало о нем знаю. Так что готовьтесь к ошибкам и не воспринимайте всё за чистую монету.
Внутри чистого функционального программирования
Основано полностью на математике, все функции обязаны быть чистыми. Операция присваивания запрещена (разрешены константы), переменных в привычном виде нет. Прикольно? Очень!
Во многих процедурных языках функции и процедуры являются объектами второго класса (не путать с классами из ООП), что не позволяет их свободно присваивать в переменные, передавать как аргументы в другие функции или возвращать из них (только через указатели). Функциональные языки расценивают функцию как объект первого класса, то есть их можно, а часто нужно передавать в другие функции напрямую.
Это дает некоторые преимущества, особенно в плане безопасности и при работе с многопоточностью (по причине неизменяемости данных и отсутствия глобального состояния), но вся концепция имеет один фатальный недостаток: вы мало чего полезного можете сделать, ибо что ввод, что вывод являются математически нечистыми, а потому запрещены. Вот такое вот гениальное изобретение безумных математиков.
Функциональное и процедурное программирование. Холст, масло
Каждый чисто функциональный язык выкручивается из этого по-своему, к примеру через монады. Это позволяет им существовать вне шуток и даже использоваться на практике.
Это не все особенности функциональных языков, но одни из самых важных. Самый известный такой язык: Haskell. Функциональные F#, Lisp, ML и многие другие не являются 100% чистыми.
Смешанное функциональное программирование
Последнее время часто используются смешанные языки, к примеру вместе с процедурным или объектно-ориентированным программированием, что избавляет от ограничений математики, но дает гибкость в том, что функциями можно оперировать как с любыми другими типами данных, а еще дает много очень удобного сахара вроде замыканий и лямбд. Это C#, Python, JS, частично Java и C++ (в них нужны костыли в виде интерфейсов из одного метода или оберток над указателями).
Особенности
Функции как объект первого класса. К примеру в C# это реализовано через систему делегатов, которые представляют из себя тип-обертку для функций:
Action<string> printer = Console.WriteLine; // Action<string> - тип-делегат. Неявно создаем его объект и присваиваем туда функцию printer("Hello, World!"); // Вызываем функцию через делегат
Локальные функции: как обычные, только вложенные в другую функцию (объявленные внутри нее)
void Func1() { // Глобальная функция void Func2() { // Локальная функция Console.WriteLine("В локальной функции"); } Func2(); }
Лямбды: возможность объявить безымянную функцию посреди кода (часто удобнее локальных)
var pow2AsLambda = x => x * x; // => - оператор лямбда-выражения pow2AsLambda(5); // Вернет 25
Замыкания (можно использовать вместе с лямбдами и локальными функциями):
int someValue = 42; var pow2AsLambda = x => x * x + someValue; // someValue будет захвачено замыканием, хотя напрямую не передано pow2AsLambda(5); // Вернет 67
Особенность замыканий в том, что они могут захватить локальную переменную родительской функции внутрь себя, продлевая ей время жизни за пределы блока с кодом. После чего такую лямбду можно передать в другую функцию, которая просто так не имеет доступа к someValue (в обычных условиях someValue вообще уже будет уничтожен), а вот переданная лямбда сможет ее прочитать или записать всегда и откуда угодно. И самое интересное то, что значение этой переменной будет сохранятся между вызовами к замыканию. То есть она становится глобальной, но видимой только из функции-замыкания.
ООП
Посмотрим на индекс TIOBE по популярности языков. Фиолетовым я пометил чистые функциональные языки. Языки, имеющие возможности, присущие функциональным языкам (смешанные) - синим, процедурным - красным, а объектно-ориентированным (ООП) - зеленым. Языки, в которых нельзя объявить функцию, принадлежащую самой себе или модулю, процедурными считать не совсем корректно (Java).
Не претендую на 100% точность
Видите фиолетовые точки? И я не вижу. А вот синих 12. В то же время красных и зеленых по 16. При этом реально применяемый чисто процедурный язык всего 1: С. Остальные имеют какую-либо встроенную поддержку других парадигм.
И сейчас практически всё большое ПО пишут в ООП. Ибо позволяет хоть как-то сдерживать структурированность кода после перехода с десятков тысяч строк к сотням (и миллионам).
Внутри
ООП является развитием идеи процедурного программирования. Проблема была в том, что помимо кода в программе существуют еще и данные, которые было бы неплохо связать с соответствующим им кодом. И через некоторое время после появления процедур появились структуры (они же записи в Pascal, не путать со структурным программированием).
Структуры были удобным способом объединить несколько переменных в единое целое. Пример:
typedef struct user_struct { char *username; int userid; int reputation; } user_t; user_t someuser = { "IvanKr08", 17002, 253 }; // Инициализируем значениями someuser.reputation += 10; // Обращаемся к полю
(Вместе с этим объявляем структуру новым типом через typedef, что не делается по умолчанию в C, в отличии от C++. Один из ярких примеров несовместимости C и C++):
После чего user_t превращается в новый тип, как int или char. Можно создать полноценную переменную типа user_t (или массив такого типа), а потом обратиться к какой-нибудь ее части (полю) через оператор точки (.). Можно сделать функцию, которая будет принимать или возвращать переменную типа user_t (к примеру someuser). Можно сделать указатель на user_t, тогда для обращение к полю используется оператор стрелки (->). Или можно встроить переменную типа одной структуры в другую, это тоже не запрещено. Внутри структурная переменная представляет из себя последовательно слепленные поля в одно целое.
Если поля разного размера, то часто добавляют пустоты между полей в целях выравнивания по размеру наибольшего поля и улучшения производительности, но это сложная тема
Возвращаемся к ООП
Суть ООП в том, что помимо полей структуры могут содержать процедуры и функции. Такая структура называется классом, переменная класса - объект, а процедура класса - метод.
Метод принципиально ничем не отличается от любой процедуры или функции, кроме того, что неявно принимает в себя специальный аргумент this (в Python это делается явно, в некоторых языках вместо this может быть self). Он содержит в себе ссылку/указатель на объект класса, от которого был вызван. Пример:
class User { public: char *username; int userid; int reputation;
void print() { printf("Пользователь \"%s\" (%i). Репутация: %i\n", this->username, this->userid, this->reputation); // Постоянно писать this-> не обязательно. Если локальной переменной с таким именем нет, то будет произведен доступ к полю } }; User someuser = { "IvanKr08", 17002, 253 }; // Инициализируем значениями someuser.print(); // Вызвали метод, print() неявно получил в себя указатель this, который указывает на someuser someuser.reputation += 10;// Обращаемся к полю someuser.print(); // Теперь вывод поменяется
Да, никто не мешает объявить обычную функцию, которая будет явно принимать в себя указатель на User и ничего не поменяется, и так повсеместно делают в C (тот же WinAPI на этом построен целиком и полностью), но ООП на самом деле крайне сложная, большая и холиварная тема, которая развивалась на протяжении 60 лет в разных направлениях и которую поднимать тут глупо. Только опишу еще несколько разновидностей методов:
Статичный метод - как обычный метод, только не получает в себя this и вызывается от имени класса, а не объекта (не someuser.method(), а User::method()). Зачем это нужно и в чем отличие от просто функции? Статичный метод можно спрятать за инкапсуляцией, плюс он привязан к классу, а не болтается в глобальном пространстве имен.
Конструктор - автоматически вызывается при создании нового объекта. Может иметь аргументы, тогда обязан явно вызываться как функция с именем класса, т.е. User("IvanKr08", 17002, 253)
Деструктор - есть в C++. В C# называется финализатором и имеет несколько важных отличий. Автоматически вызывается перед уничтожением объекта (к примеру выход из области видимости, явное удаление оператором delete или удаление сборщиком мусора в C#). Не может иметь аргументов.
Свойство - есть в Delphi и C#, куда перешел от первого. С точки зрения синтаксиса это поле, которое можно читать и записывать, только вместо прямого обращения в память вызываются соответствующие методы (set и get). Пример: ... int SomeProperty{ get =>42; set => Console.WriteLine("Set"); } ... test.SomeProperty = 10; // Значение никуда не сохранится, будет выведено "Set" в консоль Console.WriteLine(test.SomeProperty); // Всегда будет 42
Перегрузка оператора - специальный метод, который позволяет переопределить стандартные операции для типа. К примеру нельзя сложить два объекта User или User и число, но если перегрузить оператор +, то можно будет определить свою логику для этого (не обязательно, чтобы оно что-то реально складывало. Это может быть любое действие).
Индексатор - синоним перегрузки оператора квадратных скобок. Объект начинает вести себя как массив.
Функтор - перегрузка оператора круглых скобок (иначе называется перегрузкой оператора вызова функции). Позволяет "вызывать" объект так же, как и функцию. Звучит запутанно, но можно погуглить. Есть в C++. Также бывает функтор в функциональном программировании, но он не имеет ничего общего с функтором в C++.
Виртуальная функция (метод) - самый интересный и сложный тип, составляет основу полиморфизма. Вкратце объяснить его невозможно, но одним из ключевых принципов ООП является наследование, то есть один класс (наследник) копирует в себя все поля и методы другого класса (родитель), и может добавить новые. И суть в том, что указатель (ссылка) на объект класса-наследника может быть присвоен в переменную-указатель родительского типа. А самое интересное то, что любые обращения (к примеру вызовы методов), сделанные через родительский указатель, будут вести себя аналогично обращениям, сделанным напрямую через указатель с типом наследника. Это называется полиформизмом. Но это было бы не слишком полезным, если бы не возможность объявить метод в родительском классе виртуальным, а в наследнике полностью переопределить его код.
И то, что я забыл
Послесловие
Если вы осилили эти 3000 слов и даже смогли что-то понять, то найдите и распечатайте себе утешительную грамоту. А я устал и пойду искать смысл жизни...
UPD: исправлены косяки форматирования, очепятки, добавлено еще пару пунктов про разновидности методов и функциональное программирование.
Не совсем i8086. Источник: http://ic.onidev.fr/map/AMD_9586A.html
Думаю, что ни для кого не секрет, что подавляющее большинство современных ПК используют архитектуру, которой скоро исполнится 50 лет. Ее современный вариант заметно отличается от того, что было в 1978 году, но при этом сохраняет практически полную двоичную совместимость (современные ПК без особого труда запускают MS-DOS, проблемы начинаются при работе с периферией). Я попытался собрать наиболее ключевые особенности, этапы эволюции и поколения архитектуры.
Вступление
1978 год. Произошло несколько политических революций, сменилось трое римских пап, открыли первый спутник Плутона, многие еще не родились, а Intel выпустили 16-битного наследника i8080: i8086, который в последствии практически полностью вытеснил другие архитектуры из потребительских ПК и стал серьезным шагом к стандартизации.
Рынок ПК на тот момент и еще в ближайший десяток лет был слабо похож на современный. Было много относительно бюджетных машин на MOS6502 (Apple I, Apple II, разные Commodore) и Z80 (ZX Spectrum), к середине/концу 80х начали появляться машины на заметно более совершенном и 16/32-битном Motorola 68K (тоже очень интересная архитектура), но общее у них было ровно одно: абсолютная несовместимость ни с кем и никак. Нет, появлялись +- совместимые между собой серии по типу Amiga или Macintosh, но они были проприетарными, а в конечном итоге загнулись (Amiga перерождалась, но в итоге умерла. Macintosh выжил только благодаря удаче и iMac, в последствии перейдя на x86 на много лет).
Причина: IBM-PC.
Про i8086
Во-первых, крайне краткий и упрощенный экскурс в работу наиболее типового процессора Фон-Неймановской архитектуры.
По факту процессор представляет из себя бешенный калькулятор, который последовательно выполняет различные операции и преобразования над числами, попутно управляя самим собой. Для этого у него есть
Многое поменялось, но суть осталась
1. Шина и память. По сути для процессора это одно и то же. Их можно представить как длинную полоску из нумерованных ячеек, способных хранить число от 0 до 255 (представьте себе швейный сантиметр), что не всегда верно (физически шина и память устроены сложно и разделены на много устройств, но при связи по шине они обычно превращаются именно в одномерную ячеистую полоску). Для чего нужна память понимают, наверное, все.
Вместе с этим там же обычно сидят периферийные устройства, к которым можно обращаться так же, как и к памяти. Пункт диапазонов памяти в диспетчере устройств Windows по сути и отвечает за то, как устройства делят эту шину между собой и памятью.
Шина состоит из шины данных, адреса и управления. Ширина шины данных это одна из ключевых характеристик, которая определяет битность процессора, еще от нее сильно зависит скорость работы с устройствами и памятью. По ней передаются, как ни странно, данные. Шина адреса представляет из себя индекс в полоске ячеек, по которому требуется произвести действие. А шина управления используется, к примеру, для выбора между чтением по адресу и записью.
2. Регистры. Маленькие именованные кусочки памяти внутри процессора. Самое быстрое и легкодоступное, что у него есть (современная разница в скорости по сравнению с памятью примерно как между взять карандаш из ящика (10 секунд) и поехать за ним в магазин (пол часа-час)). Бывают двух ключевых типов: общего назначения и особого (специального) назначения.
Первые обычно имеют размер машинного слова и используются как хранилище операндов для операций. К примеру 2 регистра могут использоваться как слагаемые, после чего в первый будет помещена сумма (особенность многих архитектур в том, что они не могут явно задействовать больше 2 регистров. Частое исключение - FMA). Ко всему прочему, регистры обычно выступают посредником при чтении/записи памяти (опять таки, зависит от архитектуры, но в некоторых вообще запрещены операции напрямую с памятью без предварительной загрузки всего в регистры, другие разрешают только один аргумент для операций брать из памяти). Таких регистров относительно мало, обычно от 4 до 32.
Название вторых крайне общее, ибо все они имеют абсолютно разные предназначения. Чаще всего встречается регистр флагов, в котором каждый бит отвечает или за состояние процессора, или за результат логической операции (для этого обычно есть инструкция CMP, которая вычитает одно число из другого (отбрасывая результат) и заносит в регистр флагов статистику: первое число было больше, меньше, равно, т.д. Потом этим может воспользоваться инструкция условного перехода). Еще есть стековые регистры и регистры, уникальные для архитектуры, но этот экскурс и так слишком длинный.
Самый важный и присутствующий везде регистр: указатель инструкции. Указывает, в какой ячейке памяти находится выполняемая инструкция. Самостоятельно увеличивается после выполнения каждой инструкции, но может быть явно перезаписан инструкцией условного или безусловного перехода на адрес (if-else в языках высокого уровня).
Если вы не закрыли пост, не уснули и за вами не приехала дурка, то продолжаем.
А теперь конкретно про i8086 и x86
20 бит шина адреса, то есть мегабайт ОЗУ, 16 бит шины данных, Фон-Неймановская архитектура, CISC, аппаратные деление и умножение, 4 16-разрядных регистра общего назначения (AX, BX, CX, DX), 8-битные регистры общего назначения, физически совмещенные с 16-битными (AL, AH, BL, BH, т.д. Делят на 2 части 16-битные регистры), 2 индексных (SI, DI. Для строковых операций), 4 сегментных (сегмент кода CS, сегмент стека SS, сегмент данных DS, дополнительный сегмент ES), 16-битный регистр флагов (FLAGS), указатель инструкции (IP). Защиты памяти (MMU) нет, полноценных механизмов многозадачности тоже.
i8088 отличался тем, что имел 8-битную шину данных и технически его можно было назвать 8-битным процессором. Это его замедляло, но зато с ним можно было построить более дешевую систему на старой 8-битной обвязке.
Сегменты
Пропущенная мною часть описания процессора, так как она присуще именно x86 по причине 20-битной шине адреса. Указатель инструкций 16-битный, все операции с памятью тоже 16-битные. Естественно, что 16-битным адресом покрыть все 20 бит адресного пространства было бы как минимум проблемно, как максимум невозможно. Но надмозговые инженеры Intel выход нашли: теперь у нас есть сегменты, а все операции с памятью локальны по отношению к ним. Это создало жуткий геморрой, особенно в высокоуровневых языках (3 типа указателей: ближние, дальние и огромные), но зато облегчило портирование старого ПО с i8080, всё адресное пространство которого влезает в 1 сегмент.
По факту сегмент представляет из себя смещение для логического адреса по отношению к физическому адресу. Значение сегментного регистра умножается на 16 (сдвигается на 4 бита) и прибавляется к логическому адресу для вычисления физического адреса, который будет выдан на шине адреса. Это приводит к тому, что у одного физического адреса появляется 16 логических "синонимов".
Если вы ничего не поняли, то это нормально. Никто не понимает, а потом приходит прозрение (и ночные кошмары). Я не знаю, как это нормально объяснить. У меня есть график, но я не уверен, будет ли он читаем и понятен
Блоки это сегменты, по горизонтали физическое адресное пространство (1 мегабайт), внутри блоков логический 16-битный адрес (64 килобайта, которые можно адресовать внутри сегмента). Вертикаль показывает наложение логических адресов на физические (те самые 16 "синонимов"). При этом в i80286 возможно переполнение и получение доступа к памяти за пределом 1 мегабайта
CISC и RISC
Это легко. CISC предлагает увеличенный набор инструкций взамен на сложность архитектуры и процессора. Время выполнения и длина инструкций может быть совершенно непредсказуемой, иногда встречаются конструкции из высокоуровневых языков (к примеру строковые операции в x86. Подсчет длины строки (strlen()) можно реализовать де-факто одной инструкцией). Удобно для написания на ассемблере, часто не очень удобно для разработчиков компиляторов. Вместе со сложностью растет энергопотребление. Это x86, i8080 и M68K.
RISC же предлагает упрощенный набор инструкций взамен их максимальной оптимизации. Все инструкции должны умещаться в строго одинаковое количество байт. Вместе с этим часто запрещено брать операнды из памяти и увеличено количество регистров. Часто запрещено обращаться к памяти без выравнивания по словам. Иногда даже нет операций деления и умножения, их приходится реализовывать программно. Типовые представители: ARM, RISC-V. MOS6502 можно в некоторой степени назвать RISC, но у него только 1 регистр и один аргумент он всегда берет из памяти (тогда так можно было делать, память была примерно равной по скорости с процессором).
Есть другие варианты, такие как VLIW или шуточные MISC, URISC и ZISC. В дикой природе не встречаются, только если VLIW у "Эльбруса".
А теперь IBM
Сюда хоть кто-то дочитал?
Как гром среди ясного неба начался 1981 год, а IBM представили свой IBM-PC, использовавший i8088. И знаете, получилось хорошо. Проблема была одна: дорого (зачем выкидывать пару зарплату на какой-то электронный гроб?). Но их покупали для бизнеса, покупали просто небедные энтузиасты, причем в больших количествах. Ожидания оправдались в 9 раз.
16-килобайтная версия стоила $1,500 (не забывайте про инфляцию, это около $5000 сейчас). Apple II с 4 килобайтами на момент выхода в 1977 стоил $1,298. Но, конечно, к моменту выхода IBM-PC Apple II успел подешеветь и нарастить память, хотя отставание в производительности было колоссальным. Но простенькие машинки Commodore были многократно дешевле и до, и после.
Amiga вышла сильно позже (1985) и в начале тоже стоила неприятно, но потом подешевела и нашла своих покупателей благодаря отличному звуку и графике. Пока IBM предлагал исключительно пищалку и ядовитый EGA (а то и малиново-голубой CGA) вплоть до 87 года за много тысяч, Amiga уже в 1985 предлагала вот такое (а еще графическую многозадачную ОС), а в 1987 делала это же за $800 в базовой комплектации. Очевидно, что покупал обычный человек себе домой, не искушенный бизнесом и работой в Excel.
А теперь главная ошибка IBM, которая их одновременно и погубила, и сделала IBM-PC стандартом: они не стали закрывать архитектуру за патентами и сделали ее крайне расширяемой. Любой мог прийти и купить за небольшую сумму всю необходимую документацию вплоть до исходников BIOS, после чего начать продавать свои платы расширения или вообще компьютеры целиком, полностью совместимые с другими IBM-PC. Причем делать это стали уже через год и очень активно. Так активно, что IBM обос... профукались и в итоге к 2006 году продали свой компьютерный бизнес от греха подальше.
Но статья у нас о поколениях процессоров, так что мы летим назад в 1982 год...
i80286
Технически существовал i80186 и i80188, но они совершенно неинтересны. Не могу сказать, чтобы и i80286 был сильно интересным.
Первое существенное отличие второго поколения x86: шина адреса теперь 24 бита, то есть 16 мегабайт. А в процессоре появился новый режим: защищенный. При этом режим, в котором работал i8086, стал называться реальным. Все последующие процессоры поддерживают все режимы предыдущих, и при этом всегда запускаются в реальном, даже спустя 50 лет. Помимо этого добавили сотню новых инструкций, в основном для работы с защищенным режимом, и нарастили производительность.
Суть в том, что в реальном режиме был коммунизм: все жили равно и ни у кого не было привилегий ограничений. Но это было крайне опасно, неудобно и мешало созданию полноценных многозадачных ОС, так как любая программа могла залезть в другую и что-нибудь ей поломать, а то и влезть и сломать ОС. Даже не обязательно специально. И на перспективе ограничение на объем ОЗУ начинало переходить из космического в потенциальную проблему недалекого будущего.
Защищенный режим на то и защищенный, что работает в связке с MMU, который позволяет разграничивать регионы памяти под разные программы и привязывать логические адреса к разным физическим адресам, тем самым позволяя реализовать виртуальную память, файлы подкачки и прочее.
Но были у защищенного режима фатальные проблемы. Ключевая: переключаться из реального в защищенный режим было легко, а вот из защищенного в реальный... ну можно было аппаратно сбросить процессор (попросив нажать пользователя на кнопку Reset). Ни о какой одновременной работе защищенного ПО со старым для реального режима речи идти не могло. Потом придумали подвести хитрую схему для программного сброса и вручную сохранять то, что будет при этом сбросе утеряно, но проблему полностью это не решало как минимум из-за серьезных тормозов и отсутствия должных механизмов для "кастрирования" от вредных привычек того, что хочет работать в реальном режиме.
Ничего хорошего из этого выйти не могло, так как всё старое ПО затачивалось под реальный режим с MS-DOS и не могло работать в ОС, использующих защищенный режим. А ОС без программ никому не нужна.
По поводу технической части мне сказать особо нечего, ибо я не работал с этим процессором и практически ничего не знаю о нем. Знаю, что осталась сегментация, но она работала абсолютно иначе и гораздо адекватнее.
I80386
Прошло 3 года. На дворе 1985 год, Intel учли свои ошибки и разработали новый вариант защищенного режима. Это самый интересный и второй по важности режим, который повсеместно использовался вплоть до конца 2000х и продолжает неявно использоваться до сих пор.
Во-первых, теперь процессор стал полностью 32-битным. Поверх старых 16-битных регистров нарастили новые регистры с префиксом 'E'. То есть теперь есть EAX, EBX, EIP, ESP, EBP, EFLAGS и так далее. Но не сегментные регистры, они остались 16-битными. Шина данных и адреса тоже стали 32-битными (шину адреса любят обрезать под лимиты конкретной платформы, но технически ее возможно было сделать 32-битной без модификаций архитектуры. В последствии разработали расширение PAE, что позволило расширить ее свыше 32 линий и 4 гигабайт).
Во-вторых, теперь появился третий (четвертый) режим: виртуальный 8086.Он совмещал в себе особенности работы реального режима и защищенность защищенного, позволяя достаточно эффективно переключаться между ними, а еще реализовывать одновременную работу множества программ реального режима внутри одной ОС одновременно с защищенными. При этом подобный псевдореальный режим оставался достаточно безопасным, так как многие опасные наглости эмулировались и не допускались напрямую к железу, а память была изолирована.
Продолжение следует
К сожалению, мысль о написании подобного текста у меня возникла слишком поздно, а на часах 4 часа 5 часов утра и я уже физически не в состоянии продолжать писать этот пост. Если это будет интересно, то я напишу продолжение, в котором полноценно расскажу про защищенный режим, костыли реального режима и пропущенный промежуток до появления длинного режима (i80486, Pentium). И так страшно представить, сколько неточностей и откровенно грубых ошибок я тут понаписывал на автомате. Если вы их нашли - просьба указать в комментариях
Обещал не делить посты на куски и такой облом, извините
Решил немного попробовать себя в авторском контенте, а за одно и рассказать о своём видении расширений для браузера.
Многие из нас знакомы с расширениями(плагинами) для браузеров которыми они пользуются, в основном это блокировщики рекламы, вроде Адблока или Юблока. В наше время, как по мне блокировщик рекламы в браузере, это прям вещь необходимая, так как я лично на многие сайты просто зайти не могу без морального потрясения когда нет блокировщика. Серьёзно! Я понимаю что владелец сайта хочет заработать, но некоторый типа фандома, откровенно жадничают, количество рекламных блоков просто зашкаливает! Но суть не в этом.
Что такое вообще расширение для браузера? В примитиве это небольшой скрипт который производит некие манипуляции на странице сайта. В случае адблока, скрипт пробегает по странице сайта и скрывает все блоки которые в его "списке" помечены как рекламные.
Есть более сложные расширения такие как ВПН, которые взаимодействуют не просто со страницей, а уже с настройками браузера и т.д., но речь сегодня не о них.
Мне больше нравится сама идея того что есть возможность расширить или модифицировать свой браузер эдаким изящным апплетом.
Например когда на ютюбе скрыли дизлайки, некто написал расширение которое при загрузке страницы улавливает параметр количества дизлайков ил "потрохов" сайта и не смотря на то что администрация ютюба это дело скрыла, плагин сам генерирует кусочек html кода и через JavaScript вставляет его в нужное место!
В этом мне нравится сама идея избавления от каких то идей которые нам хочет так или иначе навязать администрация сайта! (Цифровая диктатура как сейчас модно говорить)
Самое простое что можно сделать через расширения это скорректировать дизайн того или иного сайта под себя (или сразу всех), создать какую то свою интересную тему. Например заменить общий фон тела страницы на какой то узор, и сделать цвет текста мягче.
Работа расширений как и их создание (и размещение) очень просты, всем управляет JavaScript, а какие то вещи вроде меню настроек которые открываются при нажатии на его иконку, это просто небольшая html страничка.
По сути расширение это такая папочка с файлами, которая в случае firefox запаковывается в zip, после чего расширение у файла с zip меняется xpi и расширение готово к установке в браузер. В случае с браузерами на движке Chromium (chrome,opera,eadge,yandex e.t.c.) ничего запаковывать и вовсе не нужно, в настройках можно выбрать установку расширения из папки.
А что в папке?
Во первых файл manifest.json - это грубо говоря "паспорт" расширения, документ в котором написано, что это за расширение, кем создано, какая версия, какая у него иконка стоит, какие оно запрашивает разрешения у браузера (например возможность работать как раз с контентом страницы), указания того что за страничка будет открываться при нажатии на иконку, а так же указание какой скрипт нужно выполнять на сайтах при работе.
Далее естественно сама страничка index.html - которая показывается при нажатии на иконку, там обычно размещают какие то настройки, переключатели состояния. Это все легко верстается на html тэгах, а стили задаются в отдельному css файлике, ну или если лень то прям среди html тэгов.
Ну и раз есть настройки то нажатие на них нужно как то регистрировать? Здесь нам поможет файлик main.js - в котором мы пропишем логику того что будет происходить когда пользователь нажимает например на наши кнопочки в меню. Грубо говоря там будут инструкции в стиле "нажал на кнопку > происходит следующее"
И если мы пишем расширение которое работает с чем то на открытых нами сайтах то и скрипт background.js - в котором будут уже инструкции о том что будет расширение делать на открытых нами сайтах.
Так же тут находятся сопутствующие папочки вроде img или icons где у нас лежат какие то изображения и графика которую мы используем и иконка самого расширения.
по итогу у нас получается примерно такая вот структура:
Как это работает: человек кликает на иконку приложения в шапке окна браузера, ему показывается index.html в котором какие то настройки, при клике по настройкам файл main.js их обрабатывает, например сохраняет их значение в память браузера. А далее файл background.js который запускается на каждом открытом нами сайте, в начале получает из памяти браузера настройки которые натыкал пользователь, а затем на их основе проводит какие то манипуляции, например заменяет все изображения на странице на фото Николаса Кейджа, с той прической которая похожа на птицу.
Самое интересное для меня что в теории любой может на досуге попробовать в плане "поиграть", написать что то подобное для себя. В сети полно бесплатных мануалов и уроков, да и сейчас тот же chatGpt если задавать правильные вопросы может наглядно показывать подробные примеры кода и объяснять их работу.
Я постарался максимально простым языком описать работу расширений, без лишней терминологии что бы любой заинтересованный (надеюсь) смог примерно понять как это работает)
Надеюсь было интересно, за ошибки прошу прощения, я больше по части языков на котором говорят машины😁
Давайте знакомиться) Меня зовут Макс Атыгаев. Я занимаюсь программированием всю сознательную жизнь) с юности и до сих пор)
Я пробовал Android разработку и даже выпустил пару приложений в Google Play, но потом понял что мир бекенда мне нравится больше.
Где-то в 2016 я открыл для себя Telegram и полюбил разработку ботов) Самый любимый проект - пересылка между групповым чатом в вк и групповым чатом в телеграм. То есть словно соединить два чата на разных сервисах в один)
Сейчас работаю в российской компании, в свободное от работы время веду канал на YouTube, RuTube, Telegram. В 2019 году начал консультировать и обучать людей программированию на Java.
Если у вас есть какие-нибудь вопросы к действующему разработчику то смело пишите) обсудим в комментах или сделаю отдельный пост)
Числа Фибоначчи - это последовательность, где каждое очередное число равно сумме двух предыдущих. 1 = 0+1, 2 = 1+1, 3 = 2+1, 5 = 3+2, 8 = 5+3, ...
В программировании для вычисления этой и подобных последовательностей (факториалы, степени, если нет прямой возможности...) используются циклы.
Цикл представляет собой некоторые условия: начальные условия, условия завершения, изменение данных для достижения условий завершения. (Заметка для тех кто в теме, это касается любых циклов, а не только цикла for)
А вот то, что делается внутри цикла называется ТЕЛОМ цикла.
Linux, а для меня основная ОС для дома (удевлены?), является продолжателем самых первых операционок семейства UNIX, фактически - родоначальника всех ОСей. И есть в ней такая замечательная "либа", некий аналог виндовой DLLки. Практически во всех линухах (и частенько в MAC-OS/Windows) не только библиотека есть, но и её обёртка, что бы можно было использовать в командной строке.
Так вот, разраба данной библиотеки хакнули, причём конкретно его самого. Это наивысшый уровень социального инжиниринга.
Т.к. его библиотека была распростанённой, и достаточно эффективной, то хакеры решили "хакнуть" большинство дистров на базе Linux одним махом.
Там была цельная спецоперация.
Сначала долбили разраба фич-реквестави (пожелалками) - продолжалось енто пару лет. Потом появляются коммитеры, которые енти фичи реализуют. Их количество росло, а чел (ну не буду я его ФИО писать, что блы поисковики его опять пердолили ссылаясь на ентот пост - не хочу), просто устал, выгорел. И дал права над исходниками левому чуваку, который был наиболее активен среди коммитеров.
Результат не заставил себя ждать - в качестве очередного патча, в библиотеку был внедрён бэкдур, позволяющий получить права root.
Так будет тост: "Не давайте прав кому либо выше Ваших! Программисты - вы боги-создатели своего кода".