Русский
Русский
English
Статистика
Реклама

Лайфхаки для гиков

Перевод Пишем загрузчик на Ассемблере и C. Часть 1

07.01.2021 16:12:32 | Автор: admin


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

О чем пойдет речь?


Мы рассмотрим написание кода программы и его копирование в загрузочный сектор образа флоппи-диска, после чего с помощью эмулятора bochs (x86) для Linux научимся проверять работоспособность полученной дискеты с загрузчиком.

О чем речь не пойдет


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

Структура статьи


Начнем мы со знакомства с основами, после чего перейдем к написанию самого кода. В целом план будет такой:

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

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

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

Знакомство с загрузочными устройствами


Что происходит при включении стандартного компьютера?


Обычно при нажатии кнопки включения питания от нее подается сигнал блоку питания о необходимости подачи необходимого напряжения на внутреннее и внешнее оборудование компьютера, такое как процессор, монитор, клавиатура и пр. Процессор при этом инициализирует ПЗУ-чип BIOS (базовую систему ввода/вывода) для загрузки содержащейся в нем исполняемой программы, именуемой также BIOS.

После запуска BIOS выполняет следующие задачи:
Тестирование оборудования при подаче питания (Power On self Test).
Проверка частоты и доступности шин.
Проверка системных часов и аппаратной информации в CMOS RAM.
Проверка настроек системы, предустановок оборудования и т.д.
Тестирование подключенного оборудования, начиная с RAM, дисководов, оптических приводов, HDD и т.д.
В зависимости от определенной в разделе загрузочных устройств информации выполняет поиск загрузочного диска и переходит к его инициализации.

К сведению: все ЦПУ с архитектурой x86 в процессе загрузки запускаются в реальном режиме (Real Mode).

Что такое загрузочное устройство?


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

Что такое сектор?


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

Что такое загрузочный сектор?


Загрузочный сектор или блок загрузки это область загрузочного устройства, в которой содержится загружаемый в RAM машинный код, за что отвечает встроенная в ПК прошивка на стадии инициализации. На флоппи-диске размер сектора составляет 512 байт. Чуть позже о байтах будет сказано дополнительно.

Как работает загрузочное устройство?


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

Что такое начальный загрузчик?


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

Какие есть виды микропроцессоров?


Я приведу основные:
16 битные
32 битные
64 битные

Чем больше значение бит, тем к большему объему памяти имеют доступ программы, получая большую производительность в плане временного хранилища, обработки и пр. На сегодня микропроцессоры производят две основные компании Intel и AMD. В этой же статьи я буду обращаться только к процессорам семейства Intel (x86).

В чем отличие процессоров Intel и AMD?


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

Знакомство со средой разработки


Что такое реальный режим?


Я уже упоминал, что все процессоры с архитектурой x86 при загрузке с устройства запускаются в реальном режиме. Это очень важно иметь в виду при написании загрузочного кода для любого устройства. Реальный режим поддерживает только 16-битные инструкции. Поэтому создаваемый вами код для загрузки в загрузочную запись или сектор должен компилироваться в 16-битный формат. В реальном режиме инструкции могут работать только с 16 битами одновременно. Например, в 16-битном ЦПУ конкретная инструкция будет способна складывать в одном цикле два 16-битных числа. Если же для процесса будет необходимо сложить два 32-битных числа, то потребуется больше циклов, выполняющих сложение 16-битных чисел.

Что такое набор инструкций?


Это гетерогенная коллекция сущностей, ориентированных на конкретную архитектуру микропроцессора, с помощью которых пользователь может взаимодействовать с ним. Здесь я подразумеваю коллекцию сущностей, состоящую из внутренних типов данных, инструкций, регистров, режимов адресации, архитектуры памяти, обработки прерываний и исключений, а также внешнего I/O. Обычно для семейства микропроцессоров создаются общие наборы инструкций. Процессор Intel-8086 относится к семейству 8086, 80286, 80386, 80486, Pentium, Pentium I, II, III, которое также известно как семейство x86. В этой статье я будут использовать набор инструкций, относящийся именно к этому типу процессоров.

Как написать код для загрузочного сектора устройства?


Для реализации этой задачи необходимо иметь представление о:
Операционной системе (GNU Linux).
Ассемблере (GNU Assembler).
Наборе инструкций (x86).
Написании инструкций на GNU Assembler для x86 микропроцессоров.
Компиляторе (как вариант язык C).
Компоновщике (GNU linker ld)
Эмуляторе x86, например bochs, используемом для тестирования.

Что такое операционная система?


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

Отдельно отмечу, что все современные ОС работают в защищенном режиме.
Какие виды ОС бывают?
Windows
Linux
MAC


Что значит защищенный режим?


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

Что такое Ассемблер?


Ассемблер преобразует передаваемые пользователем инструкции в машинный код.

Разве компилятор делает не то же самое?


На более высоком уровне да, но, фактически, внутри компилятора этим занимается именно ассемблер.

Почему компилятор не может генерировать машинный код напрямую?


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

Зачем нужна ОС для написания кода загрузочного сектора?


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

Какую ОС можно использовать?


Так как я писал загрузочные программы под Ubuntu, то и вам для ознакомления с данным руководством порекомендую именно эту ОС.

Какой следует использовать компилятор?


Я писал загрузчики при помощи GNU GCC и демонстрировать компиляцию кода я буду на нем же. Как протестировать рукописный код для загрузочного сектора? Я представлю вам эмулятор архитектуры x86, который помогает дорабатывать код, не требуя постоянной перезагрузки компьютера при редактировании загрузочного сектора устройства.

Знакомство с микропроцессором


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

Что такое регистры?


Регистры подобны утилитам микропроцессора, служащим для временного хранения данных и управления ими согласно нашим потребностям. Предположим, пользователь задает операцию сложения 2 и 3, для чего компьютер сохраняет число 3 в одном регистре, а 2 в другом, после чего складывает содержимое этих регистров. В итоге ЦПУ помещает результат в еще один регистр, который и представляет нужный пользователю вывод. Регистры разделяются на четыре основных типа:

регистры общего назначения;
сегментные регистры;
индексные регистры;
регистры стека.

Я дам краткое пояснение по каждому типу.

Регистры общего назначения: используются для хранения временных данных, необходимых программе в процессе выполнения. Каждый такой регистр имеет 16 бит в ширину и 2 байта в длину.
AX регистр сумматора;
BX регистр базового адреса;
CX регистр-счетчик;
DX регистр данных.

Сегментные регистры: служат для представления микропроцессору адреса памяти. Здесь нужно знать два термина:
Сегмент: независимый блок памяти, поддерживаемый аппаратно. Обычно обозначается начальным адресом.
Смещение: указывает индекс относительно начала сегмента.

Пример: у нас есть байт, представляющий значение X и расположенный в 10-й позиции от начала блока памяти со стартовым адресом 0x7c00. В данной ситуации мы выразим сегмент как 0x7c00, а смещение как 10.
Абсолютным адресом тогда будет 0x7c00 + 10.

Здесь я хочу выделить четыре категории:
CS сегмент кода;
SS сегмент стека;
DS сегмент данных;
ES расширенный сегмент.

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

movw $0x07c0, %axmovw %ax    , %dsmovw (0x0A) , %ax 


Здесь происходит:

загрузка значения 0x07c0 * 16 в AX;
загрузка содержимого AX в DS;
установка 0x7c00 + 0x0a в ax.

Регистры стека:
BP базовый указатель;
SP указатель стека.

Индексные регистры:
SI: регистр индекса источника.
DI: регистр индекса получателя.
AX: используется ЦПУ для арифметических операций.
BX: может содержать адрес процедуры или переменной (это также могут SI, DI и BP) и использоваться для выполнения арифметических операций и перемещения данных.
CX: выступает в роли счетчика цикла при повторении инструкций.
DX: содержит старшие 16 бит произведения при умножении, а также задействуется при делении.
CS: содержит базовый адрес всех выполняемых инструкций программы.
SS: содержит базовый адрес стека.
DS: содержит предустановленный адрес переменных.
ES: содержит дополнительный базовый адрес переменных памяти.
BP: содержит предполагаемое смещение из регистра SS. Часто используется подпрограммами для обнаружения переменных, переданных в стек вызывающей программой.
SP: содержит смещение вершины стека.
SI: используется в инструкциях перемещения строк. При этом на исходную строку указывает регистр SI.
DI: выступает в роли места назначения для инструкций перемещения строк.

Что такое бит?


В вычислительных средах бит является наименьшей единицей данных, представляющей их в двоичном формате, где 1 = да, а 0 = нет.

Дополнительно о регистрах:

Ниже описано дальнейшее подразделение регистров:
AX: первые 8 бит AX обозначаются как AL, последние 8 бит как AH.
BX: первые 8 бит BX обозначаются как BL, последние 8 как как BH.
CX: первые 8 бит CX обозначаются как CL, последние 8 бит как CH.
DX: первые 8 бит DX обозначаются как DL, последние 8 бит как DH.

Как обращаться к функциям BIOS?


BIOS предоставляет ряд функций, позволяющих распределять приоритеты ЦПУ. Доступ к этим возможностям BIOS можно получить с помощью прерываний.

Что такое прерывания?


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

Какое прерывание будем использовать мы?


Прерывание INT 0x10.

Написание кода на Ассемблере


Какие типы данных доступны в GNU Assembler?


Типы данных определяют их характеристики и могут быть следующими:

байт;
слово;
int;
ascii;
asciiz.

байт: состоит из восьми бит и считается наименьшей единицей хранения информации при программировании.
слово: единица данных, состоящая из 16 бит.

Int: целочисленный тип данных, состоящий из 32 бит, которые могут быть представлены четырьмя байтами или двумя словами.
Прим. Справедливости ради, стоит отметить, что размер Int зависит от архитектуры и может составлять от 16 до 64 бит (а на некоторых системах даже 8 бит). То, очем говорит автор это тип long. Подробнее о типах С можно прочесть по ссылке.

ascii: представляет группу байтов без нулевого символа.

asciiz: выражает группу байтов, завершающуюся нулевым символом.

Как генерировать код для реального режима в Ассемблере?


В процессе запуска ЦПУ в реальном режиме (16 бит) мы можем задействовать только встроенные функции BIOS. Я имею в виду, что с помощью этих функций можно написать собственный код загрузчика, поместить его в загрузочный сектор и выполнить загрузку. Давайте рассмотрим написание на Ассемблере небольшого фрагмента программы для генерации 16-битного кода ЦПУ через GNU Assembler.

Файл-образец: test.S  


.code16                   #генерирует 16-битный код.text                     #расположение исполняемого кода     .globl _start;_start:                   #точка входа     . = _start + 510     #перемещение из позиции 0 к 510-му байту      .byte 0x55           #добавление сигнатуры загрузки     .byte 0xaa           #добавление сигнатуры загрузки


Пояснения:

.code16: это директива, отдаваемая ассемблеру для генерации не 32-, а 16-битного кода. Зачем это нужно? Ассемблер вы будете использовать через операционную систему, а код загрузчика будете писать с помощью компилятора. Но вы также наверняка помните, что ОС работает в защищенном 32-битном режиме. Поэтому, по умолчанию ассемблер в такой ОС будет производить 32-битный код, что не соответствует нашей задаче. Данная же директива исправляет этот нюанс, и мы получаем 16-битный код.
.text: этот раздел содержит фактические машинные инструкции, составляющие вашу программу.
.globl _start: .global <символ> делает символ видимым для компоновщика. При определении символа в подпрограмме его значение становится доступным для других связанных подпрограмм. Иначе говоря, символ получает атрибуты от символа с таким же именем, находящегося в другом файле, связанном с этой программой.
_start: точка входа в основной код, а также предустановленная точка входа для компоновщика.
= _start + 510: переход от начальной позиции к 510-му байту.
.byte 0x55: первый байт, определяемый как часть сигнатуры загрузки (511-й байт).
.byte 0xaa: последний байт, определяемый как часть сигнатуры загрузки (512-й байт).

Как скомпилировать программу ассемблера?


Сохраните код в файле test.S и введите в командной строке:

as test.S -o test.o
ld Ttext 0x7c00 --oformat=binary test.o o test.bin

Что означают эти команды?


as test.S o test.o: преобразует заданный код в промежуточную объектную программу, которая затем преобразуется уже в машинный код.
--oformat=binary сообщает компоновщику, что выходной двоичный файл должен быть простым двоичным образом, т.е. не иметь кода запуска, связывания адресов и пр.
Ttext 0x7c00 сообщает компоновщику, что для вычисления абсолютного адреса нужно загрузить адрес text (сегмент кода) в 0x7c00.

Что такое сигнатура загрузки?


Давайте вспомним о загрузочном секторе, используемом BIOS для запуска системы, и подумаем, как BIOS узнает о наличии такого сектора на устройстве? Тут нужно пояснить, что состоит он из 512 байт, в которых для 510-го байта ожидается символ 0x55, а для 511-го символ 0xaa. Исходя из этого, BIOS проверяет соответствие двух последний байт загрузочного сектора этим значениям и либо продолжает загрузку, либо сообщает о ее невозможности. При помощи hex-редактора можно просматривать содержимое двоичного файла в более читабельном виде, и ниже в качестве примера я привел снимок этого файла.

Как скопировать исполняемый код на загрузочное устройство и протестировать его?


Чтобы создать образ для дискеты размером 1.4Мб, введите в командную строку следующее:
dd if=/dev/zero of=floppy.img bs=512 count=2880

Чтобы скопировать этот код в загрузочный сектор файла образа, введите:
dd if=test.bin of=floppy.img

Для тестирования программы введите:
bochs

Если bochs не установлен, тогда можно ввести следующее:
sudo apt-get install bochs-x

Файл-образец bochsrc.txt

megs: 32#romimage: file=/usr/local/bochs/1.4.1/BIOS-bochs-latest, address=0xf0000#vgaromimage: /usr/local/bochs/1.4.1/VGABIOS-elpin-2.40floppya: 1_44=floppy.img, status=insertedboot: alog: bochsout.txtmouse: enabled=0 


В результате должно отобразиться стандартное окно эмуляции bochs:



Просмотр:
Если теперь заглянуть в файл test.bin через hex-редактор, то вы увидите, что сигнатура загрузки находится после 510-го байта:



Пока что вы просто увидите сообщение Booting from Floppy, так как в коде мы еще ничего не прописали. Давайте рассмотрим пару других примеров создания кода на ассемблере.

Файл-образец: <i>test2.S</i> 

.code16                    #генерирует 16-битный код.text                      #расположение исполняемого кода     .globl _start;_start:                    #точка входа     movb $'X' , %al       #выводимый символ     movb $0x0e, %ah       #выводимый служебный код bios     int  $0x10            #прерывание цпу     . = _start + 510      #перемещение из позиции 0 к 510-му байту     .byte 0x55            #добавление сигнатуры загрузки     .byte 0xaa            #добавление сигнатуры загрузки


После ввода этого кода сохраните его в test2.S и выполните действия согласно прежней инструкции, изменив имя исходного файла. После компиляции, копирования кода в загрузочный сектор и выполнения bochs вы должны увидеть следующий экран, где теперь отображается прописанная нами в коде буква X.



Поздравляю, ваша первая программа в загрузочном секторе работает!

Просмотр:
В hex-редакторе вы увидите, что символ X находится во второй позиции от начального адреса.



Теперь давайте выведем на экран текст побуквенно.

Образец: test3.S

.code16                  #генерирует 16-битный код.text                    #расположение исполняемого кода     .globl _start;_start:                  #точка входа     #выводит 'H'      movb $'H' , %al     movb $0x0e, %ah     int  $0x10     #выводит 'e'     movb $'e' , %al     movb $0x0e, %ah     int  $0x10     #выводит 'l'     movb $'l' , %al     movb $0x0e, %ah     int  $0x10     #выводит 'l'     movb $'l' , %al     movb $0x0e, %ah     int  $0x10     #выводит 'o'      movb $'o' , %al     movb $0x0e, %ah     int  $0x10     #выводит ','     movb $',' , %al     movb $0x0e, %ah     int  $0x10     #выводит ' '     movb $' ' , %al     movb $0x0e, %ah     int  $0x10     #выводит 'W'     movb $'W' , %al     movb $0x0e, %ah     int  $0x10     #выводит'o'     movb $'o' , %al     movb $0x0e, %ah     int  $0x10     #выводит 'r'     movb $'r' , %al     movb $0x0e, %ah     int  $0x10     #выводит 'l'     movb $'l' , %al     movb $0x0e, %ah     int  $0x10     #выводит 'd'     movb $'d' , %al     movb $0x0e, %ah     int  $0x10     . = _start + 510    #перемещение из позиции 0 к 510-му байту     .byte 0x55            #добавление сигнатуры загрузки     .byte 0xaa            #добавление сигнатуры загрузки


Сохраните файл как test3.S. После компиляции и всех сопутствующих действий перед вами отобразится следующий экран:



Просмотр:



Хорошо. Теперь давайте напишем программу, выводящую на экран фразу Hello, World.
При этом мы также определим функции и макросы, с помощью которых и будем выводить эту строку.

Файл-образец: test4.S

#генерирует 16-битный код.code16#расположение исполняемого кода.text.globl _start;#точка входа загрузочного кода_start:      jmp _boot                           #переход к загрузочному коду      welcome: .asciz "Hello, World\n\r"  #здесь мы определяем строку     .macro mWriteString str              #макрос, вызывающий функцию вывода строки          leaw  \str, %si          call .writeStringIn     .endm     #функция вывода строки     .writeStringIn:          lodsb          orb  %al, %al          jz   .writeStringOut          movb $0x0e, %ah          int  $0x10          jmp  .writeStringIn     .writeStringOut:     ret_boot:     mWriteString welcome     #перемещение от начала к 510-му байту и присоединение сигнатуры загрузки     . = _start + 510     .byte 0x55     .byte 0xaa  


Сохраните файл как test4.S. Теперь после компиляции и всего за ней следующего вы увидите:



Отлично! Если вы поняли все проделанные мной действия и успешно создали аналогичную программу, то я вас поздравляю еще раз!

Просмотр:



Что такое функция?


Функция это блок кода, имеющий имя и переиспользуемое свойство.

Что такое макрос?


Макрос это фрагмент кода с присвоенным именем, на место использования которого подставляется содержимое этого макроса.

В чем синтаксическое отличие функции от макроса?


Для вызова функции используется следующий синтаксис:

push call А для макроса такой:

macroname И поскольку синтаксис вызова и применения макроса проще, чем функции, я предпочел использовать в основном коде именно его.

Написание кода в компиляторе С



Что такое C?


С это язык программирования общего назначения, разработанный сотрудником Bell Labs Деннисом Ритчи в 1969-1973 годах.

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

Что нужно для написания кода на С?


Мы будем использовать компилятор GNU C под названием GCC и разберем написание в нем программы на примере.

Файл-образец: test.c

__asm__(".code16\n");__asm__("jmpl $0x0000, $main\n");void main() {} 


File: test.ld

ENTRY(main);SECTIONS{    . = 0x7C00;    .text : AT(0x7C00)    {        *(.text);    }    .sig : AT(0x7DFE)    {        SHORT(0xaa55);    }} 


Для компиляции программы введите в командной строке:

gcc -c -g -Os -march=i686 -ffreestanding -Wall -Werror test.c -o test.o
ld -static -Ttest.ld -nostdlib --nmagic -o test.elf test.o
objcopy -O binary test.elf test.bin

Что значат эти команды?


Первая преобразует код C в промежуточную объектную программу, которая в последствии преобразуется в машинный код.
gcc -c -g -Os -march=i686 -ffreestanding -Wall -Werror test.c -o test.o:

Что значат эти команды?


-c: используется для компиляции исходного кода без линковки.
-g: генерирует отладочную информацию для отладчика GDB.
-Os: оптимизация размера кода.
-march: генерирует код для конкретной архитектуры ЦПУ (в нашем случае i686).
-ffreestanding: в среде отдельных программ может отсутствовать стандартная библиотека, а инструкции запуска программы не обязательно располагаются в main.
-Wall: активирует все предупреждающие сообщения компилятора. Рекомендуется всегда использовать эту опцию.
-Werror: активирует трактовку предупреждений как ошибок.
test.c: имя входного исходного файла.
-o: генерация объектного кода.
test.o: имя выходного файла объектного кода.

С помощью всей этой комбинации флагов мы генерируем объектный код, помогающий нам в обнаружении ошибок и предупреждений, а также создаем более эффективный код для данного типа ЦПУ. Если не указать march=i686, будет сгенерирован код для используемой вами машины. В связи с этим нужно указывать, для какого именно типа ЦПУ он создается.

ld -static -Ttest.ld -nostdlib --nmagic test.elf -o test.o:
Эта команда вызывает компоновщик из командной строки, и ниже я поясню, как именно мы его используем.

Что значат эти флаги?


-static: не линковать с общими библиотеками.
-Ttest.ld: разрешить компоновщику следовать командам из его скрипта.
-nostdlib: разрешить компоновщику генерировать код, не линкуя функции запуска стандартной библиотеки C.
--nmagic: разрешить компоновщику генерировать код без фрагментов _start_SECTION и _stop_SECTION.
test.elf: имя входного файла (соответствующий платформе формат хранения исполняемых файлов. Windows: PE, Linux: ELF)
-o: генерация объектного кода.
test.o: имя выходного файла объектного кода.

Что такое компоновщик?


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

objcopy -O binary test.elf test.bin
Эта команда служит для генерации независимого от платформы кода. Обратите внимание, что в Linux исполняемые файлы хранятся не так, как в Windows. В каждой системе свой способ хранения, но мы создаем всего-навсего небольшой загрузочный код, который на данный момент не зависит от ОС.

Зачем в программе C использовать инструкции ассемблера?


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

Как скопировать код на загрузочное устройство и проверить его?


Чтобы создать образ для дискеты размером 1.4Мб, введите в командную строку:

dd if=/dev/zero of=floppy.img bs=512 count=2880
Чтобы скопировать код в загрузочный сектор файла образа, введите:
dd if=test.bin of=floppy.img
Для проверки программы введите:
bochs

Должно отобразиться стандартное окно эмуляции:



Что мы видим: как и в первом нашем примере, пока что здесь отображается только сообщение Booting from Floppy.

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

Такой способ вложения называется встраивание ассемблерного кода.
Рассмотрим еще несколько примеров написания с помощью компилятора.

Пишем программу для вывода на экран X


Файл-образец: test2.c

__asm__(".code16\n");__asm__("jmpl $0x0000, $main\n");void main() {     __asm__ __volatile__ ("movb $'X'  , %al\n");     __asm__ __volatile__ ("movb $0x0e, %ah\n");     __asm__ __volatile__ ("int $0x10\n");}


Написав код, сохраните файл как test2.c и скомпилируйте его согласно все тем же инструкциям, изменив исходное имя. После компиляции, копирования кода в загрузочный сектор и выполнения команды bochs вы снова увидите экран, где отображается буква X:



Теперь напишем код для показа фразы Hello, World


Для вывода данной строки мы также определим функции и макросы.

Файл-образец: test3.c

