Это персональный блог, который ведётся в режиме записок обо всём, что меня интересует. В настоящий момент я занимаюсь управлением разработкой электронного документооборота «Практика».

Позднее Ctrl + ↑

«Спасибо»

Так как не вожу машину, частенько пользуюсь такси — два раза в день и больше. Иногда в «Убере» попадаются глухие или слабослышащие водители, приложение об этом предупреждает заранее. Кстати, это один из пяти агрегаторов, которыми я пользуюсь, но больше нигде не трудоустраивают таких водителей. «Убер» — молодчики!

Много лет назад у меня было желание выучить жестовый язык — мне это тогда казалось очень интересным. Интерес прошёл, а литература осталась. Недавно вытащил на свет учебник жестовой речи и выучил оттуда несколько жестов. Прежде всего жест «спасибо», конечно же. Теперь всегда его показываю, когда выхожу из машины. Многие глухие и слабослышащие водители искренне радуются!

10 февраля  

Список книг

У меня на прежнем сайте был список книг — раздел «Прочтённое». У меня там был обычный бестолковый список, по которому неудобно было собирать статистику.

Переехав, я подумал перенести этот список с сайта куда-нибудь наружу, ну должны же быть сервисы по ведению списков прочитанного? Спросил на «Фейсбуке», там дали несколько ссылок. Посмотрел несколько и остановился на «ЛайвЛибе» — теперь список прочтённого будет там. Не уверен, что поставлю ссылку на него с первой страницы блога, скорее всего, когда обзаведусь страницей «Обо мне», спрячу ссылку туда.

8 февраля  

DevelNext

Понадобилось тут по работе быстро накидать графическую утилиту под Виндоуз, пока шли совещания погуглил инструменты и случайно наткнулся на «ДевелНекст» — развитие «ДевелСтудио», о которой я писал восемь лет назад.

Внутри у неё свой диалект ПХП (JPHP), написанный на Джаве. Отличия от обычного интерпретатора, в основном, в стандартных функциях — они присутствуют не все, но чаще всего есть какие-то аналоги. Актуальная на текущий момент версия поддерживает синтаксис ПХП 7.1.

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

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

Потерял много времени, пытаясь найти пропавшую с сайта документацию — я догадывался, что всю работу надо выполнять в отдельном треде, чтобы не мешать интерфейсу прорисоваться, но не знал как это сделать. Пока случайно не попал в подраздел «Разного» — оказалось там притаилось решение моей проблемы.

Причём как оказалось, работать надо не просто в отдельном треде, так ещё и изменения интерфейса делать разрешается толко через специальную обёртку — функции uiLater и uiLaterAndWait. Пример из документации:

$this->label->text = 'Поток выполняется...';

$thread = new Thread(function () {
    sleep(3); // ждем 3 сек.

    uiLater(function() {
        $this->label->text = 'Поток выполнен.';
    });
});

$thread->start();

На выходе получается обычный запускаемый файл (в моём случае — 3,3 мегабайта, немного по нынешним временам). Внутри — судя по всему, интерпретатор Джавы и скомпилированная в байт-код программа.

К сожалению, сразу нашёлся баг — если на компьютере пользователя установлена Джава младше версии 1.8, программа запускаться отказывается. Автор «ДевелНекста» вызвался помочь исследовать эту проблему, надеюсь скоро исправит.

Разобрался: ларчик просто открывался! Я так был уверен, что должен получиться всего один файл, что не понял, что папку jre (где и лежит Джава) надо тоже копировать. Это уже менее интересно, конечно.

Дам пирожка

Больше недели назад кардинально изменил причёску — побрился налысо. Что делать — лысею, у меня только две дороги — зачёсывать волосы с шеи или побриться налысо. Хотя нет, можно ещё делать вид, что ничего не замечаешь. Мне показалось, что увидеть как выглядит мой лысый череп — хороший вариант.

Переживал, что мне не пойдёт — когда трогаешь руками, череп кажется удивительно неровным, но нет, оказалось всё в порядке. Было неожиданно тяжело на это решиться, не могу сказать почему, не разобрался.

Из неожиданных ощущений — кожа головы оказалась непривычной к зиме. В шапке сначала казалось холоднее, чем обычно, а в помещении при лёгком сквозняке приходилось прикрываться капюшоном худи. Прошла неделя и вроде привык. Или волосы немного выросли.