/* генерирует 16-битный код */__asm__(".code16\n");/* переходит к точке входа загрузочного кода */__asm__("jmpl $0x0000, $main\n");void main() {     /* выводит 'H' */     __asm__ __volatile__("movb $'H' , %al\n");     __asm__ __volatile__("movb $0x0e, %ah\n");     __asm__ __volatile__("int  $0x10\n");     /* выводит 'e' */     __asm__ __volatile__("movb $'e' , %al\n");     __asm__ __volatile__("movb $0x0e, %ah\n");     __asm__ __volatile__("int  $0x10\n");     /* выводит 'l' */     __asm__ __volatile__("movb $'l' , %al\n");     __asm__ __volatile__("movb $0x0e, %ah\n");     __asm__ __volatile__("int  $0x10\n");     /* выводит 'l' */     __asm__ __volatile__("movb $'l' , %al\n");     __asm__ __volatile__("movb $0x0e, %ah\n");     __asm__ __volatile__("int  $0x10\n");     /* выводит 'o' */     __asm__ __volatile__("movb $'o' , %al\n");     __asm__ __volatile__("movb $0x0e, %ah\n");     __asm__ __volatile__("int  $0x10\n");     /* выводит ',' */     __asm__ __volatile__("movb $',' , %al\n");     __asm__ __volatile__("movb $0x0e, %ah\n");     __asm__ __volatile__("int  $0x10\n");     /* выводит ' ' */     __asm__ __volatile__("movb $' ' , %al\n");     __asm__ __volatile__("movb $0x0e, %ah\n");     __asm__ __volatile__("int  $0x10\n");     /* выводит 'W' */     __asm__ __volatile__("movb $'W' , %al\n");     __asm__ __volatile__("movb $0x0e, %ah\n");     __asm__ __volatile__("int  $0x10\n");     /* выводит 'o' */     __asm__ __volatile__("movb $'o' , %al\n");     __asm__ __volatile__("movb $0x0e, %ah\n");     __asm__ __volatile__("int  $0x10\n");     /* выводит 'r' */     __asm__ __volatile__("movb $'r' , %al\n");     __asm__ __volatile__("movb $0x0e, %ah\n");     __asm__ __volatile__("int  $0x10\n");     /* выводит 'l' */     __asm__ __volatile__("movb $'l' , %al\n");     __asm__ __volatile__("movb $0x0e, %ah\n");     __asm__ __volatile__("int  $0x10\n");     /* выводит 'd' */     __asm__ __volatile__("movb $'d' , %al\n");     __asm__ __volatile__("movb $0x0e, %ah\n");     __asm__ __volatile__("int  $0x10\n");}


Сохраните этот код в файле test3.c и следуйте уже знакомым вам инструкциям, изменив имя исходного файла и скопировав скомпилированный код в загрузочный сектор дискеты. Теперь на этапе проверки должна отобразиться надпись Hello, World:



Напишем программу C для вывода строки Hello, World


При этом мы определим функцию, выводящую эту строку на экран.

Файл-образец: test4.c

/*генерирует 16-битный код*/__asm__(".code16\n");/*переход к точке входа в загрузочный код*/__asm__("jmpl $0x0000, $main\n");/* пользовательская функция для вывода серии знаков, завершаемых нулевым символом*/void printString(const char* pStr) {     while(*pStr) {          __asm__ __volatile__ (               "int $0x10" : : "a"(0x0e00 | *pStr), "b"(0x0007)          );          ++pStr;     }}void main() {     /* вызов функции <i>printString </i>со строкой в качестве аргумента*/     printString("Hello, World");} 


Сохраните этот код в файле test4.c и снова проследуйте всем инструкциям компиляции и загрузки, в результате чего на экране должно отобразиться следующее:



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

Далее мы перейдем к написанию циклов и их использованию в функциях, а также познакомимся с другими службами BIOS.

Мини-проект отображения прямоугольников


Файл-образец: test5.c

/* генерирация 16-битного кода                                                 */__asm__(".code16\n");/* переход к главной функции или программному коду                                */__asm__("jmpl $0x0000, $main\n");#define MAX_COLS     320 /* количество столбцов экрана               */#define MAX_ROWS     200 /* количество строк экрана                  *//* функция вывода строки*//* input ah = 0x0e*//* input al = <выводимый символ>*//* прерывание: 0x10*//* мы используем прерывание 0x10 с кодом функции 0x0e для вывода байта из al*//* эта функция получает в качестве аргумента строку и выводит символ за символом, пока не достигнет нуля*/void printString(const char* pStr) {     while(*pStr) {          __asm__ __volatile__ (               "int $0x10" : : "a"(0x0e00 | *pStr), "b"(0x0007)          );          ++pStr;     }}/* функция, получающая сигнал о нажатии клавиши на клавиатуре *//* input ah = 0x00*//* input al = 0x00*//* прерывание: 0x10*//* эта функция регистрирует нажатие пользователем клавиши для продолжения выполнения */void getch() {     __asm__ __volatile__ (          "xorw %ax, %ax\n"          "int $0x16\n"     );}/* функция вывода на экран цветного пикселя в заданном столбце и строке *//* входной ah = 0x0c*//* входной al = нужный цвет*//* входной cx = столбец*//* входной dx = строка*//* прерывание: 0x10*/void drawPixel(unsigned char color, int col, int row) {     __asm__ __volatile__ (          "int $0x10" : : "a"(0x0c00 | color), "c"(col), "d"(row)     );}/* функции очистки экрана и установки видео-режима 320x200 пикселей*//* функция для очистки экрана *//* входной ah = 0x00 *//* входной al = 0x03 *//* прерывание = 0x10 *//* функция для установки видео режима *//* входной ah = 0x00 *//* входной al = 0x13 *//* прерывание = 0x10 */void initEnvironment() {     /* очистка экрана */     __asm__ __volatile__ (          "int $0x10" : : "a"(0x03)     );     __asm__ __volatile__ (          "int $0x10" : : "a"(0x0013)     );}/* функция вывода прямоугольников в порядке уменьшения их размера *//* я выбрал следующую последовательность отрисовки: *//* из левого верхнего угла в левый нижний, затем в правый нижний, оттуда в верхний правый и в завершении в верхний левый край */void initGraphics() {     int i = 0, j = 0;     int m = 0;     int cnt1 = 0, cnt2 =0;     unsigned char color = 10;     for(;;) {          if(m < (MAX_ROWS - m)) {               ++cnt1;          }          if(m < (MAX_COLS - m - 3)) {               ++cnt2;          }          if(cnt1 != cnt2) {               cnt1  = 0;               cnt2  = 0;               m     = 0;               if(++color > 255) color= 0;          }          /* верхний левый -> левый нижний */          j = 0;          for(i = m; i < MAX_ROWS - m; ++i) {               drawPixel(color, j+m, i);          }          /* левый нижний -> правый нижний */          for(j = m; j < MAX_COLS - m; ++j) {               drawPixel(color, j, i);          }          /* правый нижний -> правый верхний */          for(i = MAX_ROWS - m - 1 ; i >= m; --i) {               drawPixel(color, MAX_COLS - m - 1, i);          }          /* правый верхний -> левый верхний */          for(j = MAX_COLS - m - 1; j >= m; --j) {               drawPixel(color, j, m);          }          m += 6;          if(++color > 255)  color = 0;     }}/* эта функция является загрузочным кодом и вызывает следующие функции: *//* вывод на экран сообщения, предлагающего пользователю нажать любую клавишу для продолжения. После нажатия клавиши происходит отрисовка прямоугольников в порядке убывания их размера */void main() {     printString("Now in bootloader...hit a key to continue\n\r");     getch();     initEnvironment();     initGraphics();}


Сохраните все это в файле test5.c и следуйте все тем же инструкциям компиляции с последующим копированием кода в загрузочный сектор дискеты.

Теперь в качестве результата вы увидите:



Нажмите любую клавишу.






Просмотр:
Если внимательно рассмотреть содержимое исполняемого файла, то можно заметить, что в нем практически закончилось свободное пространство. Поскольку размер загрузочного сектора ограничен 512Кб, мы смогли вместить только несколько функций, а именно выполнить инициализацию среды и вывод цветных прямоугольников. Вот снимок содержимого файла:



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

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

Подробнее..

Перевод Пишем макет 16-битного ядра на CC

12.01.2021 12:17:05 | Автор: admin


В первой и второй статьях я лишь коротко представил процесс написания загрузчика на ассемблере и C. Для меня это было хоть и непросто, но в то же время интересно, так что я остался доволен. Однако создания загрузчика мне показалось мало, и я увлекся идеей его расширения дополнительной функциональностью. Но так как в итоге размер готовой программы превысил 512 байт, то при попытке запуска системы с несущего ее загрузочного диска я столкнулся с проблемой This is not a bootable disk.

О чем эта статья?


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

Нужен ли для этого опыт?


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

Как и ранее, процесс изложения построен по принципу вопрос-ответ, что должно упростить восприятие информации.

План статьи


Ограничения загрузчика
Вызов из загрузчика других файлов диска
Файловая система FAT
Принцип работы FAT
Среда разработки
Написание загрузчика для FAT
Мини-проект: написание 16-битного ядра
Тестирование ядра

Ограничения загрузчика


В предыдущих статьях я написал загрузчик и, реализовав с его помощью вывод на экран цветных прямоугольников, захотел добавить дополнительную функциональность. Однако при этом я столкнулся с ограничением размера сектора в 512 байт, что не позволило добиться желаемого стандартным путем.

В итоге передо мной стоит две задачи:

Расширить загрузчик кодом, реализующим дополнительную функциональность.
Сохранить при этом размер загрузчика в 512 байт.

Как я буду это делать?


Этап 1:

Напишу программу kernel.c на C, внедрив в нее всю необходимую функциональность.
Скомпилирую и сохраню исполняемый файл как kernel.bin.
Скопирую этот файл во второй сектор загрузочного диска.

Этап 2:

В загрузчике мы можем просто загрузить второй сектор, содержащий kernel.bin, в RAM по адресу, к примеру, 0x1000, а затем перейти к этому адресу из 0x7с00 и запустить kernel.bin.

Вот схема для лучшего понимания идеи:



Запуск из загрузчика других файлов диска


Как мы теперь знаем, у нас есть возможность передачи управления от загрузчика (0x7c00) в другую область памяти, где размещается, например, наш kernel.bin, после чего продолжить выполнение. Но здесь у меня я хочу кое-что уточнить.

Как узнать сколько секторов kernel.bin займет на диске?


Ну это простой вопрос. Для ответа на него нам достаточно выполнить несложную арифметику, а именно разделить размер kernel.bin на размер сектора, который составляет 512 байт. Например, если kernel.bin будет равен 1024 байта, то и займет он 2 сектора.

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

Можно ли добавить помимо kernel.bin другие файлы, например office.bin, entertainment.bin, drivers.bin?


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

Откуда мы знаем, что после загрузочного сектора выполняются именно желаемые файлы?


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

Чего не хватает?


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

Что произойдет, если по ошибке загрузить во второй сектор не тот файл, обновить загрузчик и начать выполнение?


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

Мне такой вариант очень нравится, так как он избавляет от лишних действий.

Здесь мы избегаем нескольких проблем. До этого загрузчик вслепую загружал жестко закодированные сектора. Но зачем загружать файл, не будучи уверенным в том, что это именно нужный файл, и что он вообще существует?

Как это решается?


Для этого нужно просто организовать информацию на диске, после чего перепрограммировать загрузчик, повысив тем самым его эффективность при загрузке файлов.

Такой вид организации информации называется файловой системой. Существует много типов файловых систем, среди которых есть как коммерческие, так и бесплатные. Вот некоторые из них:

FAT
FAT16
FAT32
NTFS
EXT
EXT2
EXT3
EXT4

FAT


Для лучшего понимания этой файловой системы вам потребуется знать некоторые технические нюансы.
Минимальной единицей измерения пространства FAT является кластер, который занимает 1 сектор накопителя. Иначе говоря, на дискете, отформатированной в FAT, 1 кластер эквивалентен 1 сектору и, соответственно, равен 512 байт.

Для удобства использования файловая система дополнительно разделяется на четыре основные области:

загрузочный сектор (boot sector);
таблицу размещения файлов (file allocation table);
корневой каталог (root directory);
область данных (data area).

Я постарался максимально понятно изобразить эту структуру в виде схемы:



Рассмотрим каждую часть подробнее.

Загрузочный сектор


Загрузочный сектор содержит служебную информацию, на основе которой ОС распознает тип файловой системы диска, после чего уже переходит к чтению его содержимого.

Информация о файловой системе FAT, содержащаяся в загрузочном секторе, называется блоком параметров BIOS.

Блок параметров BIOS


Ниже я привел пример значений из этого блока:



Таблица размещения файлов


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

Это значение кластера служит для:
определения окончания файла. Если оно находится между 0x0ff8 и 0x0fff, значит файл не содержит данных в других секторах, т.е. достигнут его конец.
определения следующего кластера с данными этого файла.

К сведению: на схеме выше я отметил две таблицы FAT. Вторая является резервной копией первой и используется в случае ее повреждения.

Корневой каталог


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

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

Область данных


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

Принцип работы FAT


Продолжим наш пример с kernel.bin, который загрузчик помещает в память для выполнения. Теперь в этом сценарии нужно прописать для загрузчика следующую функциональность:

Сравнить первые 11 байт данных с kernel.bin, начиная со смещения 0 в таблице корневого каталога.
В случае совпадения этой строки извлечь первый кластер kernel.bin из смещения 26 корневого каталога.
Далее преобразовать этот кластер в соответствующий сектор и загрузить его данные в память.
После загрузки первого сектора в память перейти к поиску в FAT следующего кластера файла и определить, является он последним, или есть еще данные в других кластерах.

Ниже я привел очередную схему.



Среда разработки


Для успешной реализации этой задачи нам нужно иметь представление о следующем. Более подробную информацию по этим пунктам можете найти в двух предыдущих статьях.

Операционная система (GNU Linux).
Ассемблер (GNU Assembler).
Набор инструкций (x86).
Написание инструкций для микропроцессора x86 на GNU Assembler.
Компилятор (GNU C компилятор GCC).
Компоновщик (GNU linker ld)
Эмулятор, например bochs, используемый для тестирования.

Написание загрузчика FAT


Ниже я привожу фрагмент кода для выполнения файла kernel.bin на FAT-диске.

Вот загрузчик.

Файл: stage0.S



/********************************************************************************* *                                                                               * *                                                                               * *    Name       : stage0.S                                                      * *    Date       : 23-Feb-2014                                                   * *    Version    : 0.0.1                                                         * *    Source     : assembly language                                             * *    Author     : Ashakiran Bhatter                                             * *                                                                               * *    Описание: основная логика подразумевает сканирование файла kernel.bin      * *                 на дискете fat12 и передачу этому файлу права                 * *                 выполнения.                                                   * *    Использование: подробности в файле readme.txt                              * *                                                                               * *                                                                               * *********************************************************************************/.code16.text.globl _start;_start:     jmp _boot     nop     /*блок параметров BIOS                           описание каждой сущности      */     /*--------------------                           --------------------------    */     .byte 0x6b,0x69,0x72,0x55,0x58,0x30,0x2e,0x31    /* метка OEM                  */     .byte 0x00,0x02                                  /* байтов в секторе           */     .byte 0x01                                       /* секторов в кластере        */     .byte 0x01,0x00                                  /* зарезервированных секторов */     .byte 0x02                                       /* таблиц fat                 */     .byte 0xe0,0x00                                  /* записей в каталоге         */     .byte 0x40,0x0b                                  /* всего секторов             */     .byte 0xf0                                       /* описание среды передачи    */     .byte 0x09,0x00                                  /* размер в каждой таблице fat    */     .byte 0x02,0x01                                  /* секторов в дорожке         */     .byte 0x02,0x00                                  /* головок на цилиндр         */     .byte 0x00,0x00, 0x00, 0x00                      /* скрытых секторов           */     .byte 0x00,0x00, 0x00, 0x00                      /* больших секторов           */     .byte 0x00                                       /* идентификатор загрузочного диска*/     .byte 0x00                                       /* неиспользуемых секторов    */     .byte 0x29                                       /* внешняя сигнатура загрузки */     .byte 0x22,0x62,0x79,0x20                        /* серийный номер             */     .byte 0x41,0x53,0x48,0x41,0x4b,0x49              /* метка тома 6 байт из 11    */     .byte 0x52,0x41,0x4e,0x20,0x42                   /* метка тома 5 байт из 11    */     .byte 0x48,0x41,0x54,0x54,0x45,0x52,0x22         /* тип файловой системы       */     /* включение макросов */     #include "macros.S"/* начало основного кода */_boot:     /* инициализация среды */     initEnvironment      /* загрузка stage2 */     loadFile $fileStage2/* бесконечный цикл */_freeze:     jmp _freeze/* непредвиденное завершение программы */_abort:     writeString $msgAbort     jmp _freeze     /* включение функций */     #include "routines.S"     /* пользовательские переменные */     bootDrive : .byte 0x0000     msgAbort  : .asciz "* * * F A T A L  E R R O R * * *"     #fileStage2: .ascii "STAGE2  BIN"     fileStage2: .ascii  "KERNEL  BIN"     clusterID : .word 0x0000     /* перемещение от начала к 510-му байту */     . = _start + 0x01fe     /* добавление сигнатуры загрузки             */     .word BOOT_SIGNATURE

В этом основном файле загрузки происходит:

Инициализация всех регистров и настройка стека вызовом макроса initEnvironment.
Вызов макроса loadFile для загрузки kernel.bin в память по адресу 0x1000:0000 и последующей передачи ему права выполнения.

Файл: macros.S


Этот файл содержит все предопределенные макросы и функции.

/*********************************************************************************          *                                                                               * *                                                                               * *    Name       : macros.S                                                      * *    Date       : 23-Feb-2014                                                   * *    Version    : 0.0.1                                                         * *    Source     : assembly language                                             * *    Author     : Ashakiran Bhatter                                             * *                                                                               * *                                                                               * *********************************************************************************//* предопределенный макрос: загрузчик                         */#define BOOT_LOADER_CODE_AREA_ADDRESS                 0x7c00#define BOOT_LOADER_CODE_AREA_ADDRESS_OFFSET          0x0000/* предопределенный макрос: сегмент стека                       */#define BOOT_LOADER_STACK_SEGMENT                     0x7c00#define BOOT_LOADER_ROOT_OFFSET                       0x0200#define BOOT_LOADER_FAT_OFFSET                        0x0200#define BOOT_LOADER_STAGE2_ADDRESS                    0x1000#define BOOT_LOADER_STAGE2_OFFSET                     0x0000 /* предопределенный макрос: разметка дискеты                  */#define BOOT_DISK_SECTORS_PER_TRACK                   0x0012#define BOOT_DISK_HEADS_PER_CYLINDER                  0x0002#define BOOT_DISK_BYTES_PER_SECTOR                    0x0200#define BOOT_DISK_SECTORS_PER_CLUSTER                 0x0001/* предопределенный макрос: разметка файловой системы                  */#define FAT12_FAT_POSITION                            0x0001#define FAT12_FAT_SIZE                                0x0009#define FAT12_ROOT_POSITION                           0x0013#define FAT12_ROOT_SIZE                               0x000e#define FAT12_ROOT_ENTRIES                            0x00e0#define FAT12_END_OF_FILE                             0x0ff8/* предопределенный макрос: загрузчик                         */#define BOOT_SIGNATURE                                0xaa55/* пользовательские макросы *//* макрос для установки среды */.macro initEnvironment     call _initEnvironment.endm/* макрос для отображения строки на экране.   *//* Для выполнения этой операции он вызывает функцию _writeString *//* параметр: вводная строка                */.macro writeString message     pushw \message     call  _writeString.endm/* макрос для считывания сектора в памяти  *//* Вызывает функцию _readSector со следующими параметрами   *//* параметры: номер сектора               *//*            адрес загрузки                *//*            смещение адреса          *//*            количество считываемых секторов      */.macro readSector sectorno, address, offset, totalsectors     pushw \sectorno     pushw \address     pushw \offset     pushw \totalsectors     call  _readSector     addw  $0x0008, %sp.endm/* макрос для поиска файла на FAT-диске.   *//* Для этого он вызывает макрос readSector *//* параметры: адрес корневого каталога     *//*               целевой адрес             *//*               целевое смещение          *//*               размер корневого каталога */.macro findFile file     /* считывание таблицы FAT в память */     readSector $FAT12_ROOT_POSITION, $BOOT_LOADER_CODE_AREA_ADDRESS, $BOOT_LOADER_ROOT_OFFSET, $FAT12_ROOT_SIZE     pushw \file     call  _findFile     addw  $0x0002, %sp.endm/* макрос для преобразования заданного кластера в номер сектора *//* Для этого он вызывает _clusterToLinearBlockAddress *//* параметр: номер кластера */.macro clusterToLinearBlockAddress cluster     pushw \cluster     call  _clusterToLinearBlockAddress     addw  $0x0002, %sp.endm/* макрос для загрузки целевого файла в память.  *//* Он вызывает findFile и загружает данные соответствующего файла в память *//* по адресу 0x1000:0x0000 *//* параметр: имя целевого файла */.macro loadFile file     /* проверка наличия файла */     findFile \file     pushw %ax     /* считывание таблицы FAT в память */     readSector $FAT12_FAT_POSITION, $BOOT_LOADER_CODE_AREA_ADDRESS, $BOOT_LOADER_FAT_OFFSET, $FAT12_FAT_SIZE     popw  %ax     movw  $BOOT_LOADER_STAGE2_OFFSET, %bx_loadCluster:     pushw %bx     pushw %ax      clusterToLinearBlockAddress %ax     readSector %ax, $BOOT_LOADER_STAGE2_ADDRESS, %bx, $BOOT_DISK_SECTORS_PER_CLUSTER     popw  %ax     xorw %dx, %dx     movw $0x0003, %bx     mulw %bx     movw $0x0002, %bx     divw %bx     movw $BOOT_LOADER_FAT_OFFSET, %bx     addw %ax, %bx     movw $BOOT_LOADER_CODE_AREA_ADDRESS, %ax     movw %ax, %es     movw %es:(%bx), %ax     orw  %dx, %dx     jz   _even_cluster_odd_cluster:     shrw $0x0004, %ax     jmp  _done _even_cluster:     and $0x0fff, %ax_done:     popw %bx     addw $BOOT_DISK_BYTES_PER_SECTOR, %bx     cmpw $FAT12_END_OF_FILE, %ax     jl  _loadCluster     /* выполнение ядра */     initKernel     .endm/* параметры: имя целевого файла *//* макрос для передачи права выполнения файлу, загруженному *//* в память по адресу 0x1000:0x0000                     *//* параметры: none                       */.macro initKernel     /* инициализация ядра */     movw  $(BOOT_LOADER_STAGE2_ADDRESS), %ax     movw  $(BOOT_LOADER_STAGE2_OFFSET) , %bx     movw  %ax, %es     movw  %ax, %ds     jmp   $(BOOT_LOADER_STAGE2_ADDRESS), $(BOOT_LOADER_STAGE2_OFFSET).endm 

Общая сводка


initEnvironment:
Макрос для установки сегментных регистров.
Аргументов не требует.

Применение: initEnvironment

writeString:
Макрос для отображения на экране строки с завершающим нулем.
В качестве аргумента передается строковая переменная с завершающим нулем.

Применение: writeString <строковая переменная>

readSector:
Макрос для чтения с диска заданного сектора и его загрузки в целевой адрес памяти.
Количество аргументов: 4.

Применение: readSector <номер сектора>, <целевой адрес>, <смещение целевого адреса>, <количество считываемых секторов>

findFile:
Макрос для проверки наличия файла.
Количество аргументов: 1.

Применение: findFile <имя целевого файла>

clusterToLinearBlockAddress:
Макрос для преобразования заданного кластера в номер сектора.
Количество аргументов: 1.

Применение:
clusterToLinearBlockAddress <ID кластера>


loadFile:
Макрос для загрузки целевого файла в память с последующей передачей ему права выполнения.
Количество аргументов: 1.

Применение:
loadFile <имя целевого файла>


initKernel:
Макрос для передачи права выполнения конкретному адресу памяти в RAM.
Аргументов не требует.

Применение: initKernel

Файл: routines.S



/********************************************************************************* *                                                                               * *                                                                               * *    Name       : routines.S                                                    * *    Date       : 23-Feb-2014                                                   * *    Version    : 0.0.1                                                         * *    Source     : assembly language                                             * *    Author     : Ashakiran Bhatter                                             * *                                                                               * *                                                                               * *********************************************************************************//* Пользовательские подпрограммы. *//* функция для настройки регистров и стека *//* параметры: none                  */_initEnvironment:     pushw %bp     movw  %sp, %bp_initEnvironmentIn:     cli     movw  %cs, %ax     movw  %ax, %ds     movw  %ax, %es     movw  %ax, %ss     movw  $BOOT_LOADER_STACK_SEGMENT, %sp     sti_initEnvironmentOut:     movw  %bp, %sp     popw  %bpret/* функция для отображения строки на экране *//* параметр: вводная строка                */_writeString:     pushw %bp     movw  %sp   , %bp     movw 4(%bp) , %si     jmp  _writeStringCheckByte_writeStringIn:     movb $0x000e, %ah     movb $0x0000, %bh     int  $0x0010     incw %si_writeStringCheckByte:     movb (%si)  , %al     orb  %al    , %al     jnz  _writeStringIn_writeStringOut:     movw %bp    , %sp     popw %bpret/* функция для считывания сектора в целевой адрес памяти *//* параметры: номер сектора                              *//*            целевой адрес                              *//*            смещение адреса                            *//*            количество считываемых секторов            */_readSector:     pushw %bp     movw %sp    , %bp     movw 10(%bp), %ax     movw $BOOT_DISK_SECTORS_PER_TRACK, %bx     xorw %dx    , %dx     divw %bx     incw %dx     movb %dl    , %cl     movw $BOOT_DISK_HEADS_PER_CYLINDER, %bx     xorw %dx    , %dx     divw %bx     movb %al    , %ch     xchg %dl    , %dh     movb $0x02  , %ah     movb 4(%bp) , %al     movb bootDrive, %dl     movw 8(%bp) , %bx     movw %bx    , %es     movw 6(%bp) , %bx     int  $0x13     jc   _abort     cmpb 4(%bp) , %al     jc   _abort     movw %bp    , %sp     popw %bpret/* функция поиска файла на дискете         *//* параметры: адрес корневого каталога     *//*               целевой адрес             *//*               целевое смещение          *//*               размер корневого каталога */_findFile:     pushw %bp     movw  %sp   , %bp     movw  $BOOT_LOADER_CODE_AREA_ADDRESS, %ax     movw  %ax   , %es     movw  $BOOT_LOADER_ROOT_OFFSET, %bx     movw  $FAT12_ROOT_ENTRIES, %dx     jmp   _findFileInitValues_findFileIn:     movw  $0x000b  , %cx     movw  4(%bp)   , %si     leaw  (%bx)    , %di     repe  cmpsb     je    _findFileOut_findFileDecrementCount:     decw  %dx     addw  $0x0020, %bx_findFileInitValues:     cmpw  $0x0000, %dx     jne   _findFileIn     je    _abort_findFileOut:     addw  $0x001a  , %bx     movw  %es:(%bx), %ax     movw  %bp, %sp     popw  %bpret/* функция для преобразования заданного кластера в номер сектора *//* параметры: номер кластера                                     */_clusterToLinearBlockAddress:     pushw %bp     movw  %sp    , %bp     movw  4(%bp) , %ax_clusterToLinearBlockAddressIn:     subw  $0x0002, %ax     movw  $BOOT_DISK_SECTORS_PER_CLUSTER, %cx     mulw  %cx     addw  $FAT12_ROOT_POSITION, %ax     addw  $FAT12_ROOT_SIZE, %ax_clusterToLinearBlockAddressOut:     movw  %bp    , %sp     popw  %bpret

Общая сводка


_initEnvironment:
Функция, отвечающая за установку сегментных регистров.
Аргументов не требует.

Применение: call _initEnvironment

_writeString:
Функция для отображения на экране строки с завершающим нулем.
В качестве аргумента получает строковую переменную с завершающим нулем.

Применение:
pushw <строковая переменная>
call _writeString
addw $0x02, %sp


readSector:
Макрос для считывания заданного сектора с диска и его загрузки в целевой адрес памяти.
Количество аргументов: 4.

Применение:
pushw <номер сектора>
pushw <адрес>
pushw <смещение>
pushw <всего секторов>
call _readSector
addw $0x0008, %sp


findFile:
Функция для проверки наличия файла.
Количество аргументов: 1

Применение:
pushw <target file variable>call _findFileaddw $0x02, %sp 


clusterToLinearBlockAddress:
Макрос для преобразования ID заданного кластера в номер сектора.
Количество аргументов: 1

Применение:
pushw <ID кластера>call _clusterToLinearBlockAddressaddw $0x02, %sp


loadFile:
Макрос для загрузки целевого файла в память с последующей передачей ему права выполнения.
Количество аргументов: 1

Применение:
pushw <целевой файл>call _loadFileaddw $0x02, %sp


Файл: stage0.ld


Этот файл служит для линковки файла stage0.object.

/********************************************************************************* *                                                                               * *                                                                               * *    Name       : stage0.ld                                                     * *    Date       : 23-Feb-2014                                                   * *    Version    : 0.0.1                                                         * *    Source     : assembly language                                             * *    Author     : Ashakiran Bhatter                                             * *                                                                               * *                                                                               * *********************************************************************************/SECTIONS{     . = 0x7c00;     .text :     {          _ftext = .;     } = 0}

Файл: bochsrc.txt


Файл-конфигурации, необходимый для запуска эмулятора bochs.

megs: 32floppya: 1_44=../iso/stage0.img, status=insertedboot: alog: ../log/bochsout.txtmouse: enabled=0 

Мини-проект: написание 16-битного ядра


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

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

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

Файл: kernel.c


/********************************************************************************* *                                                                               * *                                                                               * *    Name       : kernel.c                                                      * *    Date       : 23-Feb-2014                                                   * *    Version    : 0.0.1                                                         * *    Source     : C                                                             * *    Author     : Ashakiran Bhatter                                             * *                                                                               * *    Описание: За загрузку этого файла отвечает stage0.bin, который передает    * *                 ему право выполнения. Его функциональность                    * *                 заключается в отображении экрана-заставки и командной строки. * *    Внимание   : Вводить команды бессмысленно, так как они не запрограммированы*                                                                            *                                                                               * *********************************************************************************//* генерирует 16-битный код                                  */__asm__(".code16\n");/* переход к основной функции                                */__asm__("jmpl $0x1000, $main\n");#define TRUE  0x01#define FALSE 0x00char str[] = "$> ";/* функция установки регистров и стека *//* параметры: none                     */void initEnvironment() {     __asm__ __volatile__(          "cli;"          "movw $0x0000, %ax;"          "movw %ax, %ss;"          "movw $0xffff, %sp;"          "cld;"     );     __asm__ __volatile__(          "movw $0x1000, %ax;"          "movw %ax, %ds;"          "movw %ax, %es;"          "movw %ax, %fs;"          "movw %ax, %gs;"     );}/* VGA-функции. *//* функция для установки режима VGA на 80*24   */void setResolution() {     __asm__ __volatile__(          "int $0x10" : : "a"(0x0003)     );}/* функция очистки буфера экрана разделяющими пробелами */void clearScreen() {     __asm__ __volatile__ (          "int $0x10" : : "a"(0x0200), "b"(0x0000), "d"(0x0000)     );     __asm__ __volatile__ (          "int $0x10" : : "a"(0x0920), "b"(0x0007), "c"(0x2000)     );}/* функция установки позиции курсора на заданный столбец и строку */void setCursor(short col, short row) {     __asm__ __volatile__ (          "int $0x10" : : "a"(0x0200), "d"((row <<= 8) | col)     );}/* функция включения и отключения курсора */void showCursor(short choice) {     if(choice == FALSE) {          __asm__ __volatile__(               "int $0x10" : : "a"(0x0100), "c"(0x3200)          );     } else {          __asm__ __volatile__(               "int $0x10" : : "a"(0x0100), "c"(0x0007)          );     }}/* функция инициализации режима VGA на 80*25,            *//* очистки экрана и установки положения курсора на (0,0) */void initVGA() {     setResolution();     clearScreen();     setCursor(0, 0);}/* I/O-функции. *//* функция для получения символа с клавиатуры без эха*/void getch() {     __asm__ __volatile__ (          "xorw %ax, %ax\n"          "int $0x16\n"     );}/* эта функция аналогична getch(),                                 *//* но возвращает скан-код клавиши и соответствующее значение ascii */short getchar() {     short word;     __asm__ __volatile__(          "int $0x16" : : "a"(0x1000)     );     __asm__ __volatile__(          "movw %%ax, %0" : "=r"(word)     );     return word;}/* функция для отображения нажатых клавиш на экране*/void putchar(short ch) {     __asm__ __volatile__(          "int $0x10" : : "a"(0x0e00 | (char)ch)     );}/* функция вывода на экран строки с завершающим нулем */void printString(const char* pStr) {     while(*pStr) {          __asm__ __volatile__ (               "int $0x10" : : "a"(0x0e00 | *pStr), "b"(0x0002)          );          ++pStr;     }}/* функция, вызывающая задержку на несколько секунд */void delay(int seconds) {     __asm__ __volatile__(          "int $0x15" : : "a"(0x8600), "c"(0x000f * seconds), "d"(0x4240 * seconds)     );}/* Строковая функция. *//* эта функция вычисляет длину строки и возвращает ее */int strlength(const char* pStr) {     int i = 0;     while(*pStr) {          ++i;     }     return i;}/* Функция UI. *//*эта функция отображает логотип */void splashScreen(const char* pStr) {     showCursor(FALSE);     clearScreen();     setCursor(0, 9);     printString(pStr);     delay(10);}/* Оболочка. *//* функция для отображения фиктивной командной строки.                  *//* При нажатии клавиши Ввод выполняется переход на следующую строку     */void shell() {     clearScreen();     showCursor(TRUE);     while(TRUE) {          printString(str);          short byte;          while((byte = getchar())) {               if((byte >> 8)  == 0x1c) {                    putchar(10);                    putchar(13);                    break;               } else {                    putchar(byte);               }          }     }}/* точка входа в ядро */void main() {     const char msgPicture[] =              "                     ..                                              \n\r"             "                      ++`                                            \n\r"             "                       :ho.        `.-/++/.                          \n\r"             "                        `/hh+.         ``:sds:                       \n\r"             "                          `-odds/-`        .MNd/`                    \n\r"             "                             `.+ydmdyo/:--/yMMMMd/                   \n\r"             "                                `:+hMMMNNNMMMddNMMh:`                \n\r"             "                   `-:/+++/:-:ohmNMMMMMMMMMMMm+-+mMNd`               \n\r"             "                `-+oo+osdMMMNMMMMMMMMMMMMMMMMMMNmNMMM/`              \n\r"             "                ```   .+mMMMMMMMMMMMMMMMMMMMMMMMMMMMMNmho:.`         \n\r"             "                    `omMMMMMMMMMMMMMMMMMMNMdydMMdNMMMMMMMMdo+-       \n\r"             "                .:oymMMMMMMMMMMMMMNdo/hMMd+ds-:h/-yMdydMNdNdNN+      \n\r"             "              -oosdMMMMMMMMMMMMMMd:`  `yMM+.+h+.-  /y `/m.:mmmN      \n\r"             "             -:`  dMMMMMMMMMMMMMd.     `mMNo..+y/`  .   .  -/.s      \n\r"             "             `   -MMMMMMMMMMMMMM-       -mMMmo-./s/.`         `      \n\r"             "                `+MMMMMMMMMMMMMM-        .smMy:.``-+oo+//:-.`        \n\r"             "               .yNMMMMMMMMMMMMMMd.         .+dmh+:.  `-::/+:.        \n\r"             "               y+-mMMMMMMMMMMMMMMm/`          ./o+-`       .         \n\r"             "              :-  :MMMMMMMMMMMMMMMMmy/.`                             \n\r"             "              `   `hMMMMMMMMMMMMMMMMMMNds/.`                         \n\r"             "                  sNhNMMMMMMMMMMMMMMMMMMMMNh+.                       \n\r"             "                 -d. :mMMMMMMMMMMMMMMMMMMMMMMNh:`                    \n\r"             "                 /.   .hMMMMMMMMMMMMMMMMMMMMMMMMh.                   \n\r"             "                 .     `sMMMMMMMMMMMMMMMMMMMMMMMMN.                  \n\r"             "                         hMMMMMMMMMMMMMMMMMMMMMMMMy                  \n\r"             "                         +MMMMMMMMMMMMMMMMMMMMMMMMh                      ";     const char msgWelcome[] =              "              *******************************************************\n\r"             "              *                                                     *\n\r"             "              *        Welcome to kirUX Operating System            *\n\r"             "              *                                                     *\n\r"             "              *******************************************************\n\r"             "              *                                                     *\n\r"              "              *                                                     *\n\r"             "              *        Author : Ashakiran Bhatter                   *\n\r"             "              *        Version: 0.0.1                               *\n\r"             "              *        Date   : 01-Mar-2014                         *\n\r"             "              *                                                     *\n\r"             "              ******************************************************";     initEnvironment();      initVGA();     splashScreen(msgPicture);     splashScreen(msgWelcome);     shell();      while(1);}

Общая сводка


initEnvironment():
  • Устанавливает сегментные регистры и формирует стек.
  • Количество аргументов: none


Применение: initEnvironment();

setResolution():
Устанавливает разрешение экрана 80*25.
Количество аргументов: none.

Применение: setResolution();

clearScreen():
Заполняет буфер экрана пробелами.
Количество аргументов: none

Применение: clearScreen();

setCursor():
Устанавливает курсор в заданное положение на экране.
Количество аргументов: 2.

Применение: setCursor(столбец, строка);

showCursor():
По желанию пользователя активирует или отключает курсор.
Количество аргументов: 1.

Применение: showCursor(1);

initVGA():
Устанавливает разрешение 80*25, очищает экран и устанавливает курсор в позицию (0,0).
Количество аргументов: none

Применение: initVGA();

getch():
Регистрирует нажатия клавиш без эха.
Количество аргументов: none

Применение: getch();

getchar():
Возвращает скан-код нажатой клавиши и соответствующее значение ascii.
Количество аргументов: none.

Применение: getchar();

putchar():
Отображает символы нажатых клавиш на экране.
Количество аргументов: 1.

Применение: putchar(символ);

printString():
Выводит на экран строку с завершающим нулем.
Количество аргументов: 1.

Применение: printString();

delay():
Вызывает задержку на несколько секунд.
Количество аргументов: 1.

Применение: printString(строковая переменная с завершающим нулем);

strlength():
Возвращает значение длины строки с завершающим нулем.
Количество аргументов: 1.

Применение: strlength(строковая переменная с завершающим нулем);

splashScreen():
Отображает заданную картинку определенное время.
Количество аргументов: 1.

Применение: splashScreen(строковая переменная с завершающим нулем);

shell():
Отображает командную строку.
Количество аргументов: none.

Применение: shell();

Тестирование ядра


Использование исходного кода:
В прикрепленном архиве sourcecode.tar.gz находятся все исходные файлы и каталоги, необходимые для генерации исполняемых файлов.
Убедитесь, что вы являетесь супер-пользователем системы, после чего распакуйте архив.
Для перехода к компиляции и тестированию кода установите эмулятор bochs-x64 и GNU bin-utils.
После извлечения файлов вы увидите 5 каталогов:

bin
iso
kernel
log
src

Подготовив среду, откройте терминал и выполните следующие команды:
cd $(DIRECTORY)/src
make -f Makefile test
bochs

Сриншоты


Экран 1:
Это первый экран, отображаемый при выполнении ядра.



Экран 2:
Дальше идет экран приветствия:



Экран 3:
Это командная строка, в которой можно ввести текст.



Экран 4:
Здесь я привожу пример написания команд и перехода строки при нажатии Ввода.



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

Заключение


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

Подробнее..

Перевод Жизнь инженера Netflix дело о лишних 40 мс

16.01.2021 14:23:21 | Автор: admin
Приложение Netflix работает на сотнях смарт-телевизоров, потоковых пультах и приставках платного ТВ. Инженер-партнёр помогает производителям устройств запустить приложение Netflix на их устройствах. В этой статье я расскажу об одной особенно сложной проблеме, которая заблокировала запуск устройства в Европе.




Как начались странности


Ближе к концу 2017 года я участвовал в конференции-связи, где обсуждали проблему с приложением Netflix на новой ТВ-приставке. Эта приставка была новым устройством Android с воспроизведением 4K на базе Android Open Source Project (AOSP) 5.0 Lollipop. В Netflix я имел дело с несколькими устройствами, но это было моё первое устройство Android TV.

В этой конференции участвовали все четыре игрока рынка: крупная европейская компания платного телевидения (оператор), эта компания запускала устройство; подрядчик, который интегрировал прошивку ТВ-приставки (интегратор); производитель микросхем; я представлял Netflix.

Интегратор и Netflix уже закончили строгую сертификацию Netflix, но во время внутреннего испытания оператора телеканалов один из руководителей компании рассказал о серьёзной проблеме: воспроизведение Netflix на его устройстве прерывалось, то есть видео проигрывалось совсем недолго, затем останавливалось, проигрывалось и снова прерывалось. Случалось это не всегда, но, как правило, спустя несколько дней после включения устройства. На представленной видеозаписи баг выглядел ужасно.

Интегратор нашёл способ воспроизвести проблему: нужно было несколько раз запустить Netflix, начать воспроизведение, затем вернуться в пользовательский интерфейс устройства. Нам дали сценарий, чтобы повторять ситуацию автоматически. Иногда воспроизведение ситуации с помощью скрипта занимало пять минут, но баг проявлялся всегда.

Тем временем полевой инженер производителя микросхем диагностировал причину проблемы: приложение Netflix, Ninja для Android-TV, медленно подавало аудиоданные. Видео останавливалось из-за истощения буфера в конвейере аудиоустройства. Ролик замирал, пока декодер ждал данных от Ninja. Когда новые данные поступали, проигрыватель оживал.

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

Расследование


Я был настроен скептически. То же самое приложение Ninja работает на миллионах устройств Android TV, включая смарт-ТВ и другие приставки. Если ошибка в Ninja, то почему она проявляется только на Android 5.0?

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

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


Рисунок 1 Упрощённый конвейер воспроизведения

Давайте поговорим об аудио/видеоконвейере. Всё, вплоть до буфера декодера, одинаково на всех телевизионных приставках и смарт-ТВ, но перемещение аудио- и видеоданных в буфер декодера это процедура, которая зависит от устройства и работает в собственном потоке. Задача процедуры перемещения поддерживать заполненность буфера декодера через вызов API Netflix, этот вызов предоставляет следующий кадр аудио- или видеоданных.

В Ninja эта работа выполнялась с помощью Android Thread. Есть простой конечный автомат и логика для обработки разных состояний воспроизведения, но при нормальном воспроизведении поток копирует один кадр данных в API воспроизведения Android, а затем сообщает планировщику потоков, что он должен подождать 15 мс и снова вызвать обработчика. Когда вы создаёте поток Android, можно запросить, чтобы поток запускался повторно, как если бы в цикле; но это планировщик потоков Android и он вызывает обработчика, а не ваше собственное приложение.

Чтобы воспроизвести видео со скоростью 60 кадров в секунду (наивысшей чистотой кадров в Netflix), устройство должно отображать новый кадр каждые 16,66 мс, поэтому наличие нового сэмпла проверяется каждые 15 мс. Этого времени достаточно, чтобы опережать любой видеопоток Netflix.

Интегратор определил, что проблема кроется в аудиопотоке, поэтому я сосредоточился на конкретном обработчике потока, который доставлял аудиосэмплы в аудиосервис Android. Где же лишние миллисекунды?

Я предположил, что виновата какая-то вызванная обработчиком функция, поэтому прописал логирование по всему обработчику, предполагая, что код-виновник станет очевиден. Вскоре стало ясно, что в обработчике не было ничего плохого: даже когда воспроизведение прерывалось, он отрабатывал за несколько миллисекунд.

Прозрение


В конце концов я обратил внимание на три цифры: скорость передачи данных, время вызова обработчика и время, когда он передаёт управление обратно в Android. Я написал сценарий, чтобы проанализировать вывод логирования, и построил график, который ответил на мой вопрос.


Рисунок 2 Визуализация пропускной способности аудио и синхронизации обработчика потоков

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

  1. Две высокие шипованные части, где скорость передачи данных достигает 500 байт/мс. Эта фаза буферизации, перед началом воспроизведения. Обработчик копирует данные так быстро, как только может.
  2. Область посередине это нормальное воспроизведение. Аудиоданные перемещаются со скоростью около 45 байт/мс.
  3. Область заикания находится справа, где аудиоданные движутся со скоростью ближе 10 байт/мс. Это недостаточно быстро, чтобы воспроизведение продолжалось.

Неизбежный вывод оранжевая линия подтверждает то, что рассказал инженер производителя микросхем: Ninja медленно передаёт данные. Чтобы понять причину, давайте посмотрим, о чём свидетельствуют жёлтые и серые линии. Жёлтая линия показывает время, проведённое в самой подпрограмме обработчика, это время рассчитывалось по записанным вверху и внизу обработчика отметкам.
И при нормальном воспроизведении, и при воспроизведении с заиканием время в обработчике было одинаковым: около 2 мс. Пики показывают случаи, когда выполнение замедлялось из-за затрат на другие задачи устройства.

Корень проблемы


Серая линия, время между вызовами обработчика, свидетельствует о другом. Когда видео проигрывается нормально, видно, что обработчик вызывается каждые 15 мс. Когда видео прерывается (справа), обработчик вызывается примерно каждые 55 мс. Между вызовами есть лишние 40 мс, а значит, успеть за воспроизведением невозможно. Но почему?