7 февраля  

Переезд на «Эгею»

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

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

Покопавшись в себе, удалось поднять со дна несколько причин и часть из них — технические. Мне уже давно надоел тот движок блога, который у меня стоит — я им не занимаюсь и он явно обветшал, как внутренне — архитектурно, так и внешне, с точки зрения дизайна. Я с большой надеждой смотрел на «Эгею» Бирмана и в этой году нашёл в себе силы наконец переехать на неё.

Эта работа отняла оба выходных и два вечера рабочей недели. Требовалось сконвертировать мою внутреннюю разметку в актуальную разметку «Эгеи», а так же внести несколько исправления в код (надеюсь скоро они попадут в основную ветку), чтобы сохранить кое-какие возможности, которые мне хотелось сохранить, а так же просмотреть большую часть блога и поправить несколько сотен ошибок конвертации, которые я нашёл.

Наверное пока не всё идеально, временно пропали страницы «Обо мне», «Прочтённое» и «99», но я надеюсь в скором времени справиться со всеми трудностями.

Кроме того, я сменил хостинг. Уважаемые «Докеры» хостили меня бесплатно восемь лет, спасибо им за это огромное, при случае всем его рекомендовал и рекомендую. Однако пора и честь знать, в связи с переходом на другой движок мне нужны большие аппаратные ресурсы и мне кажется неправильным просить их у «Докера», не платя за это ни копейки. Сейчас я подселился на площадку внутри «Мастерхоста» к своим знакомым, надеюсь ресурсов всем хватит.

В конце хотел бы сказать спасибо Илье Бирману за консультации по движку!

6 февраля  

Вастрик-бокс

Вастрик-бокс (154.47КиБ)
В первые дни января пришёл Вастрик-бокс, внутри — сюрпризы

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

Затея массовая, анонсируется заранее, её смысл для анонсирующего — финансовая выгода и пиар, для поддавшегося — получить положительные эмоции. То есть если вы покупаете бокс для того, чтобы получить какого-то товара на потраченную сумму — мимо, за этим идите в магазин.

Вастрик-бокс (154.47КиБ)
Распакованный заряд непрактичности, сюрпризов и весёлого настроения

Я бокс заказал у Вастрика — это такой новый модный блогер, спешите видеть! Ожидал получить какие-то прикольные штуки, получил какие-то прикольные штуки. Одну прикольную штуку пришлось зацензурировать. Жаль бокс не успел приехать к Новому году, отправитель живёт в маленькой европейской стране, до Казани посылка шла месяц, на неделю опоздала.

Причём П — персонализация, Вастрик даже зашёл ко мне в бложик и, увидев, что мне интересны монеты, насыпал несколько в бокс. Что, кстати, сподвигло меня наконец сделать перепись всех современных моей тушке монет, которые у меня есть. Надо бы какую-то статистику по этому поводу посчитать.

2 февраля  

Моно-но аварэ

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

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

В прошлой версии своей утилиты я сначала пытался файл открыть, если не получалось, то создать. Вообще, есть такой флаг O_CREAT, который можно указать в качестве режима открытия и если файл не существует, он создаётся. У меня не создавался. Я не понимал почему и понял лишь когда запустил strace. Оказалось, что константы, которые я взял откуда-то из недр своего Линукса, не соответствуют действительности. Скомпилировал небольшую программку на Сях и оттуда выдернул нужные значения, заработало как ожидается, минус один сисколл.

Вторая вещь — я очень давно не программирую на ассемблере и растерял большинство лучших практик. Вот например я не помню как лучше — когда подпрограмма сохраняет регистры, которая она портит или наоборот. Вроде первый вариант выглядит как-то универсальнее, но в моём случае придётся сохранять много регистров, которые я в реальности не занимаю. Так как программа у меня более чем обозримая (пара сотен строк) я остановился на втором варианте, в этом случае ничего лишнего я не делаю. Попутно ещё оказалось, что аналога команд PUSHA/POPA в 64-битном режиме нет (хаха), а это сохранение и восстановление всех регистров через стек одной командой, очень удобно, если мы не паримся над использованием памяти (всё-таки регистров уже немало и каждый по 8 байт).