Я рассказал о своём открытии интегратору и производителю микросхем (посмотрите, виноват планировщик потоков Android!). Но они продолжали сопротивляться: почему бы вам просто не копировать новые данные, когда вызывается обработчик? Критика была справедливой, но, если переписать код таким образом, это повлечёт за собой больше изменений, чем я был готов внести, поэтому я решил продолжить поиск первопричины.

Я погрузился в исходный код Android и узнал, что потоки Android это конструкция пользовательского пространства, а планировщик потоков, чтобы определять время, использует системный вызов epoll(). Производительность epoll() не гарантируется, поэтому я подозревал, что на эту функцию влияет что-то системное.
И здесь меня спас другой инженер поставщика микросхем, который обнаружил ошибку; эту ошибку исправили в следующей версии Android Marshmallow. Планировщик потоков Android изменяет поведение потоков в зависимости от того, работает ли приложение в фоновом режиме или на переднем плане. Потокам в фоновом режиме задается дополнительное время ожидания в 40000000 нс. Ошибка в глубине самой Android означала, что дополнительное время возникает, когда поток перемещается на передний план.

Обычно поток обработчика звука создавался, когда приложение выполнялось на переднем плане, но иногда поток создавался немного раньше. Такое случалось, когда приложение Ninja работало в фоновом режиме и тогда проигрыватель останавливался.

Извлеченные уроки


Это была не последняя исправленная на этой платформе ошибка, но именно её было труднее всего отследить. Баг скрывался за пределами Netflix, не в конвейере воспроизведения; вместе с тем все исходные данные указывали на ошибку в приложении Netflix.

Эта история показывает одну из сторон моей любимой работы: я не могу предугадать все проблемы, которые наши партнёры перебросят мне, кроме того, я знаю, что для разрешения проблем мне нужно разбираться в нескольких системах, работать с замечательными коллегами и постоянно заставлять себя узнавать что-то новое. Моя работа прямо влияет на людей, на их удовольствие от отличного продукта. Я понимаю, что, когда люди наслаждаются Netflix в своей гостиной, именно я тот человек в команде, который делает это возможным.




Подробнее..

Перевод Человеческое эго и стремления движущие силы инженерных решений

31.12.2020 16:11:51 | Автор: admin
Вы думаете, что выбираете технологию потому, что она подходит требованиям? Вы можете ошибаться.

Давайте начнём с примера, который, возможно, вдохновлён реальной ситуацией. Команде необходимо подобрать брокера событий. Претендента два Kafka и Pulsar.

Разработчик А имеет значительный опыт с Kafka в реальных ситуациях. Упоминают сложность при масштабировании Kafka и поручаются Pulsar. Разработчик B сторонник Kafka, так как технология стала стандартом индустрии и имеет сильную поддержку в целом. Но у команды мало опыта работы с ней. Оба согласны в том, что в обозримом будущем изменений рабочей нагрузки нет и два этих решения соответствуют требованиям. Но остальные члены команды не так самоуверенны.

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

Но раскрыты ли истинные мотивы выбора?





Человек это сложно


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

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

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

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

Пересмотр процесса принятия решения


Разработчик A талантливый специалист широкого профиля. Такие специалисты предпочитают широту знаний, а не их глубину мастер на все руки вовсе не мастер. Таким людям быстро надоедает работать над одним предметом. Естественно, когда нужно выбрать технологию, такие люди склонны отдавать предпочтение всему новому.

Разработчик B консервативнее. Такие люди по-настоящему увлечены некоторыми технологиями и хотят овладеть ими. Они склонны выбирать знакомое. Они также не очень напористы, и, хотя могут быть увлечены технологиями, им трудно говорить и делиться мыслями.

Оба они также строят свою карьеру, один как универсал, а другой как специалист. Освоение технологии или нового подхода к делу небольшой толчок в карьере.

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

Так что же мы должны делать?

Делаем невидимое видимым


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

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

Критерии
Макс рейтинг
Технология 1
Технология 2
Функции
100
Безопасность
100
Экосистема
50
Стоимость установки
30
Стоимость технического обслуживания
150
Стоимость лицензии
150
Совместимость
50


Это основные вещи. Затем мы добавляем субъективные элементы. Вы должны предложить и обсудить новые пункты с командой.

Критерии
Макс рейтинг
Технология 1
Технология 2
Карьерный интерес
30
Популярность
50
Знакомство
30


Конечно, это всего лишь пример. Нужно соответствие устремлениям команды. Эти критерии можно понимать так:

  • Карьерный интерес понятен сам по себе, это критерий о том, как технология вписывается в карьерные планы.
  • Популярность технологии: тяга к новизне, время, чтобы понять облачную технологию, о которой все говорят на встречах.
  • Комфорт ознакомления с технологией, с которой я, возможно, не знаком.

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

Право не соглашаться


Очевидно, окончательный выбор может не соответствовать холодной арифметической истине, особенно если оценки равны. В таких ситуациях лидер группы, как правило, делает окончательный выбор направления. Вполне естественно, что человек в команде может иметь твёрдое убеждение в другом выборе и быть обескураженным. Вы должны признать это несогласие. На самом деле долг лидера поощрять разногласия. Классическая история от Питера Друкера хорошо резюмирует:

Несогласие необходимо, чтобы стимулировать воображение (Питер Друкер)

Поощрение разногласий единственный способ избежать ловушки консенсуса. Но в конце концов люди должны не соглашаться и брать на себя обязательства. Эта фраза стала известной благодаря Джеффу Безосу в одном из его писем инвесторам:

Используйте фразу Можно не соглашаться, но сделать. Эта фраза сэкономит вам кучу времени. Если у вас есть уверенность в определённом направлении, но нет консенсуса по нему, полезно сказать: Слушайте, я знаю, что мы не сходимся по этому вопросу, но вы могли бы рискнуть? Не согласиться и сделать? Никто не знает правильного ответа.

Но зачем людям принимать решение, с которым они изначально не согласны? Просто потому, что у каждого была возможность высказать свое мнение и быть выслушанным. Важно, что у команды была возможность выразить не только свои логические аргументы, но и свои устремления. Таким образом, окончательное решение хорошо сформулировано и всем легче его принять.

Заключение


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

Это финальный материал в нашем блоге в 2020 году. Поздравляем всех хабравчан с наступающим Новым Годом и желаем удачи, развития, здоровья, счастья, релизов без багов, работы без кранчей, компилинга без ошибок! Увидимся уже в 2021 :-)


image



Подробнее..

Перевод Человеческий организм различает только две поры года

11.01.2021 12:12:41 | Автор: admin

Новое исследование показало, что биологически человек различает только зиму и весну.

Осенью желтеют листья, зимой выпадает первый снег, с началом весны зацветают нарциссы, летом дни долгие и жаркие в умеренных широтах Европы и Америки четыре поры года в существенной степени определяют жизнь людей. Но оказывается, что у человеческого организма график другой.

Стэнфордский генетик Майкл Снайдер в своем недавнем исследовании, опубликованном в научном журнале Nature Communications, изучал изменение биологических показателей человека в течение года. Огромное количество данных более 1000измерений от более чем 100человек с оценкой генов, белков, метаболических маркеров, маркеров иммунной системы и микробиома позволило выяснить, что наш организм различает не четыре поры года, а всего две: первая начинается с наступлением зимы, вторая в середине весны.

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

Elemental: Почему вы заинтересовались сезонными изменениями в организме?

Майкл Снайдер: Я как-то задумался, почему мы считаем, что есть четыре поры года? Это же, по сути, произвольное число: может, их всего три, а может, вообще 15 неизвестно. Поэтому я решил получить данные, которые могли бы показать, сколько времен года есть на самом деле по крайней мере, с точки зрения человеческой биологии. Хотелось понять, удастся ли обнаружить закономерности, которые указывают на сезонные изменения? Так что поводом для исследования послужило сочетание двух факторов: желание понять закономерности работы человеческого организма, относящиеся к здоровью, и понимание того, что времена года довольно условное разделение, если задуматься.

Что вы обнаружили?

Мы в течение нескольких лет наблюдали группу из 109человек и всего провели более тысячи измерений. В полученных данных мы искали биологические закономерности. Мы начали с отдельных молекул и увидели [закономерности], которые уже были известны: например, что уровень гемоглобина A1c [показатель, отражающий среднее содержание сахара в крови] достигает пика весной. Но, конечно, обнаружилось и много новых [закономерностей], поскольку были изучены данные по множеству молекул. Например, было известно, что [экспрессия] гена циркадного ритма CIR1 колеблется в течение дня. Но мы обнаружили, что есть и сезонная закономерность с пиками в конце апреля начале мая. Кроме того, изменения были и в различных цитокинах [белки иммунной системы], которые участвуют в борьбе с вирусными заболеваниями. Также нас очень интересовал микробиом, который ранее был уже немного изучен, но, опять же, не так широко, как в нашем исследовании, и в нем тоже было обнаружено немало изменений.

Отметив изменения в отдельных молекулах, мы задались вопросом: Если объединить данные по всем молекулам, получится ли вывести основные закономерности? Оказалось, такие закономерности и правда есть. Мы обнаружили два биологических сдвига [по данным 109человек]. Первый вполне ожидаемое изменение в конце декабря начале января: это, можно сказать, начало биологической зимы. Можно было бы подумать, что второе изменение должно быть в июле или августе, когда очень жарко, то есть, в конце лета. Но это не так: второе изменение приходится на конец апреля начало мая, и это, по крайней мере для меня, оказалось неожиданностью.

В исследовании участвовали жители Северной Калифорнии понятно, что времена года там не такие, как в Новой Англии или на юго-востоке. Как вы думаете, обнаруженная вами двухсезонная закономерность так же проявится и в других географических регионах или она определяется местными погодными условиями и изменениями температуры?

Не знаю. Думаю, использованный нами подход можно применить в любой точке мира нужно просто получить данные. Нет причин, по которой биологический год должен разделяться именно на две части: в одном месте их может быть три, в другом и все 10. Кто знает? Думаю, это было бы очень интересно выяснить.

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

Мы старались измерить показатели как можно большего числа молекул. Некоторые из них клинические маркеры, но в основном это РНК транскрипты, белки и метаболиты. Их мы и измеряли всего это около 20000 молекул, плюс бактерии.

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

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

Переход организма в зимний режим вполне можно было ожидать из-за распространения вирусных инфекций и подобного [и изменения биологических показателей]. Но есть и еще кое-то интересное [особенно в отношении микробиома]. Например, на зиму приходится пик численности бактерий, вызывающих акне. Конец весны (начало апреля) время проявления симптомов астмы и аллергии [которые вызывают множество молекулярных изменений], и это тоже вполне ожидаемо. Но есть и много метаболических изменений, которые оказались для меня неожиданными. Мы объясняем эти закономерности тем, что калифорнийцы в сезон дождей как бы впадают в спячку: физическая активность у них становится меньше, чем летом. Соответствующие изменения накапливается до марта и апреля, и когда люди выходят из спячки, начинают чаще заниматься физической активностью, их метаболические показатели и состояние сердечно-сосудистой системы улучшаются. Если подумать, то в этом есть смысл.

Расскажите подробнее о взаимосвязи между окружающей средой и здоровьем. Например, наблюдаемые изменения в иммунной системе и воспалительные процессы вызваны исключительно увеличением числа вирусных инфекций зимой или что-то делает людей более восприимчивыми к инфекциям?

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

Весенний переход объяснить намного сложнее. В нем определенно есть признаки вклада аллергии и астмы, но мы видим изменения и в метаболических маркерах например, в гемоглобине A1c, связанном с диабетом и инсулинорезистентностью. Некоторые маркеры диабета 2-го типа показывают высокий уровень именно весной то же относится к маркерам сердечно-сосудистых заболеваний. Думаю, это также связано с внешней средой в данном случае мы можем соотнести эти изменения с наличием пыльцы. Согласно данным, которые мы еще не опубликовали, есть корреляция между внешним воздействием и некоторыми внутренними метаболитами то есть, внешние факторы (например, пыльца), могут коррелировать с метаболическими изменениями внутри организма. Поэтому мы считаем, что определенные маркеры коррелируют с изменениями окружающей среды, а другие с образом жизни.

Что дает нам знание о биологических временах года?

Думаю, на это можно посмотреть с двух сторон. Во-первых, в медицине лонгитюдные данные [полученные за длительный промежуток времени] используются не очень активно. Меня расстраивает, что в кабинете врача данные пациента будут сравнивать с характеристиками популяции: тенденции никто не определяет, а я считаю, что это как раз очень важно. Например, если гемоглобин A1c повышается, за этой тенденцией важно проследить. Но полезно и знать, какое влияние оказывают сезонные изменения. Так, если какой-то показатель немного повышается в конце апреля начале мая, можно сказать: Это может быть сезонным сдвигом из-за снижения физической активности к лету всё вернется в норму. Но если что-то начинает существенно изменяться зимой, это укажет на то, что в организме что-то происходит и хорошо, если вы это заметили. Так что полученные нами данные можно принимать во внимание, оценивая показатели, относящиеся к состоянию здоровья.

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


О переводчике

Перевод статьи выполнен в Alconost.

Alconost занимается локализацией игр, приложений и сайтов на 70 языков. Переводчики-носители языка, лингвистическое тестирование, облачная платформа с API, непрерывная локализация, менеджеры проектов 24/7, любые форматы строковых ресурсов.

Мы также делаем рекламные и обучающие видеоролики для сайтов, продающие, имиджевые, рекламные, обучающие, тизеры, эксплейнеры, трейлеры для Google Play и App Store.

Подробнее..

Перевод Git, я хочу все отменить! Команды исправления допущенных ошибок

25.12.2020 14:07:02 | Автор: admin
image

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

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

Черт, я сделал что-то не то. Дайте мне волшебную машину времени!

git reflog
# you will see a list of every thing you've
# done in git, across all branches!
# each one has an index HEAD@{index}
# find the one before you broke everything
git reset HEAD@{index}
# magic time machine


image

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

Я сделал коммит, но сразу же заметил ошибку, ее нужно исправить!

# make your change
git add . # or add individual files
git commit --amend --no-edit
# now your last commit contains that change!
# WARNING: never amend public commits


Команда дает возможность поправить неприятные мелочи когда вы что-то закоммитили, а потом увидели проблему вроде отсутствующего пробела после знака "=". Да, есть возможность внести изменения новым коммитом, объединив два варианта при помощи rebase -i. Но это долгий путь.

NB! Никогда не изменяйте коммиты в публичной ветке. Применяйте команду лишь для коммитов в локальной ветке, иначе у вас будут проблемы.

Хочу изменить сообщение последнего коммита!

git commit --amend
# follow prompts to change the commit message


Это просто глупые требования к оформлению сообщений.

Я случайно закоммитил в мастер, хотя это должен был в новую ветку!

# create a new branch from the current state of master
git branch some-new-branch-name
# remove the last commit from the master branch
git reset HEAD~ --hard
git checkout some-new-branch-name
# your commit lives in this branch now :)


Если вы уже закоммитили в публичную ветку, команды не сработают. В этом случае поможет git reset HEAD@{укажите количество коммитов, на которое нужно вернуться} вместо HEAD~.

Ну вот, я ошибочно закоммитил не в ту ветку

# undo the last commit, but leave the changes available
git reset HEAD~ --soft
git stash
# move to the correct branch
git checkout name-of-the-correct-branch
git stash pop
git add . # or add individual files
git commit -m "your message here";
# now your changes are on the correct branch


Есть еще один способ, который использует большое количество разработчиков это cherry-pick.

git checkout name-of-the-correct-branch
# grab the last commit to master
git cherry-pick master
# delete it from master
git checkout master
git reset HEAD~ --hard


Мне нужно запустить diff, но ничего не получается

Если вы уверены в том, что изменения были внесены, но diff пустой, вполне может быть, что вы индексировали изменения через add. Поэтому стоит использовать специальный флаг.

git diff --staged

В общем, это не баг, а фича, но она чертовски неочевидная \_()_/

Мне срочно нужно отменить коммит, который сделан 5 коммитов назад

# find the commit you need to undo
git log
# use the arrow keys to scroll up and down in history
# once you've found your commit, save the hash
git revert [saved hash]
# git will create a new commit that undoes that commit
# follow prompts to edit the commit message
# or just save and commit


К счастью, не нужно отказываться назад на 5 коммитов, занимаясь копипастом старых и новых файлов. Отменить все это можно при помощи revert.

Кроме того, откатить можно не только коммит, но и целый файл. Правда, это уже будут другие команды

Отменить изменения в файле

А вот и они, эти другие команды.

# find a hash for a commit before the file was changed
git log
# use the arrow keys to scroll up and down in history
# once you've found your commit, save the hash
git checkout [saved hash] -- path/to/file
# the old version of the file will be in your index
git commit -m "Wow, you don't have to copy-paste to undo"


Когда я впервые нашел эту возможность, это было КРУТО, КРУТО, К-Р-У-Т-О. Но если задуматься почему именно checkout лучший вариант для отмены изменений в файле? :shakes-fist-at-linus-torvalds:

Все, я сдаюсь

cd ..
sudo rm -r fucking-git-repo-dir
git clone https://some.github.url/fucking-git-repo-dir.git
cd fucking-git-repo-dir


Спасибо Eric V. За этот способ. И все жалобы по поводу использования sudo адресуйте ему.

Если вам нужно обнулить изменения и полностью откатиться до исходной версии, то можно попробовать сделать именно так. Но помните эти команды разрушительны и необратимы.

# get the lastest state of origin
git fetch origin
git checkout master
git reset --hard origin/master
# delete untracked files and directories
git clean -d --force
# repeat checkout/reset/clean for each borked branch


Внимание! Эта статья не является исчерпывающим руководством. И да, есть и другие способы сделать то же самое, причем еще лучше. Но я пришел именно к этим вариантам методом проб и ошибок. Потом у меня появилась сумасшедшая идея поделиться находками. Берите это или уходите!

image

Комментарий эксперта

Даниил Пилипенко, директор центра подбора IT-специалистов SymbioWay и евангелист бэкенд-направления онлайн-университета Skillbox, дополнил перевод мнением о Git и его актуальности для разработчиков.

Git появился в 2005-ом году, и он далеко не сразу занял рынок. Помню, когда мы ещё в 2008-ом году в команде разработчиков внедряли SVN. И даже в 2012-ом одна близкая ко мне компания усиленно внедряла Mercurial. С годами для многих стало очевидным, что Git это лучшая система контроля версий, и её теперь используют практически все разработчики.

Если вы начинающий разработчик и собираетесь устраиваться на работу, обязательно изучите Git! Вы должны знать, что такое система контроля версий и зачем она нужна, что такое коммит, ветка, как клонировать репозиторий и отправлять сделанные изменения на сервер, как получать новые изменения с сервера, как делать merge, какие бывают виды reset. Поначалу эта тема вам может показаться непонятной и сложной, но вам нужно лишь привыкнуть пользоваться Git, и отвыкнуть вы уже не сможете.
Подробнее..

Перевод Подарок на Рождество от программиста Alexa, WebSocket и мобильное приложение

25.12.2020 16:21:43 | Автор: admin
Каждый год я дарю брату рождественские подарки необычным способом. Это началось как шутка на Рождество, но в конце концов дошло до того, что я превращаю подарок в настоящее испытание. В прошлом году я заставил его писать и звонить подаркам, чтобы узнать, готовы ли они к открытию. За год до этого мой брат должен был провести некоторые исследования пород собак Американского клуба собаководов и воспользоваться их результатами, чтобы понять, в каком порядке открывать свои подарки. Но в этом году всё по-другому.

Я решил воспользоваться своим опытом программиста, чтобы подарить брату неповторимое рождественское утро с эффектом погружения. Я сделал игру, в которую брат сможет играть на своём телефоне, чтобы понять, как открывать свои подарки. В чём прикол? Единственная возможность управления игрой это команды голосовому помощнику Alexa через Echo.




Игра


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

В игре можно выбрать два действия: move или explore. Explore исследование комнаты в поисках лакомства и возможности открыть подарок, move перемещение в соседнюю комнату. По мере перемещения карта начинает заполняться, с каждым движением макет видно всё лучше. Во всех комнатах Alexa рассказывает часть истории. Чем глубже мой брат в подземелье, тем более запутанной становится история. Здорово, да?


Карта

Мобильное приложение


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

У приложения два экрана: страница инвентаря и страница карты. На странице с инвентарем показываются найденные подарки и их изображения. На странице карты показаны исследованные комнаты и места, где нашлись предметы.


Страницы мобильного приложения

Каждый раз, когда мне нужно что-то быстро разработать, я захожу в OutSystems. Это интуитивно понятная платформа для разработки с минимумом кода, которая позволяет быстро создавать реактивные веб-страницы, веб-сервисы и мобильные приложения. Что в ней самое лучшее? Её можно бесплатно использовать для проверки концепции! Есть несколько подвижных частей, создающих впечатление волшебства:

  • API.
  • Мобильное приложение.
  • Навыки Alexa.
  • WebSocket.

API


Для выполнения логики и хранения состояния у приложения должен быть мозг. Мозг лучшего рождественского подарка это простой API, созданный в OutSystems. Он загружает положение на карте, проверяет возможность движения, перемещает вас, а затем рассказывает вам небольшую часть истории. Приложение целиком имеет только две конечные точки API: одну для перемещения, а другую для получения текущего состояния. Получить текущее состояние можно только при выходе из приложения и возврате в него (я знаю, что брат иногда будет делать перерывы). За кулисами, ниже, есть модель данных, которая строит карту, сюжет, подарок в инвентаре и переходы между комнатами.


Диаграмма отношений в игре.

С помощью OutSystems я создал модель данных, логику перемещения персонажа, а также REST API для управления всем этим. Пришло время создать фронтенд!

Мобильное приложение


И снова нам поможет OutSystems. У сервиса простой пользовательский интерфейс, позволяющий перетаскивать компоненты на экран, а затем собрать всё это в мобильное приложение для вас. Итак, я приступил к работе и нарисовал две страницы, чтобы отслеживать, какие подарки были найдены и какие области на карте исследованы. Для загрузки данных карты и их отображения на экране я воспользовался API. Как я уже упоминал, это довольно простое мобильное приложение, на него можно только смотреть. Я поиграл с CSS, добавил несколько рождественских изображений и решил, что этого достаточно.

Навык Alexa


Каждый день на работе я использую AWS, так что довольно хорошо знаком со многими сервисами платформы. Но я никогда раньше не разрабатывал навыки Alexa. Подойдя к приставке, я начал смотреть видео. Оказывается, команда Alexa в AWS действительно знает, что делает. Я увидел одно из лучших представлений простого решения сложной проблемы. В навыке есть две составляющие:

  • Модель взаимодействия.
  • Код на бэкенде.

Модель взаимодействия просто отображает, какие слова или фразы вызывают конкретные действия. Вводятся несколько фраз, сопоставляются с интентами, затем вы нажимаете кнопку сборки приложения. Оставшаяся магия происходит в консоли.


Два интента для навыка Alexa.

Код на бэкенде писался так же легко. Можно написать код навыков в VS Code, с помощью расширения Alexa отправив его в облако. Это достаточно просто. Определите в коде, что будет делать каждый интент при вызове. В игре я только вызывал разработанное в OutSystems API, поэтому для каждого интента написал быстрый вызов с помощью axios к соответствующей конечной точке API, попросив Alexa повторить ответное сообщение. Готово!

WebSocket


Во время тестирования приложения я быстро понял, что кое-что забыл. Как обновить мобильное приложение, когда Alexa перемещает персонажа? Мне нужно было что-то, что передаёт данные в приложение всякий раз, когда происходит событие. И это был WebSocket. WebSocket, по сути, открывает двусторонний канал связи между браузером (или мобильным приложением) и сервером. Это позволяет получать сообщения сразу вместо того, чтобы постоянно опрашивать сервер на предмет обновлений.

Внутри WebSocket происходит много вещей. К счастью для меня, есть компании, сосредоточенные на упрощении всего этого для потребителей. Я создал бесплатную учётную запись на Pusher, сконфигурировал приложение несколькими щелчками мыши и был готов интегрировать Pusher в мой API и мобильное приложение. WebSocket можно представить себе как подход издатель/подписчик. Когда в системе что-то происходит, запускается событие, подписчик получает его и выполняет действие.

Я обновил API для публикации события Pusher всякий раз, когда персонаж перемещался или исследовал комнату. Кроме того, я включил в сообщение часть возвращённой API истории. В мобильное приложение я добавил простой фрагмент кода подписки на события на JavaScript. Подписка обновляет данные на экране и отображает новую часть истории. Быстрый тест через мою Echo показал, что персонаж перемещается по карте, как только я проговариваю слово. Круто, да?


Панель управления Pusher показывает график сообщений WebSocket.

Разоблачение


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

Верите или нет, но я не хотел, чтобы прошлогодний подарок расстроил моего брата. Я хочу, чтобы этот подарок был для него особенным, радостным случаем в жизни и возможностью развивать мои навыки разработчика и учиться для меня. Думаю, подарок в этом году хороший компромисс. Он тоже довольно сложный, но есть история, за которой нужно следить. Кроме того, мне приходится работать со многими современными, фантастическими технологиями.

И это бесплатно! Всё, что я сделал для этого подарка, возможно на уровне бесплатного использования, а это значит, что единственной затратой было моё время. Я потратил около 30 часов, чтобы придумать историю, нарисовать карту и собрать всё воедино. Это была инвестиция наверняка. На Рождество мы узнаем, стоила ли она того. Обязательно сообщу о результатах. С Рождеством!


Обучение со скидкой чем не подарок самому себе, в новом 2021 году? А промокод HABR сделает этот подарок еще приятнее, добавив 10% к скидке на баннере.
image


Подробнее..

Перевод 6 принципов эффективной визуализации данных

03.01.2021 20:22:24 | Автор: admin

Ключевые принципы создания полезных и информативных графиков


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

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

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

Эта статья также в значительной степени вдохновлена книгой Роджера Д. Пенга Exploratory Data Analysis in R Она доступна бесплатно на Bookdown, и вы можете прочитать её, чтобы узнать больше о EDA.

Давайте ближе познакомимся с этими принципами.


Пример визуализации данных на Our World in Data



1. Покажите сравнение (контрольная и экспериментальная группы)


Демонстрация сравнения основа хорошего научного исследования. Доказательства гипотезы всегда связаны с чем-то другим. Возьмём пример: вы говорите: Тёмный шоколад улучшает концентрацию внимания и способность к обучению. Важный вопрос в этом утверждении по сравнению с чем? Без сравнения (относительная гипотеза) утверждение бесполезно.

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

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

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

2. Причинно-следственная связь и объяснение


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

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

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

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

3. Данные со многими переменными (более двух переменных)


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

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

Возьмем парадокс Симпсона, парадокс в вероятностной статистике, когда при объединении групп исчезает тенденция, возникающая в разных группах данных. Чтобы проиллюстрировать:

  • Две переменные отрицательная связь.
  • Три переменные положительная связь (x, y, z) (есть путающие переменные).

4. Не позволяйте инструментам управлять анализом


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

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

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

5. Документируйте свои графики соответствующими метками, шкалами и источниками данных


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

6. Содержание превыше всего


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

Заключение


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

  1. Покажите сравнение.
  2. Покажите причины.
  3. Покажите многомерные данные.
  4. Объедините как можно больше доказательств.
  5. Опишите и документируйте график.
  6. Убедись, что ваша история интересна.

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

Я оставляю цитату американского математика Джона Тьюки, который открыл новую эру статистики:

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

Для более глубокого понимания этих принципов я рекомендую обратиться к книге Роджера Д. Пенга Exploratory Data Analysis in R (ссылку на нее я оставлю чуть ниже).

Ресурсы и ссылки


Если вы хотите узнать больше о визуализации данных, посмотрите эти замечательные бесплатные книги:


Платформы, которые демонстрируют красивые визуализации

Руководства по созданию графиков имеются на этих ресурсах

Ссылки для этой статьи


Спасибо, что прочитали!

image



Подробнее..

Перевод Как преобразовать аудиоданные в изображения

14.01.2021 14:20:31 | Автор: admin

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


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

Сегодня, специально к старту нового потока курса по машинному обучению делюсь с вами статьей, в которой авторы, в качестве примера определяют вид птиц по их пению. Они находят в записях, сделанных в естественных условиях, фрагменты с пением птиц, и классифицируют виды. Преобразовав аудиоданные в данные изображений и применив модели компьютерного зрения, авторы этой статьи получили серебряную медаль (как лучшие 2 %) на соревновании Kaggle Cornell Birdcall Identification.




Обработка аудио как изображений


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

Те же рассуждения применимы к задачам обнаружения звука. Есть спектрограммы четырёх видов птиц. Прослушать оригинальные отрезки звука можно здесь. Глазами человек тем более мгновенно увидит различия видов по цвету и форме.

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

Понимание спектрограммы


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


Объяснение параметров звуковых волн

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


Объяснение спектрограммы

Чтобы понять, как частоты отражаются в спектрограммах, посмотрите на трёхмерную визуализацию, которая демонстрирует амплитуду с помощью дополнительного измерения. По оси X отложено время, а по оси Y значения частот. Ось z это амплитуда звуков частоты координаты y в момент координаты x. По мере увеличения значения z цвет меняется с синего на красный, получается цвет, который мы видели в предыдущем примере 2D-спектрограммы.


Визуализация трёхмерной спектрограммы

Спектрограммы полезны, потому что они показывают именно ту информацию, которая нам нужна: частоты, особенности, которые формируют форму слышимого нами звука. Разные виды птиц и все издающие звуки объекты имеют свой собственный уникальный частотный диапазон, поэтому звуки кажутся разными. Наша модель просто должна научиться различать частоты, чтобы достичь идеальных результатов классификации.

Шкала мел и её спектрограмма


Мел психофизическая единица высоты звука, применяемая главным образом в музыкальной акустике. Название происходит от слова мелодия. Количественная оценка звука по высоте основана на статистической обработке большого числа данных о субъективном восприятии высоты звуковых тонов.

Однако человеческий слух не воспринимает различия во всех частотных диапазонах одинаково. По мере увеличения частот нам становится всё труднее различать их. Чтобы лучше имитировать поведение человеческого уха с помощью моделей глубокого обучения, мы измеряем частоты по шкале мел. В шкале мел любое равное расстояние между частотами звучит для человеческого уха одинаково. Единица мел (m) связана с герцами (f) таким уравнением:

$m = 2595 * log(1+f/700).$


Спектрограмма на мел-шкале это просто спектрограмма с частотами, измеренными в мел.

Как мы используем спектрограмму?


Для создания мел-спектрограммы из звуковых волн мы воспользуемся библиотекой librosa.

import librosay, sr = librosa.load('img-tony/amered.wav', sr=32000, mono=True)melspec = librosa.feature.melspectrogram(y, sr=sr, n_mels = 128)melspec = librosa.power_to_db(melspec).astype(np.float32)

Где y обозначает необработанные данные волны, sr обозначает частоту дискретизации аудио-сэмпла, а n_mels определяет количество полос мел в сгенерированной спектрограмме. При использовании метода melspectrogram вы также можете установить параметры метода f_min и f_max. Можно установить Then и преобразовать спектрограмму в спектрограмму мел, выражающую амплитуду на прямоугольной шкале, к децибелам с помощью метода power_to_db.

Чтобы визуализировать сгенерированную спектрограмму, запустите код:

import librosa.displaylibrosa.display.specshow(melspec, x_axis='time',  y_axis='mel', sr=sr, fmax=16000)

В качестве альтернативы: если вы используете графический процессор, то можете ускорить процесс генерации спектрограммы с помощью библиотеки torchlibrosa.

from torchlibrosa.stft import Spectrogram, LogmelFilterBankspectrogram_extractor = Spectrogram()logmel_extractor = LogmelFilterBank()y = spectrogram_extractor(y)y = self.logmel_extractor(y)

Резюме


В заключение скажу, что мы можем воспользоваться преимуществами последних достижений компьютерного зрения в задачах, связанных со звуком, путём преобразования данных аудиоклипов в данные изображения. Мы достигаем этого с помощью спектрограмм, показывающих сведения о частоте, амплитуде и времени аудиоданных в изображении. Использование шкалы мел и спектрограммы шкалы мел помогает компьютерам имитировать человеческий слух, чтобы различать звуки разных частот. Для генерации спектрограмм мы могли бы воспользоваться библиотекой librosa или torchlibrosa для ускорения GPU на Python. Рассматривая таким образом задачи, связанные со звуком, мы можем создавать эффективные модели глубокого обучения для выявления и классификации звуков, так же, как, например, врачи диагностируют сердечные заболевания с помощью ЭКГ.




Подробнее..

Опуститься до уровня руководителя?

26.12.2020 14:17:21 | Автор: admin

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

Красота в наших глазах. Но руководитель часто далек от технических деталей. Часто очень сложно объяснить всю программистскую кухню на языке диаграмм Ганта. Когда штудирование документации и вычитывание кода библиотеки выливается в метрику +10 строк кода. (За половину месяца.) Ну ведь правда, как-то не солидно 0.125 строк кода в день. (Это сколько символов в день? А в час?) Правда?


Мы правда должны опускаться до уровня руководителей?

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

Но все же, что же с диалогом?

Если задаться вопросом: а нужно ли пасовать?

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


А если о диалоге?

А может быть изящным обсудить будущее приложения и то, какие улучшения возможны? Обсудить, где выиграет бизнес? Многогранная красота...

Подробнее..

Перевод Как обучать огромные модели машинного обучения на случайных GPU

11.01.2021 14:11:20 | Автор: admin
Вы можете спросить: почему эти полумагические модели машинного обучения работают так хорошо? Короткий ответ: эти модели чрезвычайно сложны и обучаются на огромном количестве данных. На самом деле, Lambda Labs недавно подсчитала, что для обучения GPT-3 на одном GPU потребовалось бы 4,6 миллиона долларов если бы такое было возможно.

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

В этом посте представлена предыстория и практические шаги по обучению BERT с нуля в университете с использованием пакета HetSeq. Это адаптация популярного пакета PyTorch, которая предоставляет возможность обучать большие модели нейронных сетей на гетерогенной инфраструктуре.





Чтобы исправить эту ситуацию, мы недавно выпустили программный пакет под названием HetSeq адаптацию популярного пакета PyTorch, которая предоставляет возможность обучать большие модели нейронных сетей на гетерогенной инфраструктуре.

Эксперименты показывают, что систему на базе BERT можно за день обучить с помощью более чем 8 GPU, большинство из которых нам пришлось позаимствовать в неработающих лабораториях. Прежде чем мы представим HetSeq, нужна небольшая предыстория.

Типовое обучение нейронной сети


def train(args):    # main components    dataset = Dataset()    dataloader = DataLoader(dataset)    model = Model()    loss_ = Loss()    optimizer = Optimizer(model.parameters())    # specify the GPU and transfer model to the GPU     device = Device()     model.to(device)    model.train()        # actual training loops    for epoch in range(1, Max_Epoch):        for (data, target) in dataloader:            data, target = data.to(device), target.to(device)   # **load input data and target to GPU**            optimizer.zero_grad()            output = model(data)    # **forward compute the output of model given input data**            loss = loss_(output, target)   # **forward process to compute the real loss function**            loss.backward()    # **backward process to obtain the**            optimizer.step()    # **update parameters** 

Обучение на одном GPU

Этот код показывает этап обучения базовой модели контролируемого обучения на фреймворке нейронных сетей. Учитывая некоторую архитектуру, эта задача оптимизирует параметры модели через SGD на функции потерь между предсказанными экземплярами и наблюдаемой истиной.

Фактически процесс обучения состоит из четырёх отдельных этапов: (1) загрузка данных, (2) прямой проход, (3) обратный проход, (4) обновление.

1. Загрузка данных


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


Прямой проход с одним GPU

2. Прямой проход


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

3. Обратный проход


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


Параметры обновления с единственным GPU

4. Обновление


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

Краткое описание этапов обучения


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

Что делать, если у нас несколько GPU?


Поскольку пакеты данных независимы друг от друга, довольно просто распараллелить этот процесс, отправив разные пакеты данных на разные GPU. Затем, если мы сможем каким-то образом объединить вычисленные потери и синхронизировать обновлённые параметры модели, тогда получится сделать обучение намного быстрее.