Ну и последнее, самое интересное. Утилита работает с файлами, а файловые хендлеры надо где-то хранить. Файлов будет столько, сколько передаст пользователь через параметры. Тут две стратегии — ограничить количество входных параметров (пока я пошёл по этому пути) или более интересно — научиться динамически выделять память в системе. Я хочу второе. Из разной литературы я помнил, что есть некий mmap, которому можно скормить /dev/zero и получить область памяти, а есть куча, с которой тоже как-то можно работать.

Получаются всего несколько вызовов — мне нужен sys_mmap или sys_brk, первый работает с памятью от четырёх килобайт, второй и с менее значительными кусками. Хотя первый вызов мне интереснее, но выглядит он как попытка выстрелить из пушки по воробьям, попробую использовать второй.

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

🍎 + 🍍 + 🍌 = 🥗

Уравнение (118.71КиБ)
Казалось бы, обычная картинка, которых сотни — реши фрукты

По интернету ходят сотни картинок, где надо «решить фрукты» — в уравнение в качестве имён переменных подставлены какие-нибудь посторонние предметы. В вариантах чуть посложнее надо быть повнимательнее — например, в уравнениях везде бананы, а в одном месте гроздь из трёх.

Когда я увидел картинку выше, ради развлечения сунул её в «Вольфрам» — хотелось посмотреть как ему указать, что числа должны быть целые. Заинтересовало, что решения не нашлось. Я задумался и внимательно посмотрел на фрукты, в них подвоха тоже не увидел.

Оказалось, что картинка выше — пранк по мотивам статьи на «Кьюоре». Решить уравнение, которое на ней, сможет лишь небольшое количество людей со специальными знаниями, а прямой перебор значений займёт совершенно нереальное время.

Минимальные подходящие числа для уравнения такие (ответ проверял на ЯП «Эр»):

`🍎` <- 154476802108746166441951315019919837485664325669565431700026634898253202035277999
`🍌` <- 36875131794129999827197811565225474825492979968971970996283137471637224634055579
`🍍` <- 4373612677928697257861252602371390152816537558161613618621437993378423467772036

print(`🍎` / (`🍌` + `🍍`) + `🍌` / (`🍎` + `🍍`) + `🍍`/(`🍎` + `🍌`)) # [1] 4

Это числа от 79 знаков и больше! Где уж такое решить!

31 января  

Ассемблер под Линукс

Заметка про полноту по Тьюрингу команды присваивания в ассемблере натолкнула меня на мысль, что я как-то не удосужился попрограммировать на ассемблере под Линукс — эту ОС я начал осваивать примерно в то же время, когда начал быстро терять интерес к ассемблеру.

Тогда не довелось, решил вчера попробовать. Теорию в очень общих чертах я знал — есть системные вызовы (сисколы), которые можно дёргать какой-то командой, имена сисколов мне знакомы через Си. Попробовал написать программу, которая выводит на вход то, что ей дают на вход. Быстро разобрался, что регистры сейчас имеют впереди букву «эр», а сисколы делаются прерыванием №128. Удобный способ, параметры идут в логичном виде — последовательно через регистры в почти алфавитном порядке (ближе к концу последовательность нарушается):

MOV RAX, 3 ; sys_read
MOV RBX, 0 ; stdin
MOV RCX, string ; адрес строки, которую выведем на экран
MOV RDX, len ; длина строки
INT 0x80

Но я где-то читал, что для сисколов есть команда новее — syscall, просто заменил вызов прерывания на эту команду — не заработало. Полез в интернет. Оказалось, у там другой набор параметров, да и сами сисколы имеют другие номера.

MOV RAX, 0 ; sys_read
MOV RDI, 0 ; stdin
MOV RSI, string ; адрес строки, которую выведем на экран
MOV RDX, len ; длина строки
SYSCALL

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

Так как системный вызов для открытия файла (sys_open) как раз требует на вход сишную строку, я передал указатель, который взял со стека, в сискол, ожидая, что всё заработает. Это было моей ошибкой. Я потратил не меньше часа, разбираясь, что я сделал не так, перебирал способы передачи параметров, ползал по стеку при помощи отладчика gdb, ничего не помогало, пока не забрезжила догадка, что сами параметры командной строки тоже лежат на стеке и системному вызову это может не нравится.