def torch.nn.parallel.DistributedDataParallel(    module,  # pre-defined model    device_ids=None, # input device_ids    output_device=None,  # output device_ids, in our case, input device = output device = single GPU    dim=0,     broadcast_buffers=True, # set to False in our implementation    process_group=None, # Core part    bucket_cap_mb=25,     find_unused_parameters=False,     check_reduction=False)view raw

Класс параллельного распределения данных

Это не новая идея. В PyTorch мы используем для модели модуль torch.nn.parallel.DistributedDataParallel (DDP) вместо модуля torch.nn.Module. Каждый GPU это отдельный процесс, и связь между ними осуществляется с помощью стандартного IPC. Но это ещё не всё. Четыре шага требуют некоторой настройки.

1. Загрузка данных с помощью DDP


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

Это основная идея параллельного распределения данных (DDP): каждый GPU имеет идентичные параметры модели, но одновременно обрабатывает разные пакеты данных.


Прямой проход с несколькими GPU

2. Прямой проход с DDP


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

3. Обратный проход с DDP


Мы используем средние потери для получения градиентов параметров модели при обратном проходе. Прежде чем начать, необходимо сообщить о средних потерях на разные GPU, чтобы параметры модели могли оставаться синхронизированными. Как только одни и те же параметры на разных графических процессорах получают свои градиенты, выполняется синхронизация последних, чтобы они были одинаковы.


Синхронизация градиента

4. Обновление с DDP


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

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

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

def train_multiple_GPUs(args, device_id):    # main components    dataset = Dataset()    dataloader = DataLoader(dataset)    model = DDP(Model())    loss_ = Loss()    optimizer = Optimizer(model.parameters())    device = Device(device_id)     model.to(device)    model.train()        # actual training loops    for epoch in range(1, Max_Epoch):        for (data, target) in dataloader:            data, target = data.to(device), target.to(device)              optimizer.zero_grad()            model.synchronization()    #  parameter synchronization            output = model(data)                loss = loss_(output, target)            loss_average = average(loss)            loss.backward()            model.parameter.grad.average()            optimizer.step()

Обучение на нескольких GPU

Масштабирование несколько узлов с несколькими GPU


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

def torch.distributed.init_process_group(            backend=args.distributed_backend,    # 'nccl' is the best available backend for GPU            init_method=args.distributed_init_method,    # 'tcp' or shared file system            world_size=args.distributed_world_size,  # number of nodes in total            rank=args.distributed_rank, # index of current node        )

К счастью, тот же механизм, который используется для адресации процессоров на одном узле, можно распространить на несколько узлов. Вы можете просто установить индекс узла, то есть параметр rank в функции init_process_group глобально, чтобы каждый GPU имел уникальный идентификатор относительно всех узлов.

Коммуникация вот где возникают сложности



Внутриузловая и межузловая коммуникация

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

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

Когда родители заставляют вас делиться игрушками


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

  1. Некоторые игрушки имеют сложные инструкции. Распределённая параллельная обработка данных пакета (DDP) это боль, трудно понять её и заставить работать. Особенно верно это для большинства исследователей машинного обучения, которые не очень хорошо разбираются в особенностях распределённых вычислений. В дополнение к базовой настройке DDP мирный тренировочный запуск различных архитектур GPU на многих узлах требует тщательного разделения данных и изнурительного налаживания связи между GPU и узлами.
  2. С какими-то игрушками лучше играть лучше, чем с другими. В гетерогенной системе некоторые GPU работают быстрее других, а некоторые имеют больше памяти, чем у других. Это означает, что какие-то процессоры получают больше данных для обработки, чем другие, что прекрасно; но это также означает, что средние значения градиентов и обновления параметров должны тщательно взвешиваться.
  3. Родители не разрешают нам играть с какими-то игрушками. Большинство существующих распределённых обучающих платформ GPU требуют дополнительных пакетов, таких как Docker, OpenMPI и т. д. К сожалению, большинство компетентных администраторов кластеров не позволяют пользователям иметь административные привилегии, необходимые для настройки каждого узла, чтобы обучить модель.
  4. Какие-то игрушки плохо работают с другими. Пакеты глубокого обучения, такие как BERT и GPT2/3, разработанные крупными компаниями, как правило, имеют определённые форматы дизайна модели с несколькими логическими слоями, что затрудняет их использование и адаптацию к приложению.

Из-за этих проблем мы создали общую систему, которая охватывает все сложные части DDP: разделение данных, совместимость и настраиваемость, и развернули эту систему в Нотр-Даме.

Мы называем эту систему HetSeq. Она была адаптирована из популярного пакета PyTorch и обеспечивает возможность обучения больших моделей нейронных сетей в гетерогенной инфраструктуре. Её можно легко настроить через общую файловую систему без дополнительных пакетов и административных привилегий. Вот как обучать BERT с помощью HetSeq.

BERT в университете с HetSeq


Начнём с Anaconda. Создадим виртуальную среду и установим Python.

$ conda create --name hetseq$ conda activate hetseq$ conda install python=3.7.4


Затем мы установим пакеты и привязки HetSeq: загрузим HetSeq с GitHub, установим пакеты из requirements.txt, а также HetSeq и биндинги из setup.py.
$ git clone https://github.com/yifding/hetseq.git $ cd /path/to/hetseq $ pip install -r requirements.txt $ pip install --editable .

Последний шаг перед обучением это загрузка файлов данных BERT, включая корпус обучения, конфигурацию модели и словарь BPE отсюда. Загрузите DATA.zip, распакуйте его и поместите в каталог preprocessing/.

Обучение BERT с помощью HetSeq


Крутая вещь в HetSeq: она абстрагирует все детали о распределённой обработке. Таким образом, код обучения для 100 GPU почти такой же, как для одного! Давайте попробуем!

$DIST=/path/to/hetseq $python3 ${DIST}/train.py  \ $       --task bert   --data ${DIST}/preprocessing/test_128/ \ $       --dict ${DIST}/preprocessing/uncased_L-12_H-768_A-12/vocab.txt  \ $       --config_file ${DIST}/preprocessing/uncased_L-12_H-768_A-12/bert_config.json  \ $       --max-sentences 32  --fast-stat-sync --max-update 900000 --update-freq 4  \ $       --valid-subset test --num-workers 4 \ $       --warmup-updates 10000  --total-num-update 1000000 --lr 0.0001  \ $       --weight-decay 0.01 --distributed-world-size 1  \ $       --device-id 0 --save-dir bert_single_gpu

В этом случае предположим, что у нас есть два вычислительных узла.

На первом узле:

$DIST=/path/to/hetseq $python3 ${DIST}/train.py  \ $       --task bert   --data ${DIST}/preprocessing/test_128/ \ $       --dict ${DIST}/preprocessing/uncased_L-12_H-768_A-12/vocab.txt  \ $       --config_file ${DIST}/preprocessing/uncased_L-12_H-768_A-12/bert_config.json  \ $       --max-sentences 32  --fast-stat-sync --max-update 900000 --update-freq 4  \ $       --valid-subset test --num-workers 4 \ $       --warmup-updates 10000  --total-num-update 1000000 --lr 0.0001  \ $       --weight-decay 0.01 --save-dir bert_node2gpu4  \ $       --distributed-init-method tcp://10.00.123.456:11111 \ $       --distributed-world-size 8 --distributed-gpus 4 --distributed-rank 0

На втором узле:

$DIST=/path/to/hetseq $python3 ${DIST}/train.py  \ $       --task bert   --data ${DIST}/preprocessing/test_128/ \ $       --dict ${DIST}/preprocessing/uncased_L-12_H-768_A-12/vocab.txt  \ $       --config_file ${DIST}/preprocessing/uncased_L-12_H-768_A-12/bert_config.json  \ $       --max-sentences 32  --fast-stat-sync --max-update 900000 --update-freq 4  \ $       --valid-subset test --num-workers 4 \ $       --warmup-updates 10000  --total-num-update 1000000 --lr 0.0001  \ $       --weight-decay 0.01 --save-dir bert_node2gpu4  \ $       --distributed-init-method tcp://10.00.123.456:11111 \ $       --distributed-world-size 8 --distributed-gpus 4 --distributed-rank 4

Два блока кода работают на двух разных узлах. Адрес TCP/IP должен быть установлен как один из IP-адресов узла. Как только они будут запущены, вы сможете наблюдать за выполнением кода на 8 процессорах и 2 разных узлах!

Так насколько хорошо это работает? Мы провели несколько экспериментов (подробности тут) над различными однородными (гомогенными, hom) и неоднородными (гетерогенными, het) установками.

nodes GPUs training_time speed_up
1 4 7.19day 1.00
2(het) 8 4.19day 1.72
2(hom) 8 4.26day 1.69
4(het) 16 2.23day 3.22
4(hom) 16 2.19day 3.28
8(het) 32 1.21day 5.94

В общей сложности мы смогли управлять 32 GPU в 8 неоднородных узлах, сокращая время обучения языковой модели BERT с семи дней до примерно одного дня.

Под капотом HetSeq



Структура пакета HetSeq

Пакет HetSeq содержит три основных модуля, показанных на рисунке слева: train.py, task.py и controller.py для координации основных компонентов, показанных справа. Модуль train.py инициализирует распределённую систему и её различные компоненты.

Модуль task.py определяет функции модели, набора данных, загрузчика данных и оптимизатора; он также выполняет функции прямого и обратного распространения. Модуль controller.py действует как главный контроллер обучения. Он работает как модель, оптимизатор и планировщик скорости обучения; загружает и сохраняет чекпоинты, сообщает о потере и обновляет параметры.
Но я хочу обучить не BERT!

Но я хочу обучить не BERT!


Нет проблем. Вы можете использовать HetSeq с любой другой моделью. Но вам нужно определить новую задачу с соответствующей моделью, набором данных, оптимизатором. и планировщиком скорости обучения. Есть пример MNIST со всеми расширенными классами. Предварительно определённые оптимизаторы, планировщики скорости обучения, наборы данных и модели могут быть повторно использованы в других приложениях. Для получения дополнительной информации ознакомьтесь с пакетом HetSeq и документацией.

image



Подробнее..

Перевод Использование Slack для отслеживания очереди недоставленных сообщений SQS

28.12.2020 18:17:25 | Автор: admin
AWS SQS играет значительную роль в современной архитектуре приложений, особенно в бессерверной среде. При работе с SQS часто можно увидеть, что сообщения не были прочитаны; причиной могут быть ошибка в вашем коде, временное ограничение ресурсов, превышение бюджета API или зависимости в сообщениях, которые должны быть обработаны. В большинстве случаев вы хотели бы знать, что это за сообщения, если они много раз терпят неудачу, а затем узнать, почему, и устранить проблемы. Именно здесь в игру вступает очередь недоставленных сообщений SQS.





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

  1. Нет деталей о недоставленных сообщениях. CloudWatch только показывает, что есть сообщения в очереди недоставленных сообщений, не сообщая деталей, чтобы найти более подробную информацию, DevOps часто приходится использовать другие инструменты, например AWS CLI.
  2. Нет возможности воспроизвести недоставленные сообщения, то есть система не в состоянии вернуть недоставленное сообщение в SQS, по крайней мере это не так легко. Можно использовать AWS CLI, чтобы вернуть их обратно, но опять же это делает устранение уже неприятных неполадок еще более неприятным.

Вышеперечисленные проблемы могут быть решены с помощью Slack и Lambda, как показано ниже.

Очередь недоставленных сообщений SQS является триггером событий лямбда-функции, которая отправляет уведомления Slack, затем Slack передаёт действия пользователя обратно лямбда-функции и, наконец, помещает сообщения обратно в SQS.

Часть 1. Slack


Сначала нам нужно создать приложение в Slack, а затем написать приложение, чтобы оно принимало действия пользователя и взаимодействовало с Lambda.


Перейдите на api.slack.com, чтобы создать приложение, если у вас его ещё нет.

Когда приложение будет готово, мы можем создать входящий веб-хук, на который лямбда-функция будет отправлять уведомления. Сохраните URL веб-хука, который понадобится позже.



Создаём интерактивность, URL запроса это место, куда Slack посылает действия пользователя, конечная точка шлюза API функции Lambda.



Часть 2. Lambda


Я использую бессерверный фреймворк для управления лямбда-функциями. У нас будет две лямбда-функции:

  1. Функция мониторинга. Источник события функции очередь недоставленных сообщений SQS, поэтому, когда в очереди недоставленных сообщений появляется сообщение, эта функция активируется, а затем переадресует сообщение в Slack.
  2. Функция команды. Эта функция отвечает за прослушивание действий Slack, то есть нажатие кнопки отправляет сообщение обратно в исходную SQS.

Файл Serverless.yml показывает, как настраиваются эти две функции
service: slack-sqs-monitorframeworkVersion: "2.9.0"provider:  name: aws  versionFunctions: false  runtime: nodejs12.x  region: ap-southeast-2  iamRoleStatements:    - Effect: "Allow"      Action:        # You should only give least permissions to your functions.        - "sqs:*"      Resource:        - arn:aws:sqs:ap-southeast-2:xxxxxxxx:sqs.fifo # The original SQS arn        - arn:aws:sqs:ap-southeast-2:xxxxxxxx:deadletter.fifo # The dead letter queue arnplugins:  - serverless-webpack  - serverless-domain-managercustom:  customDomain:    rest:      domainName: labs.mianio.com      basePath: sqs-command      createRoute53Record: true      securityPolicy: tls_1_2  webpack:    webpackConfig: "webpack.config.js"    packager: "yarn"functions:  monitor:    handler: functions/monitor.handler    desciption: The function has the dead letter queue as the event, and forward the event to Slack    tags:      name: Monitor    environment:#     This is the webhook URL from the previous step      SLACK_ENDPOINT: https://hooks.slack.com/services/XXXXXXX/XXXXXX/XXXXXXXXX    events:      - sqs:#       Dead letter queue ARN          arn: arn:aws:sqs:ap-southeast-2:xxxxxxxx:deadletter.fifo  command:    handler: functions/command.handler    tags:      name: Command      desciption: The function handles Slack action and place the message back to the queue    environment:    # Credentials should be retrieved from Parameter Store       SLACK_SIGNING_SECRET: ${ssm:/deadletter/slack/signing-secret~true}      SLACK_OAUTH_TOKEN: ${ssm:/deadletter/slack/oauth-token~true}    events:      - http:          path: slack          method: post          cors: true


  • В функция мониторинга есть SLACK_ENDPOINT в качестве переменной окружения, которая будет использоваться для публикации в Slack.
  • Функция command находится за шлюзом API, конечная точка URL запроса для интерактивности Slack.

Функция декомпозирует события из очереди недоставленных сообщений и создаёт полезную нагрузку Slack для отправки. Смотрите api.slack.com/block-kit, чтобы узнать подробности о блоках для разработки в Slack.

Функция мониторинга
import middy from "@middy/core";import axios from "axios";import doNotWaitForEmptyEventLoop from "@middy/do-not-wait-for-empty-event-loop";export const monitor = async (event: any): Promise<any> => {  const records = event.Records;  await Promise.all(    records.map((record: any) => {      const messageGroupId = record?.attributes?.MessageGroupId;      const messageDeduplicationId = record?.attributes?.MessageDeduplicationId;      const approximateReceiveCount =        record?.attributes?.ApproximateReceiveCount;      return axios({        method: "post",        url: process.env.SLACK_ENDPOINT,        data: {          blocks: [            {              type: "section",              text: {                type: "mrkdwn",                text: `*Messsge ID*: ${record.messageId}`,              },            },            {              type: "section",              text: {                type: "mrkdwn",                text: `*Message Group Id*: ${messageGroupId}`,              },            },            {              type: "section",              text: {                type: "mrkdwn",                text: `*Message Deduplication Id*: ${messageDeduplicationId}`,              },            },            {              type: "section",              text: {                type: "mrkdwn",                text: `*Approximate Receive Count*: ${approximateReceiveCount}`,              },            },            {              type: "section",              text: {                type: "mrkdwn",                text: record.body,              },            },            {              type: "actions",              elements: [                {                  type: "button",                  style: "primary",                  text: {                    type: "plain_text",                    text: "Send back",                  },                  action_id: "sendback",                  value: record.body,                },              ],            },          ],        },        headers: {          "Content-type": "application/json; charset=utf-8",        },      });    })  );  return;};export const salesforceDeadLetterMonitor = middy(monitor).use(  doNotWaitForEmptyEventLoop());


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



Часть 3. Отправка обратно


Захватывающая деталь проекта это возможность отправить недоставленное сообщение обратно в SQS для переработки. При нажатии зеленой кнопки Send back Slack запускает POST-запрос на определённый ранее URL-адрес действия, то есть конечную точку шлюза API.

Функция command, которая отправляет недоставленное сообщение обратно в SQS
import { APIGatewayEvent } from "aws-lambda";import AWS from "aws-sdk";import qs from "qs";import axios from "axios";import middy from "@middy/core";import doNotWaitForEmptyEventLoop from "@middy/do-not-wait-for-empty-event-loop";import httpHeaderNormalizer from "@middy/http-header-normalizer";import httpEventNormalizer from "@middy/http-event-normalizer";import httpErrorHandler from "@middy/http-error-handler";import { slackVerifier } from "../../middlewares/slack/verify";const sqs = new AWS.SQS({ region: "ap-southeast-2" });const command = async (event: APIGatewayEvent) => {  if (!event.body) return { statusCode: 200 };  const requestBody: any = qs.parse(event.body);  const payload: any = JSON.parse(requestBody.payload);  let response;  const action = payload.actions[0];  if (action.action_id === "sendback") {    try {      const sqsPayload = payload.message.blocks.find(        (block: any) => block.block_id === "payload"      );      if (sqsPayload?.text?.text && action?.value) {        const payload = JSON.parse(sqsPayload.text.text);        await putBack(payload.jobName, payload.jobData, action.value);        response = {          payload: {            attachments: [              {                color: "good",                text: "Job was sent back",              },            ],            response_type: "in_channel",          },        };      }    } catch (error) {      console.error(error);    }  }  if (payload.response_url) {    await axios({      method: "post",      url: payload.response_url,      data: response.payload,      headers: {        "Content-type": "application/json; charset=utf-8",        Authorization: `Bearer ${process.env.SLACK_OAUTH_TOKEN}`,      },    });  } else if (response && !payload.response_url && response.payload) {    return {      body: JSON.stringify(response.payload),      statusCode: 200,    };  } else {    return {      statusCode: 200,    };  }};const putBack = async (name: string, data: any, workerUrl: string) => {  const params: any = {    MessageBody: JSON.stringify({ jobName: name, jobData: data }),    QueueUrl: workerUrl,  };  return new Promise((resolve: Function, reject: Function): any => {    sqs.sendMessage(params, (err: any, data: any): any => {      if (err) {        reject(err);      } else {        resolve(data);      }    });  });};export const handler = middy(command)  .use(doNotWaitForEmptyEventLoop())  .use(httpEventNormalizer())  .use(httpHeaderNormalizer())  .use(slackVerifier())  .use(httpErrorHandler());


Эта функция довольно проста:

  • Функция slackVerifier. Она проверяет, что POST-запрос направлен от Slack.

verifier.ts
import crypto from 'crypto';import qs from 'qs';export const slackVerifier = () => {  return {    before: async (handler: any) => {      const slackSignature =        handler.event.headers && handler.event.headers['x-slack-signature'];      const timestamp =        handler.event.headers &&        handler.event.headers['x-slack-request-timestamp'];      const time = Math.floor(new Date().getTime() / 1000);      if (Math.abs(time - timestamp) > 300) {        //  The request timestamp is more than five minutes from local time.        // It could be a replay attack, so let's ignore it.        return {          statusCode: 401,          body: JSON.stringify('Too old'),        };      }      const body = handler.event.body;      const sigBasestring = `v0:${timestamp}:${body}`;      const hash = crypto        .createHmac('sha256', process.env.SLACK_SIGNING_SECRET)        .update(sigBasestring, 'utf8')        .digest('hex');      const mySignature = `v0=${hash}`;      if (        !crypto.timingSafeEqual(          Buffer.from(mySignature, 'utf8'),          Buffer.from(slackSignature, 'utf8')        )      ) {        return {          statusCode: 401,          body: JSON.stringify('Invalid Signature'),        };      }      return;    },    onError: (handler: any) => {      return handler.callback(null, handler.error);    },  };};view raw

Переменная среды SLACKSIGNINGSECRET это переменная со страницы конфигурации Slack, которая вводится из определений бессерверной среды Serverless.yml.



Я настоятельно рекомендую хранить учётные данные в безопасном месте; извлекать их во время развертывания и держать вне исходного кода это хорошая практика.

SLACK_SIGNING_SECRET: ${ssm:/deadletter/slack/signing-secret~true}

  • Функция sendBack. Она получает полезную нагрузку от POST-запроса Slack и отправляет ее обратно в SQS.

Полезная нагрузка, которую Slack отправляет в command-функцию, выглядит так.



Она содержит response_url, который должен использоваться для отправки ответа обратно в Slack, чтобы подтвердить действие.



Код отправляет [полезную нагрузку ответа] обратно в Slack с помощью Bearer-токена.

await axios({    method: "post",    url: payload.response_url,    data: response.payload,    headers: {       "Content-type": "application/json; charset=utf-8",       Authorization: `Bearer ${process.env.SLACK_OAUTH_TOKEN}`    }});

SLACK_OAUTH_TOKEN вводится из переменных среды во время развёртывания. Вы можете получить её значение на странице конфигурирования Slack:



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

Задумали с нового года начать новую жизнь и подучиться? До конца этого года ещё можно ухватить курс с хорошей скидкой. А если использовать промокод HABR к скидке на баннере можно прибавлять еще 10%.

image



Подробнее..

Про-зрение и про технологии

25.12.2020 22:21:37 | Автор: admin


Сколько пальцев я показываю?
Впереди маячила фигура в белом халате с поднятой рукой. Вероятность угадать всего 17%, и лучше я честно признаюсь, что не вижу.
Сможешь сам вернуться в палату?
Сегодня уже смогу: контуры дверных проемов вижу, а номера не нужны расположение помню, а остальное на ощупь. Хотя вначале водили за ручку, было дело.

В палате есть, чем заняться. Соседа утром выписали, и теперь я могу гулять. По диагонали шесть шагов туда и шесть обратно. Можно ещё для разнообразия поворачивать сначала через одно плечо, потом через другое. А если к тому же интенсивно махать руками, то минут через десять часы начнут вежливо теребить руку: Кажется, у вас тренировка по ходьбе, давайте запишем? Давайте, давайте. Самому включить сложно нужно касанием выбрать один из типов тренировки, которые периодически меняются местами, выстраиваясь по частоте использования. Вот выключить смогу смахнуть направо и ткнуть в левый нижний угол.

А скажи-ка мне, Siri, сколько времени! Сейчас тринадцать часов сорок одна минута. Впрочем, в детстве я смог бы ненамного хуже: 08 на дисковом аппарате вслепую набрать пустяк. Нынче же говорящие часы помогают и подсказывают. И пусть обед приносят с точностью до получаса, но процедуры-то по расписанию, пропускать нельзя.

Только немного беспокоит, что вся мощь современных технологий может рухнуть в один момент, если телефон попросит, наконец, пин-код. Поэтому стараюсь всегда прикладывать к сенсору чистый сухой палец, чтобы чудо-алгоритм не заподозрил неладное. Интересно, а можно цифры вслух произнести, чтобы разблокировать? А как? А узнал бы меня Face ID? Глаза-то я открою, но в них ведь сплошной белый туман.

Привет, Хабр!

Был тут пост, которым я искренне впечатлился: Разработка на скорости 450 слов в минуту. Мысленно переварив использование компьютера вслепую, в комментариях к оригиналу нашел ссылку на блог незрячей девушки Молли Бёрк более двух миллионов подписчиков. Она рассказывала, как пользуется современными технологиями компьютером, мобильным телефоном. Как ловко у нее всё получалось тогда! А теперь я пялюсь на голубоватый прямоугольник, покрытый разноцветными кляксами, и пытаюсь вспомнить, как же с этим всем взаимодействовать.

В мире живут более двух миллиардов людей с нарушениями зрения и десятки миллионов слепых. Я одной ногой оказался в их мире. Паники никакой не было, поэтому я спокойно изучал его изнутри. Какие тут есть занятия, достойные IT-шника?

Блогер


Обычное течение событий прервалось, я без всяких предупреждений выпал из всех рабочих процессов. Мои сотрудники наверняка волнуются и переживают. Надо их успокоить. Решаю, что самое простое в моей ситуации записать видеопослание.

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

Остается пустяк отправить ролик в Slack. Надо нажать Share (где кнопка помню) и выбрать Slack (даже размытую иконку узнать можно). Надеюсь, что нужное пространство и канал general выбраны по умолчанию. Далее Send хм, вроде, справа сверху? Но что-то идет не так. То ли я жму не туда, то ли Slack чудит, то ли видео у него не той системы.

Пробую другой вариант пойти в сам Slack и прикрепить видео к пустому сообщению. Но этот путь не для слабовидящих кнопки плотно налеплены, UX оставляет желать лучшего. Тут помощник нужен, homo sapiens! Запрашиваю видеосозвон и с личного телефона через камеру показываю экран рабочего, а моя собеседница направляет мой палец, чтобы я попадал в нужные кнопки.
Хозяйке на заметку
Это может стать хорошим развлечением для корпоративного мероприятия на удалёнке.

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

Телефона-телефона!


Я как в том анекдоте пытаюсь добиться чего-то, разговаривая со своим телефоном. В детстве это звучало смешно, а теперь не уверен, что дети поймут, в чем шутка. Ведь действительно можно попросить телефон, например, позвонить кому-нибудь. Это неплохо получалось. Звонящих мне распознавал уже по голосу. Но мне не удалось одолеть такой сценарий: когда разговариваю с одним человеком, а в это время звонит другой. Заканчиваю первый разговор и пытаюсь выяснить у телефона, кто мне звонил. Либо молчит, как партизан, либо начинает звонить по последнему номеру, который я сам набирал. Не удается найти общий язык. Потому что в обычной жизни я пренебрегал голосовым управлением, а ведь можно было хотя бы потренироваться.

Нейробика


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

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

Людям, приросшим к смартфонам и компьютерам, настоятельно рекомендую почаще контактировать с физической реальностью в разных проекциях.

Кругомысли


Я всегда считал, что главный мой актив мозг, а мои мысли, способность размышлять и придумывать что-то новое всегда обеспечат мне занятие. Это подтвердилось и на этот раз от безделья не скучал. Но всё равно пришлось серьезно перестраиваться.

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

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

О чем думаю? О работе. Сочиняю сценарий для серии видеороликов про наши приложения. После опыта с видеопосланиями это дается немного легче. Пробую погонять наши игры на телефоне: вся геометрия безнадежно, но неожиданно удается поиграть в Tchisla. Почти ничего не видно, но нажимая в разные места, решаю несколько задач и получаю золотую медальку. Число попалось несложное, но всё равно радуюсь, что UX, который мы придумали, неплох.

Пытаюсь представить, как могла бы работать Euclidea геометрические построения для слепых. Так чтобы программа могла вслух описать сцену, а пользователь голосом строить новые точки, окружности и прямые. Смогут ли люди удержать это всё в голове? Хотя ведь многие умеют играть в шахматы вслепую. В геометрической задаче объектов, может быть, и меньше, но связи между ними куда сложнее и иногда крайне неочевидные задача часто и состоит в их отыскании. А если не-оче-видное, то нужны ли очи?

Минус семь


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

Питание строго три раза в день примерно в 10, 14 и 17 часов. Из сладкого только чай или компот. Порции скромные. Уверен, что все белки-жиры-углеводы точно посчитаны, прямо как ГОСТ прописал, но это слабое утешение. Коронавирус добавил ограничений: кафе внизу закрыто, передачи делаются через пару буферных охранников, причем еду передавать запрещено.

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

По этому поводу придумал шутку для своих видеопосланий. Поспорили мы как-то с соседом про обед. Я утверждал, что гарнир рис, а он говорил, что цветная капуста. Но я же точно видел, что в контейнере что-то белое, а не цветное!
Разгадка
У соседа был диабет, и наше меню немного различалось. А для меня вся еда выглядела белой.

Но просто диета это неинтересно и нетехнологично. Помогают умные часы, отслеживающие активность. Ведь книжку не почитать, кино не посмотреть, по интернету не побродить. Основное развлечение, помимо процедур и осмотров выполнить норму по минутам тренировки и потраченным калориям. Фишка в том, что прогресс отображается в виде крупных цветных колечек, а их разглядеть я уже могу. Значит, утром обязательная зарядка. Никаких лифтов: с четвертого этажа поднимаешься на шестой строго по лестнице, причем через первый этаж. В ожидании очереди на процедуры хожу по коридору взад-вперёд. Пятнадцать минут и уже километр накапал. Самый шик бег в палате, пока никого нет и врачи не дёргают. Рекорд условные 4.3 км за полчаса.

В особо удачный день часы насчитали 21 тысячу шагов, примерно 17 км. В сумме пробег по больнице составил больше 100 км, и я выхожу из нее, не сильно растеряв спортивную форму. А бегать после этого стало ещё легче на целых семь килограммов.

Эпилог


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

Я не сразу вызову такси. Бодрым шагом пройду несколько кварталов, вдыхая свежий воздух и глядя на ставший непривычным мир сквозь прикрытые ресницы. А дома тут же брошусь не к холодильнику, а к компьютеру выяснить вопрос, который меня волнует последние дни: смогу ли? Да, пусть всё замылено и буквам не хватает контраста, но это уже мелочи. IT-жизнь продолжается! Нужно будет на Хабре рассказать свою историю.

Благодарности
Пользуясь случаем, хочу ещё раз поблагодарить всех, кто меня поддерживал и, конечно, врачей МНТК Микрохирургия глаза: Елену Владиславовну Самкович, Марину Васильевну Гацу, Ирину Евгеньевну Панову, Анастасию Юрьевну Улитину, Наталью Валентиновну Мацко и многих других к сожалению, я даже не всех видел. Если бы не их профессионализм и терпение, всё могло бы закончиться гораздо хуже.
Подробнее..

Статистика по логированию жизни и годовой отчет

31.12.2020 10:15:49 | Автор: admin
Летом 2019 года, любопытства ради, я стал логировать все свои действия работу, сон, создание контента и т.п. Получился интересный результат теперь я с цифрами в руках могу увидеть, насколько изменился мой режим дня после начала самоизоляции в марте и перехода на удаленку. Также под катом размышления об эффектах от логирования своей жизни и прочему GTD, а также традиционный годовой отчет.


Иллюстрация Рины Зенюк

Логи и GTD


Стоит отметить, что мое увлечение логированием времени вещь стихийная. Я не погружался в историю тайм-менеджмента и, когда знакомая посоветовала прочесть Эту странную жизнь Даниила Гранина, с удивлением обнаружил там историю человека, начавшего логировать свое время еще в 1916 году. Александр Александрович Любищев, советский биолог и энтомолог, вел учет своего времени 56 лет, с 26 и до самой смерти. У него не было под рукой смартфона с трекером, поэтому он главным образом учитывал и планировал свое самое важное время рабочее. Даже если вы не собираетесь логировать свое время, я бы советовал вам прочитать либо Эту странную жизнь Гранина, либо, без лишнего беллетризма, посмертно опубликованный в Химии и жизни за декабрь 1976 года фрагмент рукописи Любищева под названием Такая добровольная каторга. И важно отметить, что Любищев делал то, чего я не делаю он планировал работы на будущее. Я же пока только замеряю интереса ради и исхожу из общих идей хорошо было бы.



Результаты измерений и самонаблюдений оказалось можно поделить на две группы очевидные и не очень. К очевидным можно отнести исчезновение графы Дорога когда рабочее место в соседней комнате, а не в часе езды, логировать перемещение туда нет смысла. Вслед отправились Социальные активности посиделки стали в лучшем случае виртуальными. Ожидаемо выросла графа Работа в офисе трудишься более концентрированно, но этого роста почти не заметно. Из-за этого же эффекта выросла графа Сон когда можно лечь вздремнуть днем или проигнорировать будильник, и это никак не повлияет на рабочий процесс, вес ценностей в сознании меняется, и похвастаться шестичасовым сном в будни больше не получится. Также предсказуемо увеличились затраты времени на Хозяйственные дела раньше в магазин заскакиваешь по пути с работы, а сейчас это отдельный поход, который к тому же часто хочется растянуть. Появление новой графы Прогулки также сложно назвать неожиданным после нескольких месяцев дома нормально желание вырваться наружу, и, если раньше прогулки относились к Безделью, то сейчас их оказалось разумно выделить в отдельную, не порицаемую категорию.


Категория Безделье Безделье

Главной неожиданностью оказалось осознание потребности в чтении. В докарантинном режиме я читал в общественном транспорте (трекалась Дорога) и параллельно обеду на работе (трекалась Еда). А в мире самоизоляции эти активности исчезли. И, поскольку чтение у меня учитывалось как Безделье, и в идеале я бы хотел извести вредные виды расхода времени, то оказалось, что я практически перестал читать. И, знаете ли, это очень неприятное, холодное, отупляющее ощущение. Так что пришлось разрешить себе развлекательное чтение. Но, поскольку его сложно назвать полезным, оно остается в категории Безделье, что объясняет рост этой категории осенью и, одновременно, не выглядит как абсолютно точное решение. Из хорошего под конец года пропала категория Сборы на работу, трата времени впустую за завтраком и туплением в телефон.


Категория Игры Безделье

Была у меня гипотеза о связи пропорций расхода времени с настроением. Но во-первых, точные измерения, похоже, требуют вести еще и дневник настроения. А во-вторых, те данные, которые я вижу, скорее подтверждают гипотезу накупил интересных игр на распродаже и игрался.

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

То, что результаты по некоторым категориям совпадают на двух временных промежутках, означают, что любопытно будет посмотреть на их поведение в будущем. Не получилось бы как в анекдоте на позапрошлый спринт мы запланировали 40 стори поинтов. Выполнили 35. На прошлый спринт мы запланировали 45 стори поинтов. Выполнили 35. На этот спринт мы планируем 50 стори поинтов!



И, наконец, если кому-то интересны технические детали, то тут все просто. Всегда со мной рядом смартфон, на котором установлено приложение-трекер (в моем случае Toggl). При каждой смене деятельности оно открывается, и изменение логируется. Трудностей или неудобств нет никаких. В примерно 5% случаев я забываю переключить трекер, но это не страшно, поскольку события свежи в памяти, то восстанавливаются с хорошей точностью.

Хабр


В общем, год продолжил тенденцию 2019. Напомню, тогда научпоп хабы вынесли в непрофильные, и я переключился вместо работы по расписанию на заказанные материалы, благо их тогда хватало. Для разнообразия, до карантина меня пригласили написать несколько постов в корпоративные блоги, и это были единственные материалы, которые выходили на Хабре раньше, чем в ЖЖ.

Сейчас, со стороны, польза от расписания видна еще лучше поставленная себе задача дисциплинировала, и не возникала мысль а зачем вообще я этим занимаюсь? Поскольку я уже давно топлю за проактивность, на вопрос есть ответ, вполне выразимый цитатой Зачем не шныровское слово. Шныровские слова или надо, или по приколу. Но вообще, в таких условиях я удивляюсь тому, что количество постов получилось даже чуть большим, чем за прошлый год.



ЖЖ


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



104 поста, два в неделю, кажутся рекордом, но архив говорит, что в 2017 году их было аж 107. Тогда у меня получалось работать в формате написания чего-нибудь серьезного на понедельник и легкого на пятницу. И здесь наблюдается любопытный парадокс 2017 год я считаю худшим в своей блогерской/популяризаторской деятельности, но именно из-за того, что меня никуда не звали и не предлагали написать что-нибудь на заказ, я смог сконцентрироваться и сделать больше всего материалов.

О сделанном


Так получилось, что ужасы 2020 года по мне пока что промахивались. Айтишная работа оказалась в отрасли, не пострадавшей от коронакризиса, и я только сочувствовал в чатиках тестировщиков увольняемым или тем, кому сокращали зарплаты. Конечно, с весны отвалились мероприятия и заказы на научпоп материалы, но в 2017 году было хуже без всякого мирового кризиса, так что тут жаловаться нет никакого желания. Уход всего и вся в онлайн в моем случае привел к активизации YouTube-канала, День космонавтики мы отметили онлайн, а затем я начал вести стримы по вечерам субботы мы поиграли в Kerbal Space Program, посмотрели стратегии-менеджеры космических агентств, регулярно устраиваем интересные реконструкции в Orbiter, а сейчас перепроходим кампанию в Children of a Dead Earth. Главная радость этого года Роскосмос обратил внимание на блогеров, поездка на космодромы Восточный и Морской старт вошла в топ личных событий года (я написал бы возглавила, но рождение второго ребенка по строго формальным признакам приходится поставить на первое место).

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

Спасибо, что читали и/или смотрели меня в этом году. Идеи, комментарии, вопросы, благодарности, проклятия и прочая обратная связь принимаются любым удобным вам способом в комментариях, в личку, Вконтакте или ФБ.
Подробнее..

Испания и налоги за что и как платят испанцы и резиденты Испании

02.01.2021 14:12:00 | Автор: admin

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

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

Виды налогов в Испании


Если начать с азов, то понятие налогообложения в Испании определяется как экономический вклад, которые граждане обязаны осуществлять по закону для поддержания доходов государства и гарантии работы государственной системы услуг общего пользования. То есть налоги это не только источник благосостояния государственной системы обслуживания населения, но и прямой фактор богатства и процветания страны.

В налоговой системе Испании различают три вида платежей:
  • Сборы оплачиваются, когда гражданин или налоговый резидент пользуется определенной государственной услугой (например, обновлением документов или паркингом).
  • Специальные взносы оплачиваются, когда гражданин получает прибыль или добавочную стоимость на свои товары и услуги в результате проведения государственной реформы.
  • Налоги безвозмездные платежи граждан, определяемые законодательством как вклад в государственное управление.

Любое лицо, которое получает доход, платит государственной финансовой системе Испании в зависимости от уровня своих экономических возможностей. Основной принцип: чем больше человек зарабатывает, тем больше он платит в государственную казну.

В Испании 38% налогоплательщиков платят 72% от общей суммы получаемых государством налогов. Это граждане, доходы которых составляют от 21 до 150 тыс. евро в год.

Экономические способности каждого налогового резидента определяются в зависимости от:
  • движимого и недвижимого имущества, которым он владеет;
  • дохода, который он получает;
  • суммы денег, которые он тратит;
  • личных обстоятельств (многодетность, инвалидность и др.).


Типы налогов в Испании


Налоги в Испании классифицируются по типам например, прямые или непрямые. Также нужно знать, что налоговая система трехуровневая, когда платежи взимаются на государственном, региональном или местном уровнях.

К прямым налогам относятся:
  • подоходный налог;
  • подоходный налог нерезидентов;
  • корпоративный налог;
  • налог на наследство и дарение;
  • налог на богатство.

К непрямым налогам относятся:
  • НДС;
  • налог на передачу имущества и документальное оформление юридических актов;
  • таможенный доход;
  • специальные налоги.

Основные налоги, которые платят испанцы и резиденты


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

Подоходный налог с населения (IRPF)


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

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

Для того чтобы определить платежные способности гражданина Испании или резидента, во внимание принимаются различные факторы например, личные и семейные обстоятельства. Поэтому вместе с принципом прогрессивности (больше получаешь больше платишь) используется и система освобождений, сокращений и отчислений. Таким образом, есть категории граждан, которые освобождены от уплаты налогов, платят по сниженной ставке или получают пособия. К ним, например, относятся инвалиды, безработные, студенты и др.

Подоходный налог нерезидентов


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

Налог на деятельность компаний (IS)


Это вид прямого налога, который платят предприятия и компании по всей территории Испании, кроме Страны Басков и Наварры, где применяется своя система налогообложения компаний. IS платят все типы юридических лиц от коммерческих компаний до ассоциаций и фондов. Но есть и исключения. Существует категория юридических лиц, которые либо платят налог по сниженной ставке 25%, либо не платят его совсем. Например, предприятия без прибыли, союзы, федерации, Королевские Академии, Банк Испании, государство и автономные сообщества.

Налог на добавленную стоимость (НДС)


В отличии от других видов налога, НДС платится не потому, что испанец получает доход, а потому что он его тратит. НДС платиться всеми автономными сообществами, исключая Канарские острова, а также Сеуту и Мелилью анклавы Испанского королевства на территории Африки.

Если кратко, то сумма НДС формируется на основе трех налоговых ставок. Например, низкая ставка в 4% установлена на предметы первой необходимости. Десятипроцентная ставка, или сниженная, устанавливается на некоторые продукты питания, пассажирский транспорт, товары для здоровья, гостиничный бизнес и строительство. Общая ставка НДС в 21% установлена на все остальные товары и услуги.

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


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


В этот тип сборов входят три налога, которые не пересекаются с НДС и между собой:
  • Налог на передачу прав собственности. Здесь облагаются налогом все операции передачи собственности, если обе стороны при этом получают выгоду.
  • Налог на документально оформленные правовые акты. Речь идет об оформлении всех нотариальных документов, актов, свидетельств, оформленных в Испании и за границей, но имеющих свою силу в Испании.
  • Налог на корпоративные действия, например, на создание, увеличение/уменьшение уставного капитала, создание и роспуск компаний и др.

Из других налогов, которые испанцы платят в своей стране, также стоит отметить налог на недвижимое имущество (от 100 евро в год) и на транспортные средства (от 80 евро в год).

Наибольшее налоговое бремя ложится на граждан и резидентов страны, которые получают от 30 000 до 60 000 евро в год. Их всего 18% от общей массы налогоплательщиков, но их налоги составляют 36% от всей суммы получаемых государством налогов. При этом средний доход на одного жителя Испании составляет чуть больше 23 620 евро в год.

Кстати, в 2020 году Испания стала первой в Европе по такому признаку, как социальное неравенство. Причина пандемия и сопутствующий экономический кризис. В то же время правительство с января изменяет налоговое законодательство, вводя новые виды сборов. Кроме того, повышаются такие налоги, как подоходный, на богатство, увеличивается НДС на некоторые виды продуктов (например, на напитки с пониженным содержанием сахара), на транспортные средства, на страховые взносы и др.

Подробнее..

Более 10лет ставлю цели на год рассказываю, как это делать эффективно

04.01.2021 22:21:38 | Автор: admin

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

Я полностью прошёл все стадии грехопадения: в студенческие годы искал сакральные ответы в стопках self-help макулатуры; затем разочаровался и решил, что весь этот успешный успех это разводилово для дурачков (до сих пор уверен, что Наполеон Хилл шизофреник); позже переосмыслил всё ещё раз и пришёл к своему пониманию того, как может выглядеть саморазвитие с адекватным лицом.

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

Зачем ставить цели

Любой уважающий себя инфоцыганин тонироббинсового разлива обязательно будет топить за магическую силу целей. Учёные ведь доказали, что 3% студентов Йеля с записанными целями спустя 20 лет оказались богаче оставшихся 97% вместе взятых! (На самом деле,конечно нет.)

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

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

Можно, конечно, грести куда надо и без какого-либо структурированного процесса целеполагания. Это неплохо работает, если у вас есть одна или две глобальных цели, а всё остальное уже неважно. Но мне кажется, что наша жизнь это довольно сложная штука, и попытка упрощения её до таких нехитрых двухмерных конструкций чревата перекосами. Именно так появляются истории о богатых, но не очень здоровых и счастливых достигаторах, о выгоревших стартаперах, и так далее. А одновременно успешно жонглировать кучей разных приоритетов в разных сферах жизни наш мозг умеет весьма посредственно: здесь ему как раз была бы кстати внешняя система целеполагания, которая поможет убедиться, что всё схвачено на всех фронтах.

Многие из вас наверняка сталкивались с такой ситуацией: задачи по работе успешно выполняются в срок, а более личные задачи благополучно откладываются на завтра, кочуя изо дня в день по туду-листам. Почему так происходит? Рабочие задачи имеют соответствующий обвес в виде дедлайнов, системы контроля за их исполнением, и обратной связи по результатам. Если за личными устремлениями не стоит аналогичной системы то неудивительно, что они часто проигрывают в битве приоритетов.

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

Сферы жизни

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

Я сторонник более прагматичного подхода, связанного с накоплением капитала в ключевых сферах жизни. Идея здесь следующая:В человеческом обществе существует набор мета-ресурсов, которые можно использовать для достижения любых других целей.Грубо говоря, какие бы цели вы перед собой ни ставили, реализовать их будет гораздо проще, если у вас будет много свободного времени, денег, социальных связей, и так далее. Поэтому мне кажется разумным уделять много внимания достижению целей в этих ресурсных сферах по крайней мере, лишним это точно не будет, чем бы вы ни решили потом заниматься.

Для себя я выделил пять основных сфер жизни (подробнее о том, почему именно эти пять, и как они связаны с пирамидой Маслоу, я писалв этой статье).

Пирамида МаслоуПирамида Маслоу

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

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

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

Работа.Ну, тут в целом понятно: работа это и способ самореализации, и источник денег/уважения со стороны общества. Уметь хорошо делать свою работу (в широком смысле: сюда может попадать и работа по найму, и свой бизнес, и какие-то общественно-значимые проекты) полезно со всех сторон.

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

Как я ставлю цели на год

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

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

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

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

Фрагмент из моего годового отчётаФрагмент из моего годового отчёта

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

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

На мой взгляд, лучше поставить 15 целей и достигнуть 70% из них, чем на 100% выполнить три цели.

Но при этом, все цели не обязательно должны быть одинаковыми по важности. Каждый год я выбираю одну какую-то сферу обычно это та область, где давно не было значимого прогресса и ставлю в ней одну главную цель года. Эта цель должна быть реально крутая я описываю её для себя какЧто должно произойти в течение года, чтобы даже если всё остальное пойдёт наперекосяк я всё равно останусь дико доволен и посчитаю, что год удался?Это достаточно жёсткий критерий, поэтому главной целью оказываются почти всегда какие-то прорывные для моей жизни амбиции.

Соответственно, в течение года я каждый день оказываю этой альфа-цели первостепенное внимание: до того, как переходить к другим вещам, я всегда спрашиваю себя а что я сделал для главной цели? Если ответ ничего, то заниматься другими вещами рано, надо сначала хоть как-то продвинуться в направлении самой козырной цели. Такой фокус позволяет мне быть уверенным, что самое важное для меня не окажется в составе тех 30% нереализованных целей. Именно через принцип главной цели на год я в своё время купил квартиру, сменил карьеру и переехал в Москву, поднял уровень доходов в несколько раз, начал ходить в тренажёрный зал и набрал 15 кг, встретил свою спутницу жизни.

Ещё один важный момент это сама формулировка целей. Классическая рекомендация звучит как цели должны соответствовать принципамSMART (быть конкретными, привязанными ко времени, и так далее), но вы это и так сто раз читали, я уверен. Мне кажется более важным остановиться на различии целей от результата и от процесса.

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

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

Цели поставлены, что дальше?

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

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

Для меня такой системой служат ежедневные чеклисты.Я веду Excel-файлик, где по колонкам разбросаны уже знакомые нам сферы жизни, а каждая строка это отдельный день. Каждый вечер я вписываю в соответствующую строку итоги дня: что было сделано для достижения целей в каждой из сфер. Если прогресс был ячейка окрашивается в зелёный, если ничего не происходило она остаётся белой, если произошло что-то негативное (например, критически важная задача оказалась не выполнена) в красный. Таким образом, даже визуально легко определить, где намечается провисание по целям в данной колонке будет видна череда белых/красных ячеек.

Фрагмент моего ежедневного чеклиста (текст внутри ячеек я удалил там слишком личная информация)Фрагмент моего ежедневного чеклиста (текст внутри ячеек я удалил там слишком личная информация)

Естественно, невозможно каждый день делать всё, что запланировал поэтому для меня важнее агрегированные данные. Раз в неделю я переношу результаты на отдельный лист, который имеет такую же структуру, но каждая строка это уже неделя, а не день. А раз в месяц, как можно уже догадаться, результаты месяца оказываются на другом листе с 12 строками по одной для каждого месяца. В идеале, чем более высокий уровень агрегации тем меньше должно оставаться регулярно отстающих сфер, если на уровне дней и недель вы вовремя перераспределяете усилия.

Такой подход позволяет мне не забивать на те цели, которые я сам для себя поставил в начале года.Достаточно трудно 365 раз в году каждый день писать в специальной колонке сегодня я опять ничего сделал для этой цели рано или поздно приходится идти уже что-то делать, иначе стыдновато перед самим собой.

Мне часто говорят, что такой подход с чеклистами это какое-то извращение, и нормальные люди так не живут. Многие из этих людей, как ни странно, не видят ничего плохого в том, чтобы заниматься прокачкой какого-нибудь орка в World of Warcraft и радоваться растущим циферкам его характеристик.

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

Лайфхаки в достижении целей

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

Agile-подход

Мне очень нравятся два принципа из философии Agile, они отлично работают не только в разработке программных продуктов, но и для личных проектов (подробнее писал об этомздесь):

  • Bias for Action(склонность к действию). Если ты собираешься попробовать что-то новое, то не трать кучу времени на скрупулезное изучение вопроса и подготовку идеального плана. Лучше вместо этого начни с конкретных действий, на практике приближающих тебя к конечной цели: начни ходить в зал, запишись на курсы программирования, познакомься с владельцем бизнеса и обсуди с ним свою идею. Разобраться в нюансах и составить толковый план будет гораздо проще по ходу, чем теоретизируя на диване.

  • Minimum Viable Product(минимально жизнеспособный продукт). Не пытайся сразу же сделать идеально такая высокая планка обычно труднодостижима, и ты гарантированно будешь буксовать, пытаясь её осилить. Лучше подходить к вопросу поэтапно сначала реализуй минимально допустимое решение (которое, тем не менее, будет хоть как-то работать), а потом можешь его постепенно улучшать. Это гораздо легче, чем пытаться сразу сделать идеально; более того, в процессе поэтапного улучшения может оказаться, что идеал и не нужен приближения к нему на 7080% вполне может оказаться достаточно (по принципу Парето).

Микро-шаги

Этот принцип несколько перекликается с предыдущими. Чтобы поменять свою жизнь, гораздо важнее регулярность и долгосрочная ориентация, а не какие-то разовые сверх-усилия. Обычно люди подходят к целям наоборот: если уж заниматься своим здоровьем то нужно обязательно начинать ходить в зал так, чтобы упахиваться там до потери человеческого облика и выползать на четвереньках, блюя в коридоре из последних сил. Надо ли говорить, что заставлять себя регулярно ходить на такие самоизнасилования несколько, кхм, сложновато?

Гораздо более разумно, на мой взгляд, начинать с малого пусть даже первое время это будет до смешного малое. Например, недельку можно хотя бы просто регулярно приходить в зал и не делать там ничего страшного, просто походить на дорожке. Как только привыкнете к ощущению, можно переходить к следующему этапу вводить какие-то лёгкие упражнения, и так далее. Через 10 лет вам будет совершенно неважно: начинали вы по хардкору, или втягивались постепенно в течение месяца. Единственное, что будет иметь значение это бросили вы или нет. Так вот, микро-шаги как раз помогают снизить вероятность преждевременного бросания.

Метод яростных наскоков

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

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

Getting Things Done

Долгое время я не пользовался никакими таск-менеджерами, задачи себе писал в бумажном ежедневнике или в своём Excel-файле, и считал известную систему Getting Things Done Дэвида Аллена замороченной ерундой, которая только усложняет жизнь. Но недавно тема GTD всплыла вразговоре с Гришей Мастридером, и оказалось, что мой подход к управлению временем и задачами на самом деле достаточно близок к этой концепции (если убрать всю олдскульную чепуху Аллена с десятками бумажных папочек).

Так что я пересел на приложение Todoist, внёс несколько поправок в свою систему в соответствии с заветами GTD и теперь весьма доволен результатом. Использовать специальное приложение для управления задачами оказалось гораздо более удобным.

Целеполагание здорового человека

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

Этот момент мы обсуждали недавно винтервью с Алексеем Марковым(автором Хулиномики) он мой метод целеполагания немного покритиковал, и рассказал об альтернативном подходе, который практикует он сам. Это более мягкий вариант, без жёстких целей и чрезмерно анального контроля более ориентированный на ценности, привычки и роли. Рекомендую послушать его рассказ возможно, такой вариант подойдёт вам больше. Надо только учесть, что к такому дзен-подходу Алексей пришёл уже после достижения финансовой независимости есть подозрение, что для максимально результативного стремления к амбициозным целям он приспособлен чуть меньше.

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

Ну и напоследок буду рад, если присоединитесь к моемуТелеграм-каналу RationalAnswer, где я пытаюсь найти разумные ответы на важные жизненные вопросы: там больше про личные финансы и инвестиции, но также затрагиваются темы личной эффективности, ЗОЖ и так далее.

Подробнее..

Как автор деньги возвращал (11 450 евро) от Lufthansa, RyanAir, Booking и других

05.01.2021 00:04:18 | Автор: admin

История о том, как я вернул более 10000 евро от Ryanair, Lufthansa, Booking и частного агентства недвижимости

Содержание статьи

1. Введение как мне удалось вернуть 11 450 евро.

2. Можно ли вернуть деньги за отмененные из-за пандемии рейсы и что такое ваучеры.

3. Откуда образовался "кредит" на более чем в 10 000 евро.

4. Маклер или как "не вернуть деньги".

5. Обращение к адвокатам.

6. Возврат денег через банковскую процедуру Charge Back.

7. Зачисление 9500 евро долговых обязательств за проживание на мой расчетный счет.

8. Возврат средств от Lufthansa.

9. Возврат средств от Ryanair.

10. Возврат средств от Booking.

11. Заключение.

Семь долгих месяцев длилась моя история с возвратом денег из компаний Ryanair, Lufthansa и Booking. При этом на протяжении первых четырех месяцев я только ожидал ответы от этих компаний, а в течение следующих трех шла полноценная работа по возврату средств.

В итоге мне удалось вернуть в общей сложности 11450 евро, из которых:

  • Возврат по проживанию: 9500 евро.

  • Возврат Lufthansa: 800 евро.

  • Возврат Booking: 700 евро.

  • Возврат Ryanair: 450 евро.

История происходит на разных континентах.

Можно ли вернуть деньги за отмененные из-за пандемии рейсы и что такое ваучеры?

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

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

Итак, хочу рассказать о том, как я оплатил наперед более 10000 евро и успешно вернул все до последнего евроцента.

Как образовались предоплаты на сумму более чем в 10000 евро

Я очень люблю путешествовать и стараюсь посещать новые страны как минимум шесть раз в году. Сам я из Таллинна (Эстония). На 2020 год я возлагал большие надежды, было запланировано множество визитов в другие страны. Главной целью в этом году было посещение Palo Alto, San-Francisco (США), куда мы должны были отправить в марте. Именно по этой причине, в рамках подготовки, сформировались предоплаты на сумму более чем в 10000 евро. Визит мы должны были осуществлять в составе целой группы бизнес-клуба (Baltic Business Club) из 12 человек.

На мне, как на организаторе, лежала задача по планированию и подготовке всех ключевых моментов путешествия, начиная с бизнес-встреч в Калифорнии и проживания группы и заканчивая бронированием авиабилетов и ресторанов. Еще до вылета мы должны были оплатить:

  • 800 евро на человека на перелеты по маршруту Таллинн-Сан-Франциско-Таллинн.

  • 9500 евро за 7 дней проживания группы (100% аванса).

  • Другие минимальные расходы.

Из-за глобальной пандемии наши авиарейсы были отменены, а предоплаченные средства за авиабилеты заморожены. Мы, как и десятки тысяч людей по всему миру, попали в неопределенную ситуацию с риском и вовсе остаться без денег. В особенности это касалось момента с проживанием. Учитывая, что мы вносили оплаты агентству по недвижимости напрямую, а не через Booking или AirBnb, возврат средств оказался очень сложным.

Я расскажу о том, как мне удалось за 7 месяцев вернуть деньги за предоплаченное проживание и авиабилеты от Ryanair, Lufthansa и Booking. В своем рассказе я не буду называть конкретных имен. Я отлично понимаю позицию туристического бизнеса - сегодня тяжелое время для всех.

Общение с агентством результатов не принесло

С марта 2020 года в США начал активно распространяться COVID-19. Наша поездка была запланирована на апрель. Практически каждый день мы отслеживали новости, и вопрос о том, стоит ли ехать вообще, поднимался часто. К концу марта 2020 мэр Сан-Франциско объявил в городе чрезвычайное положение. Это означало, как минимум, перенос нашей поездки, поэтому я принял решение запросить у маклера информацию о возможности возврата уплаченных средств. Только на проживание у нас было заложено более 9000 евро.

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

После десятка отправленных маклеру писем я понял, что этот вариант не принесет результата. Ответ был всегда примерно одинаковым: Мы не можем вернуть Вам деньги, так как на счетах попросту нет средств.

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

Обращение к адвокатам

Не получив положительного ответа от маклера, я принял решение обратиться к адвокатам и получил консультацию от уважаемого агентства в Нью-Йорке. Но единственный вариант решения проблемы, который они предложили создание ими письма-требования (demand letter). Это некая юридическая формальность, так как после составления письма можно подать в суд на возврат денег. Учитывая стоимость адвокатов и судов в Штатах, я оставил данную возможность как план Б. В конце моего повествования у вас будет возможность познакомиться с примером данного письма.

Возврат денег через банковскую процедуру Charge Back

Понимая, что работа с юристами из Нью-Йорка далеко не самый дешевый вариант, я начал поиск других возможностей в Интернете. На одном онлайн-форуме я нашел ветку по возврату денег через банковскую процедуру Charge Back. Несмотря на то, что я понимал, что успеха таким способом добиться вряд ли получится, за неимением лучшего я инициировал процедуру Charge Back через местный Эстонский банк LHV (огромная благодарность его сотрудникам за проделанную работу).

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

В банке меня попросили пройти процесс инициации процедуры Charge Back. В зависимости от банка и страны, она может отличаться. По факту процедура заключалась в подтверждении отказа от предложения услуги продавцов. Необходимо было собрать все выписки и переписки, а также подтвердить, что перевод денег действительно состоялся, а продавец отказал в предоставлении услуги. На оформление процедуры у меня ушло около двух недель, банк требовал максимальное количество данных. Развернутая информация о процедуре представлена в конце текста.

Зачисление 9500 евро долговых обязательств за проживание на мой расчетный счет

После инициации процедуры сотрудники банка сообщили, что процесс может занять до трех недель. При этом гарантий возврата средств никто не давал. По стоимости: я должен был оплатить процедуру только в случае отрицательного результата. То есть если бы я не смог доказать, что услуга не была предоставлена. В этом случае банк бы запросил с меня 10 евро за инициацию процедуры. Безусловно, это того стоило!

Во время ожидания я направил еще несколько писем маклеру, на которые получал похожие ответы. Последние ответы оказались более эмоциональными. Было очевидно, что не один я пытаюсь вернуть средства. Несмотря на то, что мы получили примеры кредитных писем (Letter of Credit), было решено воспользоваться ими только в крайнем случае, ведь я не знал, как могут повлиять подобные письма на поведение маклера. У меня еще была надежда, что на его счету оставались средства, которые я и пытался востребовать с помощью процедуры Charge Back.

После двух недель ожидания и на неделю раньше обещанного срока мне пришло по электронной почте письмо из департамента Charge Back LHV банка. Сотрудница банка сообщила, что процедура успешно пройдена и 9.500 евро возвращены на мой расчетный счет. Вы можете представить мои эмоции в тот момент? Это была победа. Победа во всех отношениях! На тот момент у меня были похожие проблемы с Lufthansa, Ryanair и Booking, и я уже точно знал, как вернуть свои средства.

Возврат средств от Lufthansa

История с Lufthansa была очень похожа. У меня и других членов группы была внесена предоплата за билеты Таллинн-Сан-Франциско стоимостью в 700 евро. В мае 2020 авиарейс был отменен. Сама авиакомпания предложила оформить возврат средств в ваучерах. Этот вариант был неприемлемым, так как на тот момент (период май-апрель 2020 года) образовался глобальный локдаун и я не хотел идти на риски, в результате которых мог бы остаться с ваучерами без возможности их применения.

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

На тот момент у меня уже сложилось понимание того, что данная ситуация практически идентична ситуации в Сан-Франциско, поэтому я написал письмо в Lufthansa. Ответ пришел в течение трех дней, мне предложили еще немного подождать. Мое ожидание длилось около месяца, после чего было написано и отправлено еще одно письмо. Ответ был шаблонным, мне снова предложили подождать. В итоге за 2 месяца ожидания от Lufthansa так и не последовал конкретный ответ. Примерно в это же время я получил ответ из банка по процедуре Charge Back по проживанию и смог получить обратно 9500 евро (предыдущий абзац). После этого мною была инициирована похожая процедура возврата денег, но уже по отношению к Lufthansa. Меня попросили подождать 3 недели для инициации процесса Charge Back, но уже спустя 2 недели я получил все до последнего евроцента.

Возврат средств от Ryanair

События с Ryanair развивались примерно по тому же сценарию, что и с Lufthansa. Мы должны были лететь из Таллинна в Испанию, но в какой-то момент все авиарейсы были отменены. В отличие от Lufthansa, в Ryanair я дозвониться так и не смог из-за перегруженности телефонных линий. Также от Ryanair не последовало ни одного ответа на мои электронные письма. В то время я уже собрал достаточно большой объем информации о Ryanair и понимал, что добиться от них возврата средств посредством звонков и электронных писем невозможно. Единственным предложением от авиакомпании были ваучеры на путешествие, что по понятным причинам мне не подходило.

Собственно, получив возврат средств из Сан-Франциско, я уже имел план действий, который бы помог мне вернуть деньги от Ryanair. Я запросил процедуру Charge Back через банк. Как и в предыдущих случаях, ровно через 2 недели все средства от Ryanair были возвращены. Я встречал информацию о том, что авиакомпания может заблокировать возможность авиаперелетов для клиентов, которые инициировали процесс Charge Back. Однако подтвердить или опровергнуть ее пока не могу, так как до сих пор еще не летал этой авиакомпанией.

Возврат средств от Booking

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

В итоге нам удалось вернуть сумму примерно по 500 евро на человека. Безусловно, был риск того, что к следующему году оставшиеся два отеля могли прекратить свое существование. Но мы смогли минимизировать свои риски. Таким образом, мне удалось вернуть около 400 евро, а еще 100 евро и два отеля были перенесены на следующий год.

Заключение

Я рассказал Вам о том, как сумел вернуть 9500 евро за проживание в Сан-Франциско, а также деньги из Ryanair, Lufthansa и Booking. Могу добавить, что те семь месяцев, на протяжении которых предпринимались попытки вернуть деньги, были для меня очень непростыми. Ведь на мне лежала ответственность не только за собственные средства, но и за деньги всей команды. Во многом именно благодаря этому я отнесся к ситуации со всей серьезностью и шел до конца с твердым намерением вернуть наши средства. Надеюсь, что эта история была для Вас полезной. Теперь Вы знаете, как можно вернуть свои деньги.

Обращаю внимание, что данная история имела место было с февраля 2020 по июль 2020. И на данный момент ситуация с chargeback может быть иная. Также, мне повезло работать с европейским банком LHV, поэтому я не могу сказать, как будет выглядеть процедура возврата через другие банки. Но, если у Вас есть опыт, обязательно расскажите про него ниже.

Благодарю за внимание, буду рад услышать Ваши комментарии и ответить на вопросы.

Пример письма-требования или "demand letter".

Подробнее..

Необычный вариант вечернего освещения в комнате

08.01.2021 20:14:22 | Автор: admin

Поздним вечером, когда хочется очень теплого, но яркого света - невольно вспоминаешь о светодиодах.

  1. белый светодиод 2700k - есть много синего в спектре

  2. "новый" желтый светодиод - о нем статья здесь (PC Amber), не пробовал - но выглядит чуть лучше уже.

  3. ДНаТ - хм, прикроватный вариант в бра, довольно жестко :)

  4. специальная "дневная лампа" - о ней данная статья

Как и многие, не найдя ничего подходящего из готовых решений:

умные лампы - это либо тот же RGB (узкий спектр + ШИМ в придачу) или RGBW - нам не подходит из-за белого светодиода.

пробовал желтую/оранжевую ленту - очень маленькая яркость и цвета неразличимы кроме желтого/оранжевого (очень узкий спектр)

Здесь спектры цветных безлюминофорных светодиодов, кроме PC Amber - я его назвал "новый" желтый светодиодЗдесь спектры цветных безлюминофорных светодиодов, кроме PC Amber - я его назвал "новый" желтый светодиод

пробовал "очень теплые светодиоды" <2000k - синий присутствует в спектре

пробовал безродную китайщину с оранжевым люминофором - это незабываемо - красный свет и синий ореол вокруг :)

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

Смотря один фильм (показали завод по производству микросхем), на потолке были желтые лампы для освещения помещений. Поставив фильм на паузу, я начал искать, какое освещение используют на производстве микросхем и... Нашел то, что искал.

OSRAM CHIP Control - не надо путать с обычной "желтой" дневной лампой

Данная лампа покрыта желтой оболочкой из мягкого пластика (про нее далее), пока поговорим о спектрах. Ниже я привел спектры трех "дневных ламп" обычная белая, красная и chipcontrol. По спектру очень похожа на половину спектра обычной "дневной лампы" 840.

Теперь возьмем цветовой круг и посмотрим, какие цвета мы потеряли (здесь под МГЛ CDM-T 942)

Здесь под лампой CHIP Control (цветопередача относительно невысокая, но на глаз чуть выше чем под ДНаТ. Свет от лампы желто-оранжевый, но если смотреть на лампу, кажется, что он немного красный (см спектр). Под этим светом приятно читать, глаза не устают и не сбиваются циркадные ритмы. Я не утверждаю что это лучший вариант для "вечернего света", но мне и моей супруге очень понравился данный свет.

Вскрывать лампу не будем, внутри кроме двух спиралек, белого порошка и волшебных паров ртути ничего интересного. А вот оболочку - изучим внимательнее.

Попробуем ее просветить фиолетовым фонариком ~420нм (свет полностью задерживается)

Фиолетовый лазер 405нм (свет полностью задерживается)

Красный лазер 650нм (свет проходит)

Синий мощный лазер 445нм (0,5Вт), вот здесь странно - лампа светит желтым, хотя светофильтр должен задерживать данный свет. То ли лазер "безродный", то ли поглощение не 100% на такой мощности.

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

Выбирая лампу, не забываем про нормальный ЭПРА с прогревом для нее (например philips hf-s tl-d серия)

Цена вопроса: лампа стоит 2000-2600р + ЭПРА ~ 1000, обещают срок службы 20 000 часов (неплохо)

P.S. помним, "дневные лампы" не любят постоянного включения/выключения

Про утилизацию, лампу можно сдать там - где купили.

Подробнее..

Не нравится свой интернет-провайдер? Стань им сам опыт американца по имени Джаред Мауч

14.01.2021 00:13:39 | Автор: admin

Качество работы некоторых интернет-провайдеров не выдерживает никакой критики. Подобные компании можно найти в любой стране. Чаще всего проблема в том, что организация является монополистом в своем регионе, поэтому делает, что хочет. Есть на эту тему отличная серия из South Park, которая называется Informative Murder Porn. И хотя в ней показан провайдер кабельного ТВ, сюжет актуален и для интернет-отрасли.

Так вот, в пригороде Мичигана один из клиентов провайдера интернет-услуг остался настолько недоволен сервисом, что сам стал интернет компанией. Он пробросил оптоволокно, сделал разводку, зарегистрировал предприятие и получил скоростной интернет не только для себя, но и стал обеспечивать связью соседей. Имя этого человека Джаред Мауч.

Сразу стоит сказать, что эта история не про обычного американца из пригорода, работающего на маленьком ранчо. У Мауча был и опыт и деньги для реализации своего замысла. Он работает старшим сетевым архитектором в Akamai, так что необходимые для создания нормальной интернет-инфраструктуры знания у него были. Но в любом случае история весьма примечательная.

С чего все началось?


Мауч перебрался в новый дом в пригороде Мичигана в 2002 году. На то время он получит от местного провайдера канал T1 со скоростью в 1,5 Мбит/с, чего было вполне достаточно. Но по мере того, как совершенствовались сетевые технологии, Мауч ожидал, что и сеть в регионе будет модернизироваться. Но нет.

В итоге он переключился на провайдера, предоставляющего услуги беспроводной интернет-связи на скорости в 50 Мбит/с. Также он связался с Comcast, крупнейшей ИТ-компанией из США, спросив, во что обойдется продолжить оптоволокно к его дому. Компания сообщила, что в этом случае придется выложить $50 000. Мауч, как человек небедный, был готов потратить $10 000, но не полсотни тысяч долларов. Как он сам говорил позже, суммы в $50 000 не ожидал, и такую кучу денег непонятно за что платить не хотел.


Пять лет назад еще один крупный провайдер, AT&T, предложил DSL-линию. Но в этом случае скорость обещали ту же, что у него уже была раньше 1,5 Мбит/с. Смысла менять шило на мыло просто не было. Ну а потом провайдер и вовсе отказался от DSL, забросив и планы по модернизации своей инфраструктуры в ряде регионов.


Маучу все это надоело, и четыре года назад он решить стать интернет-провайдером сам. Несколько месяцев назад план удалось реализовать сетевой архитектор продолжил около 10 км оптоволокна и выполил все необходимые для проведения ШПД-интернета в своей дом работы. Более того, 70% его соседей стали его же клиентами. Раньше они использовали мобильные гаджеты для подключения к интернету.

Что за компания?


Американец назвал ее Washtenaw Fiber Properties LLC, зарегистрировав в качестве провайдера телефонной связи (в США только так можно предоставлять интернет клиентам). Правда, телефонных услуг он не предоставляет, равно как не проводит и кабельное.

На все про все Мауч потратил $145 000, что, конечно, немало даже для небедного человека. Но эти деньги он может постепенно вернуть, предоставляя услуги связи все большему количеству клиентов в своем регионе.


$95 000 ушло на прокладку двух интернет-каналов. Один из них сейчас используется для проведения оптоволокна, второй свободен, так что Мауч надеется в недалеком будущем сдать в аренду какой-либо IT-компании, которая придет в этот регион. Протяженность линии от точки подключения до дома Мауча 10 км. Обе магистрали находятся на глубине около 2 м, правда, в некоторых случаях углубляются до 5м, чтобы обойти трубопроводы разного назначения.


Оптоволокно Мауч продолжил сам, без наемных рабочих. Оборудование для этого ему пришлось взять в аренду, поскольку в случае покупки потратить пришлось бы еще $26 000. Дом американца теперь является хабом для подключения его собственных клиентов. На случай проблем с основным каналом связи новый интернет-провайдер подключился к еще одному более крупному поставщику интернет-услуг. В общем, здесь все по-взрослому.

В домах клиентов Mauch устанавливает медиаконвертер Mikrotik RBFTC11 с модулем Ubiquiti PON-to-Ethernet. Клиенты могут использовать свои собственные беспроводные маршрутизаторы или покупать их у Mauch по себестоимости. Он решил не сдавать оборудование в аренду, поскольку это не выгодно для клиентов.

Кстати, некоторые из них добровольно потратили по несколько тысяч долларов США, чтобы снизить затраты Мауча. За это они получили скидки и премиум-обслуживание (бесплатное пиво по субботам?). Выше уже говорилось о том, что потратить пришлось много. Но американец планирует выйти в ноль уже через 3,5 года он продумал бизнес-модель, так что особой проблемы с возвратом денег нет.


Что касается тарифов, то за канал с пропускной способность в 50 Мбит/с американец просит $65 (для США это вполне нормально), $75 за канал в 250 Мбит/с и $99 за 500 Мбит/с. Если дом клиента удален от магистрали, то клиенту приходится платить за прокладку оптоволокна.


Не обошлось и без проблем разные согласования, поломки и даже кража важного оборудования, которое полиция потом нашла благодаря объявлению, созданному вором на Facebook, мешали нормальному ходу реализации проекта. Но Мауч справился и теперь провайдер работает, обеспечивая интернетом свыше 30 домов поблизости. Каждый месяц появляются новые клиенты.

А что насчет жалоб клиентов?


Их нет, по крайней мере, пока. Клиенты довольны, никаких обрывов связи или прочих проблем нет. Единственные несколько запросов, которые поступали увеличение беспроводной зоны доступа в ряде домов. Клиенты просили установить им более мощные роутеры, вместо тех, которые были куплены ими самими.

Официально Мауч не предоставляет каналов связи с пропускной способностью выше 500 Мбит/с. Но в реальности, поскольку клиенты не выбирают всю скорость, зачастую показатель достигает 1 Гбит/с.

Кстати, соседям американца повезло. Жителям пригородов и удаленных от городов регионов приходится туго. Около 17,3% населения в таких локациях не имеют возможности подключиться к ШПД. В крайнем случае это дорогая связь по мобильной сети.

Планы на будущее



Клиентов у Мауча становится все больше. Он говорит, что совсем недавно у него появилось еще 150 потенциальных клиентов. Кроме того, он предоставляет услуги связи одной из небольших компаний, предоставляющих беспроводный интернет в регионе.


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

Подробнее..

Устанавливаем кастомную раскладку на klava.org

15.01.2021 22:23:38 | Автор: admin

Что будет рассказано?

  • Очевидная проблема при изучении своих клавиатурных раскладок

  • Немного о нашем пациенте: klava.org

  • То, как получилось решить данную проблему (спасибо F12)

  • Как правильно настраивать клавиши (при нажатии Shift или AltGr)

  • Автоматизация ручного труда с помощью расширения User JS and CSS

То, с чего всё началось

Когда мы изучаем десятипальцевый метод, для ускорения обучения мы используем различные тренажёры: typingStudy, klava.org, ratatype, rapidTyping и другие. Однако, когда встаёт вопрос об освоении своей особенной, кастомной, раскладки, то "оказывается", что ни один сайт, ни одна программа не предусматривает их существование:

  • В списке поддерживающихся раскладок, своей "конечно же" нет

  • В настройках свою добавить невозможно

  • Самому писать тренажёр - трындец полный

В качестве пациента я выбрал сайт klava.org: неплохой визуал, клавиатура на экране есть, пальцы показываются, разные режимы, ОК. Изначально на сайте есть весьма неплохой список раскладок для русского и английского языков, но нет кучи других, не менее интересных раскладок (да и разработчиков сайта нет смысла судить: раскладок куча, плюсом завтра может появиться новая -> всем не угодить).

Откапываем словарь с раскладками

Как многие из вас знают, есть нажать F12, то выскочит "волшебная" панель с HTML-кодом, CSS, JS, файлами, которые можно без проблем редактировать, и многим другим функционалом, необходимым для разработчиков. Если немного порассуждать, то можно прийти к следующему предположению:

Если сайт писали хорошие ребята, то скорее всего абсолютно всё: расположение символов на экране, автоматическая подсветка нужной буквы/клавиши, отрисовка, будет зависеть только от одного источника: словаря/массива/списка, соответственно, нужно найти тот самый словарь и расположить всё по-своему!

И, да, заветный словарь был найден! В JS этот "словарик" описан одной переменной keyboards. Там ооочень много кода и он, как назло, весь минимизирован, но разобраться можно. Найти этот код достаточно просто:

  1. Жмём F12

  2. Прожимаем Ctrl+F (появится поле для ввода)

  3. Вводим "var keyboards"

  4. Копируем словарик в блокнот (слово var удалите, оно нам больше не понадобится)

  5. Словарь полностью в нашем расположении!!!

Ставим кастомную раскладку вместо ненужной

Теперь нужно подобрать "жертву" - ненужную раскладку, которую будем редактировать. После этого её нужно отыскать (берём название из сайта и не забываем про Ctrl+F). В итоге, получаем массив строк, который нужно обработать: машинописть например выглядит так:

//название раскладки в процессе редактирования менять нельзя!!!'машинопись': ['|<sup>+</sup>', '<sup>1</sup>', '-<sup>2</sup>', '/<sup>3</sup>', '"<sup>4</sup>', ':<sup>5</sup>', ',<sup>6</sup>', '.<sup>7</sup>', '_<sup>8</sup>', '?<sup>9</sup>', '%<sup>0</sup>', '!<sup>=</sup>', ';<sup>\\</sup>', 'Й', 'Ц', 'У', 'К', 'Е', 'Н', 'Г', 'Ш', 'Щ', 'З', 'Х', 'Ъ', ')<sup>(</sup>', 'Ф', '', 'В', 'А', 'П', 'Р', 'О', 'Л', 'Д', 'Ж', 'Э', 'Я', 'Ч', 'С', 'М', 'И', 'Т', 'Ь', 'Б', 'Ю', 'Ё']

Теперь поясню, что здесь происходит:

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

  1. Самый первый символ, символ, который вводится без Shift или AltGr, то есть, просто нажатие

  2. Внутри тегов <sup></sup> находятся символы, которые вводятся при нажатом Shift; символы отрисовываются сверху

  3. Внутри тегов <sub></sub> находятся символы, которые вводятся при нажатом AltGr; символы отрисовываются снизу

'S'       //просто буква, и так всё понятно'(<sup>{</sup><sub>[</sub>' //нажатие ->  (                            //+ Shift ->  {                            //+ AltGr ->  ['ь<sup>ъ</sup>'   //разные буквы тоже можно

Отредактировали? Заменили? Теперь осталось протестировать.

  1. Отрываем сайт

  2. Жмём F12

  3. Открываем Console

  4. Вставляем наш "модифицированный код" (надеюсь var удалить не забыли)

  5. Жмём Enter

  6. Если всё сделано правильно, то сообщений об ошибке не будет

После этого закрываем панельку, выбираем нашу "жертву" и ЧУДО!!! кастомная раскладка работает (вот, что у меня получилось):

Автоматизация

Всё-таки при каждом входе на сайт не хочется каждый раз вручную вставлять этот код. Для решения этой проблемы было создано расширение под названием User Javascript and CSS. Ставим расширение. После уставновки открываем наш сайт, жмём на иконку расширения и жмём Add new. Перед вами откроется 2 поля: JS и CSS, теперь остаётся вставить наш код в поле JS, сохраниться, проверить флажок и кайфануть, что всё работает "само".

Ура!!!! Вы установили свою кастомную раскладку на сайт klava.org. Теперь тренить свою раскладку будет проще, быстрее и (наверное) интереснее.

Подробнее..

Категории

Последние комментарии

© 2006-2021, personeltest.ru