Попробовал скопировать строку оттуда в данные и всё заработало. Не знаю что это было, пока не гуглил, возможно сделано из соображений безопасности. Времени потерял изрядно.

Пока получилась очень простая утилитка, но хочу её дописать как выпадет свободный вечер — сделать поддержку указания нескольких файлов и флага для режима присоединения. Размер радует, кстати, — всего 752 байта.

Дизассемблируй это

Дум (71.16КиБ)
«ДУМ», скомпилированный с использованием одних только команд MOV

В моей жизни последовательно сменяли друг друга три ассемблера. Первый, для компьютера «Радио-86РК», я выучил по комментариям к ассемблерному коду в журнале «Радио» — другой литературы не было, а до моего знакомства с интернетом оставалось несколько лет. Потом были последовательно ассемблеры для «Спектрума» и интеловских процессоров.

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

В ассемблере есть такая команда — MOV (в некоторых ассемблерах — LD), записывает содержимое одного аргумента в другой. Сейчас набор комманд разросся, аргументом может быть почти что угодно — регистр, ячейка в памяти, сумма некоторого числа, одного регистра и другого, умноженного на число, но по сути это всегда присваивание.

И вот оказалось, что эта команда — полная по Тьюрингу. Звучит невероятно, но это так. Некие ребята заморочились и сделали компилятор, который компилирует любую программу на Си в последовательность команд MOV. Причём им даже ДУМ удалось скомпилировать, правда один кадр рисуется семь часов. Кстати, такая программа неуязвима для горюшка века — Мелтдауна и Спектра.

Есть небольшая (на 156 страниц и 90% воды) презентация, достаточно популярно объясняющая как этого удалось достичь, но для её чтения надо знать ассемблер, поэтому я позволю себе раскрыть детали трансляции двух инструкций, чтобы пояснить принцип для тех, кто ассемблера не знает или ленится причитать.

Например, сравнение двух чисел делается при помощи следующего псеводокода:

mov [X], 0
mov [Y], 1
mov R, [X]

У нас есть два числа в аргументах «X» и «Y», результат сравнения которых попадает в «R» — там будет ноль, если числа не равны и единица в противном случае. Как же это работает?

Первой командой ноль записывается в ячейку по адресу «X». Это ассемблер, у нас тут всё — число, остальное — человеческие интерператации, поэтому записанное в «X» мы используем как адрес. Второй командой единица записывается в ячейку по адресу «Y». Третьей командой мы читаем значение по адресу «X» и если значения в «X» и «Y» совпадают, то ноль перетрётся единицей (и она попадёт в R), если нет, то в ячейке по адресу «X» ноль останется (который попадёт в R).

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

Возьмём, например, логическое «ИЛИ» («OR»), тут чуточку сложнее:

OR_ADDRS: dd OR_0, OR_1
OR_0: dd 0, 1
OR_1: dd 1, 1
; …
mov eax, X
mov edx, [OR_ADDRS + eax]
mov eax, Y
mov eax, [eax + edx]
mov R, eax

Во всех логических операциях ребята используют записанные заранее неизменяемые массивы значений.

Что тут происходит? В регистр (переменную, с которыми работает процессор) «eax» записывается значение «X» (возможные входные значения у нас тут — ноль или единица, численное представление булевых значений).

Далее в регистр «edx» записывается число из адреса, который является суммой адреса массива OR_ADDRS и содержимого регистра eax. Таким образом в eax попадёт OR_0 или OR_1, в зависимости от того былы записаны в eax ноль или единица. Эти значения — тоже числа и являются адресами двух других массивов из двух элементов.

Далее в eax мы записываем аргумент Y, его значение складывается с адресом полученным на предыдущем шаге и из получившегося адреса мы читаем записанное там значение. В переводе на ПХП получается следующее:

function mov_or(int $X, int $Y): int
{
    define('OR_0', [0, 1]);
    define('OR_1', [1, 1]);

    define('OR_ADDRS', [OR_0, OR_1]);

    $R = OR_ADDRS[$X][$Y];

    return $R;
}

Кстати, интересно, что у знаменитого дисассемблера «ИДА» от полученной таким образом программы крепко уносит крышу — при попытке отладки диссасемблер не видит никаких ветвлений и падает на анализе кода. Получился бы неплохой метод защиты от анализа, если бы не производительность.

Ранее Ctrl + ↓