...

суббота, 14 сентября 2019 г.

[Перевод] Тренинг Cisco 200-125 CCNA v3.0. День 40. Стандарт 802.1Х и семейство протоколов ААА

Сегодня мы рассмотрим подраздел 1.7а и раздел 5.4 экзаменационной тематики ICND2. Это описательные темы, то есть Cisco не требует от вас их углубленного изучения. Вы просто должны знать, что означают стандарт 802.1Х и семейство протоколов ААА.

Давайте рассмотрим устройство Cisco и вспомним, как мы входили в такое устройство. Каждый раз для того, чтобы залогиниться в устройстве Cisco, мы настраивали пароль на линии VTY, которые предоставляют доступ к устройству через Telnet. Это означает, что по умолчанию IOS включает команду login и начинает искать пароль line VTY.

Если ввести login local, система начнет искать локальный пароль, настроенный на имя пользователя. Так что если вы войдете в режим глобальной конфигурации и наберете имя пользователя «Имран» и пароль «Имран», а затем зайдете в line VTY и наберете login local, система будет искать локальный пароль, то есть имя пользователя и пароль, который вы установили.
Проблема состоит в том, что обе эти конфигурации настраиваются на устройстве локально, и если у вас в организации тысяча пользователей, каждое из таких устройств должно будет помнить тысячу имен пользователей и паролей. Если у вас 1000 устройств, то каждое из них будет иметь 1000 имен пользователей и 1000 паролей. Представьте, что значит установить такое количество паролей на тысяче или даже на сотне устройств.

Критерии безопасности требуют, чтобы пароли часто менялись. Если вы должны поменять пароль для 1000 пользователей, вам нужно зайти в каждое из сотни или тысячи устройств и проделать это вручную. Это невообразимо огромный объем работы.
Существует оптимальный способ централизованной настройки устройств Cisco. Это семейство протоколов ААА (Authentication, Authorization, Accounting), обеспечивающее выполнение 3-х функций — аутентификацию, авторизацию и учет.

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

Если у нас имеется тысяча таких устройств, доступ к ним настраивается через ААА-сервер, и независимо от того, откуда вы собираетесь войти в такое устройство, оно всегда будет проверять ваше имя пользователя и пароль на этом сервере. Если вам нужно изменить пароль, вы входите на сервер, выполняете настройку, и её параметры применяются ко всем устройствам вашей сети.
ААА в основном использует два протокола – RADIUS и TACACS+.

RADIUS – это протокол удаленной аутентификации и авторизации пользователей, описанный рабочими предложениями RFC 2865-2866. Рабочие предложения собираются под эгидой открытой организации «Общество интернета», когда каждый человек может внести свои предложения по улучшению работы какого-либо программного продукта. Это достаточно старый протокол, разработанный ещё в 1991 году. Он в основном используется для аутентификации сетевых пользователей и использует UDP-порты 1645/1646, а у более новых устройств — порты 1812/1813.
RADIUS поддерживает 3 функции: аутентификацию, исполнительную авторизацию EXEC Authorization и исполнительный учет EXEC Accounting. Аутентификация дает вам возможность получить или не получить доступ к устройству, а авторизация позволяет или запрещает совершать определенные действия, например, вводить на устройстве определенные команды. Она зависит от уровня предоставленных вам привилегий, то есть определяет, что вы можете делать после того, как получите доступ к устройству. Учет означает запись всех команд, введенных вами на устройстве, благодаря чему контролируется соблюдение предоставленных вам полномочий.
TACACS+ — это сеансовый протокол, по которому центральный ААА-сервер принимает решение, разрешать или не разрешать пользователю подключаться к сети. Это усовершенствованная версия проприетарного протокола Cisco TACACS, являющаяся открытым, общедоступным стандартом. Он в большей степени используется для управления устройствами пользователей, например, можете ли вы подключиться к принтеру и что сможете делать с этим принтером.

Этот протокол использует TCP и порт 49. Если у вас между устройством и ААА-сервером имеется файрвол, соответствующий порт RADIUS или TACACS+ нужно поместить в исключения, то есть открыть к нему доступ UDP или TCP трафика. Поэтому очень важно знать номер порта для обеспечения работы этих протоколов.

TACACS+ поддерживает больше функций, чем RADIUS, например, командную авторизацию. В исполнительной авторизации EXEC Authorization сначала нужно указать уровень привилегий пользователя, например Priv 15 или Priv10, то есть указать, например, какие команды может использовать пользователь, авторизированный уровнем Priv 10. В TACACS+ используется более детальный уровень авторизации, то есть имеете возможность указать конкретные команды, которые может или не может применять пользователь, например, установить, что пользователь Priv 15 не может использовать команды управления протоколом маршрутизации. То есть на уровне Command Authorization можно задать, какие именно команды может использовать пользователь и какие команды ему запрещено использовать.

Аналогичная ситуация с функцией Command Accounting – можно точно настроить, какие команды пользователя нужно записывать в логе, а какие игнорировать.

Рассмотрим типичный свитч. Женщина на картинке работает с ноутбуком, подключенным к сетевой розетке. Если вы помните, в одном из видеоуроков ICND1 мы обсуждали PortSecurity, который блокирует порт в зависимости от MAC-адреса устройства.

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

Если у вас имеется физический порт свитча, предположим, сетевая розетка соединена патч-кордом с 48-портовым свитчем, то вы можете настроить каждый из этих портов на работу с 802.1Х.

Когда вы это проделаете, любой трафик DHCP, TFTP, HTTP и т.д. будет блокироваться, кроме трафика по специальному протоколу EAPoL, или Extensible authentication protocol over LAN. Этот трафик поступит на ААА-сервер, и если имя пользователя и пароль соответствуют уровню доступа, порт свитча будет открыт. После этого весь запрещенный до этого трафик DHCP, TFTP, KRB5, HTTP будет разрешен для отправки адресату. Если же имя пользователя и пароль не соответствуют условиям доступа, ничего не изменится – весь трафик, кроме трафика EAPoL, будет по прежнему запрещен для данного порта. Это самое простое объяснение того, что делает стандарт 802.1Х. Давайте рассмотрим процесс работы этого стандарта, который состоит из 3-х компонентов.

Первый компонент – это аппликант, пользователь, которому нужно пройти сетевую аутентификацию. Обычно под Supplicant подразумевают специальное программное обеспечение, которое имеется в составе многих современных ОС. Если ваша ОС его не содержит, вам понадобится установить такую программу. Она позволяет осуществить подключение к доступному порту свитча по протоколу 802.1Х. Свитч является вторым компонентом под названием «аутентификатор». Он расположен между аппликантом и третьим компонентом – сервером аутентификации. Сначала весь трафик пользователя блокируется портом свитча, потому что устройство даже не имеет IP-адреса, ведь все общение с аутентификатором осуществляется только по протоколу EAPoL.

Программное обеспечение Supplicant создает EAPoL – фрейм и отправляет его аутентификатору. Тот создает новый IP-пакет. Если используется RADIUS, то это будет UDP-пакет, если TACACS+, то TCP-пакет, в который помещается EAPoL – фрейм. Пакет отправляется серверу аутентификации, тот проверяет правильность логина и пароля и разрешает или запрещает работу данного пользователя.

Рассмотрим процесс более подробно. Все начинается со стартового сообщения устройства, однако не все устройства его отправляют. Но даже если устройство пользователя подсоединяется к порту без такого сообщения, аутентификатор все равно отправляет ему запрос идентификации Identify Request. В зависимости от настроек аутентификатор Cisco отправляет такой запрос каждые несколько минут или секунд. Если же устройство отправляет Start message, идентификационный запрос свитча будет отправлен немедленно. Далее устройство пользователя отправляет идентификационный ответ Identify Response, который содержит имя пользователя, например, Имран, и этот фрейм поступает к аутентификатору. Тот берет имя «Имран», вкладывает его в IP-пакет и отправляет его серверу аутентификации. Это сообщение называется Access Request, или запрос доступа. Сервер говорит: «Отлично, я получил имя пользователя «Имран», пришлите мне теперь его пароль», и отсылает соответствующее требование доступа Access Challenge аутентификатору.

Не беспокойтесь по поводу углубленного изучения данного процесса, потому что тематика стандарта 802.1Х в курсе ICND2 носит описательный характер.

Аутентификатор отсылает соответствующий запрос аппликанту, и тот высылает ему пароль. Здесь имеются нюансы: пользователь не может отослать серверу пароль в виде простого текстового сообщения, здесь необходимо туннелирование с использованием шифрования. В данном случае Supplicant должен использовать TLS или PIP – Protected IP, либо протокол проверки подлинности MS-CHAP.

Итак, устройство пользователя отсылает пароль по зашифрованному туннелю, и он поступает серверу аутентификации внутри запроса доступа Access Request. Если пароль правильный, сервер отсылает обратно разрешение доступа Access Accept. Если проверка прошла успешно, аутентификатор отсылает пользователю сообщение Success, после чего порт открывается для пропуска трафика. Таков процесс перевода порта из состояния фильтрации, или shutdown, в состояние open по протоколам стандарта сетевой аутентификации 802.1Х. Можно сказать, что данный фреймворк предоставляет вам более высокий уровень безопасности доступа к сети, чем стандартный PortSecurity.

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

В зависимости от используемого протокола, RADIUS или TACACS+, устройствам пользователя разрешается работать с определенной VLAN. Например, после аутентификации ноутбук 1 может использовать для связи VLAN 100, а ноутбук 2 – VLAN 200. Все эти параметры можно настроить с помощью упомянутых протоколов аутентификации по стандарту 802.1Х.


Спасибо, что остаётесь с нами. Вам нравятся наши статьи? Хотите видеть больше интересных материалов? Поддержите нас оформив заказ или порекомендовав знакомым, 30% скидка для пользователей Хабра на уникальный аналог entry-level серверов, который был придуман нами для Вас:Вся правда о VPS (KVM) E5-2650 v4 (6 Cores) 10GB DDR4 240GB SSD 1Gbps от $20 или как правильно делить сервер? (доступны варианты с RAID1 и RAID10, до 24 ядер и до 40GB DDR4).

Dell R730xd в 2 раза дешевле? Только у нас 2 х Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 ТВ от $199 в Нидерландах! Dell R420 — 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB — от $99! Читайте о том Как построить инфраструктуру корп. класса c применением серверов Dell R730xd Е5-2650 v4 стоимостью 9000 евро за копейки?

Let's block ads! (Why?)

Беспилотники использовались для атаки на крупные нефтеперерабатывающие заводы Саудовской Аравии

Дроны были использованы для атаки на крупнейшие нефтеперерабатывающие заводы Саудовской Аравии. В результате 14 сентября произошли пожары на месторождениях компании Saudi Aramco в Абкайке и Хурайсе.
image
Источник: РБК

По информации Al-Arabiya, на месте происшествия были слышны выстрелы; также сообщалось о взрывах. Reuters со ссылкой на представителя МВД страны сообщает, что возгорания локализованы.

Одно из пострадавших сооружений — нефтеперерабатывающий завод в Абкайке, который способен перерабатывать до 7 млн т баррелей сырья в сутки, уточнили Al-Arabiya и Associated Press.

В 2006 году неудачную попытку атаки этого завода предприняли боевики-смертники запрещенной в России террористической организации «Аль-Каида».

Ответственность за нападения на объекты Saudi Aramco взяли на себя повстанцы-хуситы из шиитского движения «Ансар Аллах», которые 17 августа использовали десять беспилотников для атаки на завод по производству природного газа на нефтегазовом месторождении Шайба Saudi Aramco.

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

«Скорее всего, за атакой на Saudi Aramco стоят хуситы, учитывая, что они уже использовали дроны в подобных акциях и, вероятно, смогли наладить сборку таких дронов. Удары в субботу стали самыми масштабным из всех, что предпринимались до сих пор. Под атаки попадают все более важные объекты и, как мы видим, Саудовская Аравия не в состоянии предотвратить такие нападения», — сказал эксперт.

Bloomberg со ссылкой на источник, знакомый с ситуацией, а также три собеседника Reuters рассказали, что нападения на заводы привели к уменьшению добычи нефти в Саудовской Аравии вдвое. В результате производство сократилось примерно на 5 млн барр./сутки — что составляет около 5% от суточной добычи нефти в мире.

Saudi Aramco — государственная нефтяная компания Саудовской Аравии, одна из крупнейших в мире по добыче нефти и размеру нефтяных запасов. В 2018 году она стала самой прибыльной компанией в мире. Чистая прибыль Saudi Aramco составила 111 миллиардов долларов. Это больше, чем показатели Apple, Google и ExxonMobil вместе взятых.

Let's block ads! (Why?)

[Из песочницы] Душевный Mikrotik против бездушного РКН и такого же провайдера

Как микроконтроллер может читать данные на скорости 1.6 Gbps

Всем доброго времени суток! никогда такого не было и вот опять. С моей прошлой статьи прошло уже достаточно времени, а оно ставит новые задачи. И если раньше я передавал данные на скорости 100 Mbps, то теперь пришлось замахнутся на 1600 Mbps…

На КПДВ — герой нашего романа — он смог читать данные на такой скорости!

Итак, мой очередной проект потребовал читать поток данных вида 32-бита на скорости 50 МГц (это и будет, кстати, тот самый 1.6 Gbps) в количестве заранее известном — пусть будет 10000. Было бы просто отлично читать сразу с помощью DMA из одного порта — но вот, к сожалению, подходящих процессоров не нашлось (надеюсь, в комментариях это дело кто-нибудь поправит), у всех подходящих по скорости порты почему-то 16-битные.

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

А так как процессор у нас — stm32h750 на 400 Мгц, а шина и таймеры на 200 Мгц, то все должно
получится.

Казалось бы простой случай — по сигналу запускать одну пересылку DMA. Но вот только у DMA нет такой возможности — порт может выдать прерывание, но не может управлять DMA. Но зато у нашего процессора есть хорошая штука — DMAMUX, в котором есть генератор событий для канала DMA, а вот в этом генераторе есть две подходящие возможности — или использовать прерывание EXTIT0 или сигнал от таймера TIM12 (такая вот странная фантазия была у разработчиков чипа).

По прерыванию мы не успеем по времени — на даже пустую отработку нужно порядка 47 тактов, а такт у нас — 2.5 ns…

Зато успеем по таймеру. Осталось только затактировать таймер от внешнего сигнала в 100 МГц, причем длину таймера ставим в 1 и по его выходу TRGO будет срабатывать генератор DMAMUX, а уж он-то задаст команду на пересылку DMA и тот прочитает порт и перешлет данные в память.

Но стоп! Порт-то 16-бит, а у нас 32… Что же, можно попробовать читать еще второй порт… Только вот для этого нужен второй канал DMA, и еще это займет ту же шину — то есть прочитать-то мы успеем, но вот можем не успеть записать данные в память. Что-же, теоретически, у этого процессора есть разные типы памяти, и на большом рисунке структуры процессора можно видеть, что и DMA и память RAM_D1 сидят на одной и той-же шине с частотой 200 МГц. Осталось проверить практически.

DMA1->LIFCR |= ~0;
DMA1_Stream0->CR = (0b11 << DMA_SxCR_PL_Pos) | (0b01 << DMA_SxCR_MSIZE_Pos)
    | (0b01 << DMA_SxCR_PSIZE_Pos) | DMA_SxCR_MINC;
DMA1_Stream0->M0AR = (uint32_t) data;
DMA1_Stream0->PAR = (uint32_t) &(GPIOE->IDR);
DMA1_Stream0->NDTR = 10000;
DMA1_Stream1->CR = (0b11 << DMA_SxCR_PL_Pos) | (0b01 << DMA_SxCR_MSIZE_Pos)
    | (0b01 << DMA_SxCR_PSIZE_Pos) | DMA_SxCR_MINC;
DMA1_Stream1->M0AR = (uint32_t) data2;
DMA1_Stream1->PAR = (uint32_t) &(GPIOD->IDR);
DMA1_Stream1->NDTR = 10000;
DMAMUX1_Channel0->CCR = DMAMUX_CxCR_EGE | (1);
DMAMUX1_Channel1->CCR = DMAMUX_CxCR_EGE | (2);
DMAMUX1_RequestGenerator0->RGCR = DMAMUX_RGxCR_GE
    | (0b01 << DMAMUX_RGxCR_GPOL_Pos) | (7);
DMAMUX1_RequestGenerator1->RGCR = DMAMUX_RGxCR_GE
    | (0b01 << DMAMUX_RGxCR_GPOL_Pos) | (7);
DMA1_Stream0->CR |= DMA_SxCR_EN;
DMA1_Stream1->CR |= DMA_SxCR_EN;
TIM12->CNT = 0;
TIM12->CCMR1 |= TIM_CCMR1_CC2S_0;
TIM12->CR2 = (0b010 << TIM_CR2_MMS_Pos);
TIM12->CR1 |= TIM_CR1_CEN;
while (DMA1_Stream0->NDTR)
    i++;
TIM12->CR1 &= ~TIM_CR1_CEN;

Ну и конечно, надо разместить массивы data и data2 в нужном сегменте памяти, это делается так:

__attribute__((section(".dma_buffer"))) uint16_t data[10240],data2[10240];

а в файле для линковщика указываем:

.dma_buffer : {
 *(.dma_buffer)
 } >RAM_D1

Для проверки, ну и как первый вариант, был реализован просто тупое копирование с помощью
центрального процессора (все же 400 МГц):

       uint16_t * ptr = cpudata;
        volatile uint16_t * src = &(GPIOE->IDR);
        volatile uint16_t * src2 = &(GPIOD->IDR);
        for (register int i = 0; i < 10000; i++) {
                *ptr++ = *src;
                *ptr++ = *src2;
        }

Для проверки данные cpudata размещались в разной памяти, быстрее всего подошла (ну она, правда, всего 64К) сверхбыстрая память (тоже 400 МГц) DTCMRAM.

Результаты

В процессе испытаний выяснилось, что с помощью CPU можно читать на скорости 12.5 МГц из двух портов. И 25 МГц из одного. Так что вариант не работает…

С помощью DMA и такой-то матери TIM12 удалось успешно читать на скорости 50 МГц, причем за несколько часов теста ошибок не было ни одной. Читались оба порта, но замерить, насколько отстает чтение по второму DMA, пока не удалось…

Так что в моем (немного вырожденном) случае, удалось достигнуть скорости передачи информации в процессор stm32h750 на скорости 32x50 = 1600 Mbps.

Let's block ads! (Why?)

Почему вернулся винил, и как с этим связаны стриминговые сервисы

Пластинки покупают все чаще. Аналитики из Американской ассоциации звукозаписывающих компаний (RIAA) отмечают, что к концу года доходы от продажи винила превысят показатели CD — такого не случалось более 30 лет. Рассказываем о причинах такого бума.


Фото Miguel Ferreira / Unsplash

Виниловый «ренессанс»


Винил оставался популярным музыкальным форматом вплоть до середины 80-х. Позже его стали вытеснять CD и другие цифровые форматы. В начале нулевых казалось, что пластинки уже ушли в прошлое, однако в 2010-х спрос на них вновь стал набирать обороты — только в 2016 году продажи винила выросли на 53% [и мы даже представили свою витрину — у нас в Аудиомании].

В этом году пластинки двигаются дальше и могут взять новую высоту. Специалисты из Американской ассоциации звукозаписывающих компаний отмечают, что доходы от продажи виниловых пластинок постепенно обгоняют доходы от продажи дисков. За первую половину 2019-го жители США потратили на покупку пластинок $224 млн, а на CD — $247 млн. Эксперты говорят, что винил сократит «разрыв» до конца года. Разберемся, что способствует росту интереса к нему.

Причины


Как ни странно, одним из главных факторов возрождения винила, считается рост популярности стриминговых платформ. Но чем больше люди «уходят в цифру» и пользуются преимуществами стриминга в процессе прослушивания музыки на работе или в транспорте, тем более интересными становятся «оффлайновые» и форматы, которые являются полной противоположностью. Они подходят для менее динамичных ситуаций — прослушивания музыки дома или в узком кругу единомышленников в клубе. Одним из тех, кто отдает предпочтение пластинкам является участник группы The White Stripes Джек Уайт. Он говорит, что стриминг хорошо играет роль инструмента для поиска новых песен и исполнителей, однако слушать музыку он предпочитает на виниле.


Фото Priscilla Du Preez / Unsplash

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

Немаловажную роль в популярности винила играет эстетическая составляющая и тяга к коллекционированию. Можно сказать, что отчасти это желание формирует картинка, которую рисуют в головах зрителей режиссеры некоторых фильмов и сериалов. Виниловые проигрыватели периодически появляются в фильмах Вуди Аллена, свои библиотеки пластинок есть у таких героев, как Тони Старк из «Железного человека» и капитан Кирк из «Стартрека» (кстати, подробно о роли пластинок в фильмах мы рассказывали в одном из предыдущих материалов).

Отдельные коллекционеры-эстеты не просто формируют библиотеку любимой музыки на виниле, а собирают уникальные релизы. Например, в 2012 году Джек Уайт совместно с лейблом Third Man Records выпустил ограниченным тиражом виниловый сингл «Sixteen Saltines». Его записали на пластинку, изнутри заполненную голубой жидкостью. Учитывая, что до Джека Уайта никто ничего подобного не делал, эти записи высоко ценятся среди коллекционеров.

Стриминговые сервисы все равно впереди


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


Фото James Sutton / Unsplash

По данным RIAA, в первой половине 2019 года на продажи виниловых пластинок пришлось всего 4% от общего дохода музыкальной индустрии в стране. У стриминговых сервисов доля составила 62%. При этом количество продаваемых пластинок также остается на низком уровне — крупные тиражи даже у таких известных исполнителей, как Radiohead и Daft Punk, не превысили 30 тыс. экземпляров. Но ситуация все же может измениться, хотя и незначительно.

Возвращаясь к винилу


Эксперты говорят, что в ближайшем будущем продажи винила будут только увеличиваться. Эту точку зрения подтверждает рост числа фабрик, занимающихся производством пластинок. В 2017 году на территории США было открыто менее 30 заводов, а сегодня их количество выросло до 72.
Новые производства запускаются и в России — например, пластинки печатают на заводе «Ультра Продакшн» в Москве.

Также развиваются компании, которые выпускают современные прессы для печати пластинок. Например, в Соединенных Штатах новые станки поставляет Record Products of America. Разрабатывают и новые технологии, призванные увеличить объемы «винилового» производства. Компания Viryl Technologies из Канады спроектировала станок, в котором отсутствует газовый нагреватель. Такой подход позволит уменьшить размеры установки и разместить в цехе больше оборудования. Все это поспособствует дальнейшему развитию виниловой отрасли.

Дополнительное чтение — из нашего «Мира Hi-Fi»:

Кто выпускает винил? Самые интересные лейблы на сегодняшний день
Винил вместо почтовой марки: необычный раритет
Vinyl Bluetooth Speaker: виниловая пластинка добавит баса Bluetooth-колонке
«Камера, мотор, музыка!»: как режиссеры используют винил в кино
«Между винилом и кассетой»: история тефифона
Что такое HD-винил и действительно ли он так хорош
Сказки в СССР: история «детского» винила


Let's block ads! (Why?)

Мультимедиа центр «Kodi» и Yocto Project


Введение в Yocto Project


Yocto Project — это совместный Open Source проект для упрощения разработки дистрибутивов для встраиваемых систем. Yocto содержит большое количество шаблонов, метаданных и инструментальных средств сборки. В Yocto Project вы можете подключить большое количество BSP (пакет поддержки платформы) слоев для всевозможных аппаратных платформ.

Основное назначение статьи — это попытка показать сборку типового дистрибутива в Yocto Project на примере хорошо известного мультимедиа центра Kodi, версии 17.6 для одноплатного компьютера «Raspberry Pi» 3B.

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

Статья является сугубо практической и демонстрирует возможности использования наработок Yocto Project и OpenEmbedded для сборки мультимедиа центра «Kodi». Управление слоями Yocto осуществляется с помощью утилиты Repo от Google. Статья из серии «Документируй это».

Итак: поехали.

Содержание:


    Установка Yocto Project в Ubuntu
    Механизм сборки дистрибутива в Yocto Project
    Использование OpenEmbedded совместно c Yocto Project
    Пакет поддержки платформы (BSP)
    Управление слоями Yocto c помощью Repo
          Установка Repo
          Манифест для сборки дистрибутива
          Содержимое Манифеста
          Описание Манифеста
          Структура bs-manifest
          Инициализация переменных Poky
          Инициализация Repo
          Синхронизация Repo
    Создание конфигурации Yocto Project
          Конфигурационный файл build/conf/local.conf
          Конфигурационный файл build/conf/bblayers.conf
    Слой для сборки мультимедиа центра
          Структура слоя
          Конфигурация слоя
          Состав recipes-berserk
          Состав recipes-core
          Состав recipes-kernel
          Состав recipes-mediacentre
          Состав recipes-multimedia
          Дополнение рецепта для сборки Kodi
          Добавление нового пункта в меню настроек Kodi
          Максимальные настройки буферизации для видео
    Просмотр телевидения по протоколу IPTV
    Просмотр Youtube с помощью Kodi плагина
    Консольное shell расширение конфигурации сети
    Рецепт сборки дистрибутива
    Краткая инструкция по созданию образа дистрибутива
    Постскриптум

Установка Yocto Project в Ubuntu


Для сборки дистрибутива с помощью Yocto Project в Ubuntu вам необходимо установить следующие пакеты:
sudo apt-get install -y --no-install-suggests --no-install-recommends \
gawk wget git-core diffstat unzip texinfo gcc-multilib \
build-essential chrpath socat cpio python python3 python3-pip python3-pexpect \
xz-utils debianutils iputils-ping python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev \
xterm


Пакеты устанавливаются с помощью команды apt-get install и команды повышения привилегий — sudo. В системе Ubuntu это широко распространенная практика, когда для выполнения административных действий используется команда sudo (при создании основного пользователя системы, он автоматически прописывается в группу «sudo»).

Более подробную инструкцию по установке вы можете посмотреть здесь:

Механизм сборки дистрибутива в Yocto Project


В Yocto Project каждая программная единица описывается с помощью рецепта сборки. Язык описания рецепта напоминает «bash» c возможностью вставок частей кода на языке «python». Основную информацию по синтаксису вы можете почерпнуть из руководства Yocto Project . Набор рецептов сборки в зависимости от назначения можно объединять в отдельные слои сборки.

Слои разделяются на аппаратно зависимые — BSP слои, UI слои (пользовательского интерфейса), специфические Yocto слои, а также слои реализующие определенную функциональность:
например слои из OpenEmbedded => multimedia, python, perl, ruby, networking, systemd, webserver и т.д.

Использование OpenEmbedded совместно c Yocto Project


И еще, если вы будете использовать Yocto Project, то наверняка вам понадобятся слои с дополнительной функциональностью т.е. большой набор рецептов на все случаи жизни. И такой набор есть — это рецепты из OpenEmbedded. OpenEmbedded — инфраструктура для сборки пакетов для встраиваемого Linux.

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

Пакет поддержки платформы (BSP)


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

В этой статье используется BSP слой — meta-raspberrypi
Репозиторий слоя располагается по адресу: git.yoctoproject.org/git/meta-raspberrypi

Управление слоями Yocto c помощью Repo


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

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

Repo — это основной инструмент для управления GIT репозиториями при сборке операционной системы «Андроид» с его большой кодовой базой. Repo позволяет в одном проекте управлять десятком, если не сотней отдельных гит репозиториев, версии которых вы можете аккуратно указать в одном xml файле Манифеста

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

repo sync

Установка Repo


C помощью следующего набора команд вы можете установить Repo в ваш домашний каталог ~/bin
(команду curl можно установить отдельно: sudo apt-get install curl)
PATH=${PATH}:~/bin
mkdir ~/bin
curl http://commondatastorage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo

и в дальнейшем вам достаточно использовать в консоли команду: repo

или так,
если каталог HOME/bin не добавился с стандартный путь запуска автоматически
см. файл HOME/.profile

$
$ PATH=${PATH}:~/bin
$ repo
$


Манифест для сборки дистрибутива


Дистрибутив, собираемый в рамках статьи, мне нужно как то назвать. Пусть это будет имя Torvin . Под кодовым названием Torvin будет содержаться минималистический Linux дистрибутив со сборкой одной единственной программы. Имеется ввиду одна прикладная пользовательская программа — Kodi, и ничего лишнего (все остальное системный уровень). Для мультимедиа центра, на мой взгляд этого вполне достаточно.

Содержимое Манифеста


Для управления слоями дистрибутива используется файл torvin-0.2.5.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <manifest>

    <default sync-j="4" revision="rocko"/>

    <remote fetch="https://git.yoctoproject.org/git" name="yocto"/>
    <remote fetch="https://github.com/openembedded" name="oe"/>
    <remote fetch="https://github.com/berserktv" name="bs"/>


    <project remote="bs" revision="master" name="bs-manifest" path="sources/bs-manifest">
        <linkfile dest="setup-environment" src="setup-environment"/>
        <linkfile dest="shell.sh" src="raspberry/shell.sh"/>
        <linkfile dest="sources/base" src="raspberry/rocko"/>
    </project>

    <project remote="yocto" revision="rocko" name="poky" path="sources/poky"/>
    <project remote="oe"    revision="rocko" name="meta-openembedded" \
                                             path="sources/meta-openembedded"/>
                                                
    <project remote="yocto" revision="rocko" name="meta-raspberrypi" \
                                             path="sources/meta-raspberrypi"/>

    <project remote="bs"    revision="rocko" name="berserk" path="sources/berserk"/>

    </manifest>


Описание Манифеста


В начале манифеста тегами remote обозначены два основных GIT репозитория и один вспомогательный:

https:⁄⁄git.yoctoproject.org/git — Yocto репозиторий, именованный как yocto
https:⁄⁄github.com/openembedded — OpenEmbedded репозиторий, именованный как oe
https:⁄⁄github.com/berserktv — вспомогательный GIT репозиторий, именованный как bs

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

    remote — имя удаленного именованного репозитория
    revision — название ветки или hash версии
    name — имя проекта в указанном репозитории
    path — локальный путь проекта в вашей файловой системе

Пример работы тега
<project remote="bs" revision="master" name="bs-manifest" path="sources/bs-manifest">
</project>

данный xml тег описывает выполнение примерно следующей команды:

git clone https://github.com/berserktv/bs-manifest -b master sources/bs-manifest


В теле тега project я указал команды создания символических ссылок на нужную мне инфраструктуру вспомогательных скриптов начальной инициализации и штатного запуска системы сборки Poky
Пример указания тегов linkfile
<project remote="bs" revision="master" name="bs-manifest" path="sources/bs-manifest"> 
  <linkfile dest="setup-environment" src="setup-environment">
  <linkfile dest="shell.sh" src="raspberry/shell.sh">
  <linkfile dest="sources/base" src="raspberry/rocko">
</project>

Создание символической ссылки разворачивается примерно
в следующую команду: ln -s src dest
т.е.
      # создание двух ссылок на файлы
      ln -s sources/bs-manifest/setup-environment  setup-environment
      ln -s sources/bs-manifest/raspberry/shell.sh shell.sh 
      
      # создание ссылки на каталог, разворачивается
      # примерно в следующий набор команд
      cd sources
      ln -s bs-manifest/raspberry/rocko base 


Структура bs-manifest

├── COPYING.MIT
├── raspberry
│   ├── rocko
│   │   ├── conf
│   │   │   ├── bblayers.conf
│   │   │   └── local.conf
│   │   └── torvin-0.2.5.xml
│   └── shell.sh
├── README.md
└── setup-environment

Проект bs-manifest используется для гибкого управления конфигурациями, с учетом сборок разных версий дистрибутива. У меня это версия — 0.2.5

Инициализация переменных Poky


Скрипт начальной инициализации setup-environment был взят из проекта Freescale Community (в среде yocto это часто встречающееся решение). Скрипт отвечает за стартовую инициализацию переменных системы сборки Poky, скрипт создает структуру каталогов, в которой очень хорошо разделяется:
  • build — каталог сборки
  • source — исходный код рецептов сборки
  • download — каталог загрузки кода программ (git базы, tar.gz архивы)

Содержимое скрипта setup-environment можно посмотреть здесь:
Содержимое скрипта shell.sh
    #!/bin/bash

    MACHINE='raspberrypi3' source ./setup-environment build

    echo "you may try 'bitbake core-image-minimal'"
    bash
    


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

Инициализация Repo


Для инициализации repo необходимо выполнить команду:
mkdir torvin
cd torvin
repo init -u https:⁄⁄github.com/berserktv/bs-manifest -m raspberry/rocko/torvin-0.2.5.xml


где -u https:⁄⁄github.com/berserktv/bs-manifest указывает GIT путь к проекту манифеста

примечание: можно еще указать -b имя_ветки
(если ключ -b не указывать, то подразумевается ветка master (по умолчанию))

где путь -m raspberry/rocko/torvin-0.2.5.xml к конфигурационному файлу указывает следующее:

  1. Имя аппаратной платформы для которой осуществляется сборка — raspberry
  2. Имя основной рабочей ветки Yocto/OpenEmbedded — rocko
  3. Кодовое имя версии — torvin (все версии серии 0.2.x)
  4. Цифровой номер версии которая собирается — 0.2.5

Синхронизация Repo


для начальной загрузки или последующей синхронизации достаточно выполнить команду:
repo sync


которая заберет все последние версии GIT проектов указанных в файле манифеста (обычно указываются ветки), если же у вас указан hash коммита или имя тега в атрибуте revision, то версия для данного git репозитория не измениться. Имя тега можно указать так: revision=«refs/tags/v0.2.5»

Создание конфигурации Yocto Project


После того, как будет выполнена команда repo sync, можно приступать к созданию основной конфигурации Yocto Project:
./shell.sh


после завершения работы скрипта, будет создан каталог build/conf:
c двумя основными файлами:
  • local.conf — управляющие переменные сборки:
    название платформы, тип дистрибутива и пакетов сборки и т.д.
  • bblayers.conf — конфигурация подключенных слоев Yocto Project

по умолчанию скрипт setup-environment ищет по пути sources/base/conf
начальную конфигурацию, и если файлы local.conf и bblayers.conf
существуют, то они копируются в build/conf
(см. переменную TEMPLATES в setup-environment)

т.е. файлы берутся из sources/bs-manifest/raspberry/rocko/conf
см. создание символической ссылки на base

Конфигурационный файл build/conf/local.conf


Показать/Скрыть
    MACHINE ??= 'raspberrypi3'
    DISTRO ?= 'poky'
    PACKAGE_CLASSES ?= "package_deb"
    EXTRA_IMAGE_FEATURES ?= "debug-tweaks"
    USER_CLASSES ?= "buildstats image-mklibs image-prelink"
    PATCHRESOLVE = "noop"
    BB_DISKMON_DIRS = "\
        STOPTASKS,${TMPDIR},1G,100K \
        STOPTASKS,${DL_DIR},1G,100K \
        STOPTASKS,${SSTATE_DIR},1G,100K \
        STOPTASKS,/tmp,100M,100K \
        ABORT,${TMPDIR},100M,1K \
        ABORT,${DL_DIR},100M,1K \
        ABORT,${SSTATE_DIR},100M,1K \
        ABORT,/tmp,10M,1K"
    PACKAGECONFIG_append_pn-qemu-native = " sdl"
    PACKAGECONFIG_append_pn-nativesdk-qemu = " sdl"
    CONF_VERSION = "1"

    DL_DIR ?= "${BSPDIR}/downloads/"

    # size memory GPU for Raspberry Pi
    GPU_MEM = "128"
    GPU_MEM_256 = "112"
    GPU_MEM_512 = "160"
    GPU_MEM_1024 = "320"

    # for libs: "mpeg2dec libmad ffmpeg x264"
    LICENSE_FLAGS_WHITELIST += "commercial"
    


Конфигурационный файл build/conf/bblayers.conf


Показать/Скрыть
    # POKY_BBLAYERS_CONF_VERSION is increased each time build/conf/bblayers.conf
    # changes incompatibly
    LCONF_VERSION = "6"
    POKY_BBLAYERS_CONF_VERSION = "2"

    BBPATH = "${TOPDIR}"
    BSPDIR := \
    "${@os.path.abspath(os.path.dirname(d.getVar('FILE', True)) + '/../..')}"
    BBFILES ?= ""

    BBLAYERS ?= " \
    ${BSPDIR}/sources/poky/meta \
    ${BSPDIR}/sources/poky/meta-poky \
    ${BSPDIR}/sources/poky/meta-yocto-bsp \
    ${BSPDIR}/sources/meta-openembedded/meta-oe \
    ${BSPDIR}/sources/meta-openembedded/meta-python \
    ${BSPDIR}/sources/meta-openembedded/meta-networking \
    ${BSPDIR}/sources/meta-openembedded/meta-multimedia \
    ${BSPDIR}/sources/meta-openembedded/meta-filesystems \
    ${BSPDIR}/sources/meta-raspberrypi \
    ${BSPDIR}/sources/berserk/meta-berserk \
    "
    

основные переменные файла local.conf — на которые нужно обратить внимание:

  • MACHINE — название платформы под которую осуществляется сборка
  • DISTRO — название категории дистрибутива
  • PACKAGE_CLASSES — формат пакетов для установки ПО
  • LICENSE_FLAGS_WHITELIST — использование дополнительных лицензий

специфические настройки для семейства плат Raspberry Pi
  • GPU_MEM = «128» — количество видео памяти для видео адаптера GPU (выделяется из ОЗУ)
  • GPU_MEM_256 = «112» — то же самое только для плат с общим размером ОЗУ = 256Мб
  • GPU_MEM_512 = «160» — то же самое только для плат с общим размером ОЗУ = 512Мб
  • GPU_MEM_1024 = «320» — то же самое только для плат с общим размером ОЗУ = 1024Мб

примечание:
например если оставить только переменную GPU_MEM = «128»,
то для всех плат RPI, RPI2, RPI3
не зависимо от количества реальной оперативной памяти
на плате будет всегда выделено для GPU — 128Мб
(и общий размер ОЗУ уменьшиться на это значение)

в случае указания всех переменных, директивы GPU_MEM_256, GPU_MEM_512, GPU_MEM_1024 являются более приоритетными.

Для сборки Мультимедиа центра, помимо штатных слоев Yocto см. файл bblayers.conf

${BSPDIR}/sources/poky/meta \
${BSPDIR}/sources/poky/meta-poky \
${BSPDIR}/sources/poky/meta-yocto-bsp \


я подключил четыре слоя с дополнительной функциональностью из OpenEmbedded.

Мультимедиа Центр Kodi — является сложной программой, которая использует большое количество внешних библиотек и каждую библиотеку нужно собрать с помощью рецепта сборки, поэтому по возможности я буду использовать все рецепты из OpenEmbedded в категории Multimedia

Итак, у меня подключен слой Multimedia и слои от которых он зависит

${BSPDIR}/sources/meta-openembedded/meta-oe \
${BSPDIR}/sources/meta-openembedded/meta-python \
${BSPDIR}/sources/meta-openembedded/meta-networking \
${BSPDIR}/sources/meta-openembedded/meta-multimedia \


затем подключен еще один слой OpenEmbedded, для работы с файловыми системами
${BSPDIR}/sources/meta-openembedded/meta-filesystems \


далее подключен основной BSP слой для платформы Raspberry Pi
${BSPDIR}/sources/meta-raspberrypi \


ну и в самом конце подключен дополнительный слой, отвечающий за сборку образа дистрибутива с функциональностью «Мультимедиа центра»
${BSPDIR}/sources/berserk/meta-berserk \


Слой для сборки мультимедиа центра


На мой взгляд Yocto Project является промышленным комбайном для создания встраиваемых дистрибутивов. Но если вы когда нибудь работали с системой сборки Buildroot, то Yocto может вам показаться достаточно громоздким. Он использует огромное количество свободного места на жестком диске. Для нормальной работы Yocto требуется порядка 80 — 100 Гб свободного пространства и это обычно с учетом сборки только под одну платформу.

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

Итак, вся функциональность, для сборки «Мультимедиа центра» у меня находиться с отдельном слое:

https://github/berserktv/berserk


(Название взято из моей любимой книги «Молот и крест», писателя Гарри Гаррисона.)
(Torvin также является персонажем данной книги.)

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

Структура слоя

├── COPYING.MIT
├── meta-berserk
│   ├── conf
│   │   └── layer.conf
│   ├── recipes-berserk
│   │   ├── bs-net
│   │   ├── first-run
│   │   ├── images
│   │   └── tv
│   ├── recipes-core
│   │   ├── init-ifupdown
│   │   └── psplash
│   ├── recipes-kernel
│   │   └── linux
│   ├── recipes-mediacentre
│   │   ├── kodi
│   │   └── kodi-plugins
│   └── recipes-multimedia
│       └── ffmpeg
├── README.md
└── changelog.txt

Состав слоя:

  • conf — конфигурация слоя
  • recipes-berserk — рецепт сборки дистрибутива, рецепты настройки тв, сети и первого запуска
  • recipes-core — базовые рецепты, в частности модификация рецепта стартовой заставки
  • recipes-kernel — рецепты сборки Linux ядра
  • recipes-mediacentre — рецепты сборки Kodi и его плагинов
  • recipes-multimedia — мультимедиа рецепты, сборка ffmpeg

конфигурация слоя


включает файл layer.conf
    # We have a conf and classes directory, add to BBPATH
    BBPATH .= ":${LAYERDIR}"

    # We have a packages directory, add to BBFILES
    BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
                ${LAYERDIR}/recipes-*/*/*.bbappend"


    BBFILE_COLLECTIONS += "bs"
    BBFILE_PATTERN_bs := "^${LAYERDIR}/"
    BBFILE_PRIORITY_bs = "5"


    DISTRO_FEATURES_append += " wifi x11"

    PREFERRED_VERSION_ffmpeg = "3.1.11"
    SYSVINIT_ENABLED_GETTYS  = "1"

    PREFERRED_VERSION_linux-raspberrypi ?= "4.9%"
    


Файл содержит указание версии библиотеки ffmpeg, номер версии linux ядра, а также количество виртуальных консолей (tty), и включает особенности дистрибутива — wifi x11
DISTRO_FEATURES_append += " wifi x11"

PREFERRED_VERSION_ffmpeg = «3.1.11»
SYSVINIT_ENABLED_GETTYS = «1»

PREFERRED_VERSION_linux-raspberrypi ?= «4.9%»


состав recipes-berserk

├── bs-net
│   └── bs-net_0.1.3.bb
├── first-run
│   ├── files
│   │   └── first-run.sh
│   └── first-run.bb
├── images
│   └── berserk-image.bb
└── tv
    ├── files
    │   └── berserk.m3u8
    ├── tv-config.bb
    └── tv-dir.inc

где:

  • bs-net_0.1.3.bb — рецепт сборки shell расширения для WLAN/Ethernet интерфейсов
  • first-run.bb — рецепт первого запуска, дополнительное разбиение дисков
  • first-run.sh — shell скрипт первого запуска (запускается на уровне исполнения S)
  • berserk-image.bb — рецепт сборки образа дистрибутива
  • tv-config.bb — рецепт настройки конфигурации ТВ каналов по протоколу IPTV
  • berserk.m3u8 — конфигурация общедоступных телевизионных каналов (формат m3u8)

состав recipes-core

├── init-ifupdown
│   ├── files
│   │   └── interfaces
│   └── init-ifupdown_1.0.bbappend
└── psplash
    ├── files
    │   └── psplash-berserk-img.h
    └── psplash_git.bbappend

где:

  • interfaces — файл с текущей настройкой сети
  • init-ifupdown_1.0.bbappend — расширение для рецепта конфигурации сети
  • psplash-berserk-img.h — изображение стартовой заставки
    заголовочный файл получен с помощью утилиты gdk-pixbuf-csource
  • psplash_git.bbappend — расширение рецепта сборки стартовой заставки

Конфигурация сети на целевом устройстве находиться в файле:
/etc/network/interfaces


Добавив расширение рецепта init-ifupdown, я заменяю штатный конфигурационный файл на собственный, и меняю порядок (приоритет) запуска скрипта для уровней исполнения
INITSCRIPT_PARAMS = "start 98 2 3 4 5 . stop 10 0 6 1 ."


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

Для этого необходимо:

  1. FILESEXTRAPATHS_prepend — добавить каталог для ресурсов
  2. SRC_URI — добавить заголовочный файл с произвольной картинкой
  3. SPLASH_IMAGES — изменить управляющую переменную пакета

и далее в рецепте образа «berserk-image.bb» необходимо добавить в качестве особенностей образа — стартовую заставку splash
IMAGE_FEATURES += "splash"

# и переменную с указанием конкретного названия пакета стартовой заставки
SPLASH = "psplash-berserk"


состав recipes-kernel

└── linux
    ├── files
    │   ├── db.txt.patch
    │   └── rbpi.cfg
    └── linux-raspberrypi_4.9.bbappend

где:

  • db.txt.patch — патч с базой Regulatory Domain (используется для WiFi)
  • rbpi.cfg — фрагмент конфигурации Linux ядра
  • linux-raspberrypi_4.9.bbappend — расширение рецепта сборки ядра 4.9 для Raspberry Pi

Wi-Fi устройства работают на определенных частотах и для них существует такое понятие как управляющий домен (regulatory domain) – именно в этом параметре указана страна, в которой, как предполагается, работает данное устройство.

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

В самом простом случае эту базу можно включить непосредственно в ядро статически, указав параметр:
CONFIG_CFG80211_INTERNAL_REGDB=y
что собственно я и сделал, подключив патч c этой базой db.txt.patch

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

В дополнении рецепта bbappend, также можно изменить параметры, которые передаются ядру во время запуска:

т.е. переопределить переменную
CMDLINE см. файл linux-raspberrypi_4.9.bbappend

содержимое rbpi.cfg
    # use statically compiled regulatory rules database
    CONFIG_CFG80211_INTERNAL_REGDB=y
    # поддержка Wifi сетевого Asus USB-N53 chipset Ralink RT3572
    CONFIG_RT2800USB=m
    # поддержка wifi адаптера на чипсете Atheros D-Link DWA-126 802.11n (AR9271), 
    # NetGear WNDA3200, NetGear WNA1100, TP-Link TL-WN722N (AR9271), 
    # TL-WN322G v3, TL-WN422G и т.д. см. cateee.net
    CONFIG_ATH9K_HW=m
    CONFIG_ATH9K_HTC=m
    # настройка Wifi драйверов для работы wpa_supplicant
    CONFIG_WIRELESS=y
    CONFIG_WEXT_CORE=y
    CONFIG_WEXT_PROC=y
    CONFIG_CRYPTO_AES=y
    # поддержка семейства протоколов IPSec, без этих модулей Wifi авторизация 
    # при подключении wpa_supplicant не работает
    CONFIG_CRYPTO_CCM=m
    CONFIG_CRYPTO_CTR=m
    CONFIG_CRYPTO_ARC4=m
    #########################
    # оценка производительности
    CONFIG_HAVE_PERF_EVENTS=y
    CONFIG_PERF_EVENTS=y
    CONFIG_HAVE_LATENCYTOP_SUPPORT=y
    CONFIG_LATENCYTOP=y
    # This option adds support for ASIX AX88xxx 
    # based USB 2.0 10/100 Ethernet adapters.
    CONFIG_USB_NET_AX8817X=m
    


содержимое linux-raspberrypi_4.9.bbappend
    # дополнительный параметры конфигурации описываются в rpbi.cfg
    FILESEXTRAPATHS_prepend := "${THISDIR}/files:"

    SRC_URI += "file://db.txt.patch;patch=1 \
                file://rbpi.cfg \
                "

    # в BSP слое meta-raspberrypi не работают фрагменты конфигураций
    # https://github.com/agherzan/meta-raspberrypi/issues/14
    # поэтому делаю напрямую
    # в методе do_kernel_configme конфигурация ядра 
    # копируется из базы архитектур arch/ в рабочий каталог
    do_kernel_configme_append() {
        cat ${WORKDIR}/rbpi.cfg >> ${WORKDIR}/defconfig
    }

    # CMDLINE for raspberrypi
    # default CMDLINE = "dwc_otg.lpm_enable=0 console=serial0,115200
    # root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"
    CMDLINE = "quiet dwc_otg.lpm_enable=0 console=serial0,115200 \
               root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"
    


состав recipes-mediacentre

├── kodi
│   ├── kodi
│   ├── kodi_17.bbappend
│   ├── kodi-dir.inc
│   ├── kodi-runner.bb
│   ├── kodi-settings.bb
│   └── kodi-version.inc
└── kodi-plugins
    ├── files
    ├── kodi-language-ru_3.0.10.bb
    ├── kodi-pvr-iptvsimple.bb
    ├── plugin-video-youtube_5.5.1.bb
    ├── screensaver-kodi-universe_0.1.2.bb
    ├── script-berserk-network_0.2.5.bb
    └── script-module-requests_2.12.4.bb


где:
  1. kodi/
    • kodi — каталог содержит подкаталоги icon,run,settings
      и патчи для исходного кода и конфигурации запуска мультимедиа центра
    • kodi_17.bbappend — дополнение для рецепта сборки мультимедиа центра Kodi
    • kodi-dir.inc — общие пути расположения Kodi и его плагинов
    • kodi-runner.bb — рецепт запуска Kodi на определенном уровне исполнения
    • kodi-settings.bb — рецепт размещения конфигурационных файлов Kodi
    • kodi-version.inc — общий файл с информацией о собираемой версии Kodi

  2. kodi-plugins/
    • files — каталог с tar.gz архивами с исходным кодом плагинов
    • kodi-language-ru_3.0.10.bb — рецепт добавления русской локализации (перевод меню Kodi)
    • kodi-pvr-iptvsimple.bb — рецепт сборки Kodi плагина pvr-iptvsimple
    • plugin-video-youtube_5.5.1.bb — рецепт сборки Kodi плагина для просмотра «Youtube»
    • screensaver-kodi-universe_0.1.2.bb — рецепт сборки плагина screensaver-kodi-universe
    • script-berserk-network_0.2.5.bb — рецепт сборки плагина настройки сети
    • script-module-requests_2.12.4.bb — рецепт сборки зависимости для плагина Youtube


состав recipes-multimedia

└── ffmpeg
    ├── ffmpeg
    │   ├── 0001-ffmpeg-Call-get_format-to-fix-an-issue-with-MMAL-ren.patch
    │   ├── h264_parser.patch
    │   └── pfcd_hevc_optimisations.patch
    └── ffmpeg_3.1.11.bb

где:

  • 0001-ffmpeg-Call-get_format-to-fix-an-issue-with-MMAL-ren.patch — патч для ffmpeg
  • h264_parser.patch — патч для коррекции парсера h264
  • pfcd_hevc_optimisations.patch — основной патч с оптимизацией для Raspberry Pi
  • ffmpeg_3.1.11.bb — рецепт сборки библиотеки ffmpeg (аудио, видео кодеки)

FFmpeg — OpenSource библиотека для кодирования / декодирования огромного количества видео и аудио форматов. FFmpeg поддерживает почти 400 кодеков (ffmpeg -codecs)
и более 300 форматов (ffmpeg -formats).

Дополнение рецепта для сборки «Kodi»


В слое OpenEmbedded находиться штатный рецепт для сборки Kodi, но он достаточно общий, а мне хотелось бы получить немного более стабильную и проверенную версию для платформы Raspberry Pi.

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

В мире OpenSource сообщества есть несколько хорошо известных проектов, использующих Kodi, лучшим из которых (на мой взляд) является LibreElec (OpenElec). У LibreElec есть хорошая сборка для платформы Raspberry Pi. Вот у них то и лучше всего взять патч обратного портирования для Kodi. Тем самым, можно избавиться от огромного количества проблем, даже не узнав об этом.

Мультимедиа центр Kodi заточен на проигрывание «Медиа» контекста, и на мой взгляд самым критичным компонентом является связка Kodi и FFmpeg, т.е. взаимодействие определенных версий этих программ, остальные библиотеки можно оставить из слоев Yocto и OpenEmbedded.

Для сборки я взял стабильную версия Kodi 17.6 и версию FFmpeg 3.1.11

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

    Для того, чтобы взять патчи из LibreElec вы можете
    загрузить нужную версию примерно так:

        #!/bin/bash

        HASH_VER="934507d922fb011ce46c76566206f2f1f603360b"
        git clone https://github.com/LibreELEC/LibreELEC.tv.git libreelec
        cd libreelec
        git checkout ${HASH_VER}


    патчи для Kodi, располагаются в каталоге:
    projects/RPi2/patches/kodi

    (см. файл: kodi-001-backport.patch)

    патчи для библиотеки FFmpeg, можно посмотреть здесь:
    packages/multimedia/ffmpeg/patches
    


Включаемый файл с описанием версии будет такой kodi-version.inc
FILESEXTRAPATHS_prepend := "${THISDIR}/kodi:"

# ветка Krypton
SRCREV = "a9a7a20071bfd759e72e7053cee92e6f5cfb5e48"
PV = "17.6+gitr${SRCPV}"


Ветка Yocto и OpenEmbedded, которую я рассматриваю — rocko, содержит версию Kodi 17.3, и для того чтобы перейти к версии 17.6, достаточно добавить небольшое дополнение для рецепта — kodi_17.bbappend
require kodi-version.inc

# убираю не работающий патч (был для версии 17.3)
SRC_URI_remove = "file://0013-FTPParse.cpp-use-std-string.patch"

# отключаю патч, так как systemd не использую
SRC_URI_remove = "file://0004-handle-SIGTERM.patch"

# добавляю патч обратного портирования для RPI взятого из libreelec
SRC_URI_append += "file://kodi-krypton-rpb-backports.patch"

# исправление error adding symbols: DSO missing from command line
SRC_URI_append += "file://vchostif.patch"


MENU_ICON = "addons/skin.estuary/media/icons/settings"
# добавление нового пункта в меню настроек (значок шестеренки)
SRC_URI_append += "file://bs-menu.patch file://icon/bs-network.png"
do_configure_prepend() {
    install -m 0644 ${WORKDIR}/icon/bs-network.png ${S}/${MENU_ICON}
}

# дополнительные зависимости для kodi plugins
RRECOMMENDS_${PN}_append = "\
                  python-xml python-misc python-db \
                  python-crypt python-threading python-math python-email \
                  python-io python-netserver python-urllib3 python-datetime"


# специфическии опции для плат Raspberry Pi
# реализация OPENGL обязательно должна быть --enable-gles
# для kodi зависимости сборки указаны в docs/README.linux => libxmu libxinerama 
#                                                            libxtst xdpyinfo
# для сборки этих библиотек в DISTRO_FEATURES необходима зависимость "x11"
# но сам kodi для RPI1 и RPI2,3 собирается с опцией --disable-x11

BS_RPI = " --disable-gl --enable-openmax --enable-player=omxplayer \
           --with-platform=raspberry-pi --disable-x11"

BS_RPI3 = " --disable-gl --enable-openmax --enable-player=omxplayer \
            --with-platform=raspberry-pi2 --disable-x11"

EXTRA_OECONF_append = "${@bb.utils.contains('MACHINE', 'raspberrypi', \
                                                       '${BS_RPI}', '', d)}"
                       
EXTRA_OECONF_append = "${@bb.utils.contains('MACHINE', 'raspberrypi2', \
                                                       '${BS_RPI3}', '', d)}"

EXTRA_OECONF_append = "${@bb.utils.contains('MACHINE', 'raspberrypi3', \
                                                       '${BS_RPI3}', '', d)}"

# опция для появления всплывающего сообщения в Kodi о подключении 
# внешнего носителя, например USB или microSDHC диска (через картридер)
EXTRA_OECONF_append = " --enable-optical-drive"


Опция сборки "--enable-optical-drive" позволяет подключить удобный механизм оповещения, который используется в Kodi при подключении оптического диска. В этом случае модуль MediaManager(а) (xbmc/storage/MediaManager.cpp) отслеживает подключение/отключение новых дисковых разделов, и выводит на экран всплывающее сообщение об этом.

пример udev правила подключения/отключения дисков:

ACTION=="add" SUBSYSTEM=="block"  ENV{ID_FS_TYPE}=="vfat" \
              KERNEL=="sd[a-z][0-9]" \
              RUN+="/bin/mkdir -p /media/%k", \
              RUN+="/bin/mount -o iocharset=utf8,noatime /dev/%k /media/%k"


ACTION=="add" SUBSYSTEM=="block"  ENV{ID_FS_TYPE}=="ntfs" \
              KERNEL=="sd[a-z][0-9]" \
              RUN+="/bin/mkdir -p /media/%k", \
              RUN+="/usr/bin/ntfs-3g -o \
                    iocharset=utf8,noatime,windows_names /dev/%k /media/%k"

                             
ACTION=="add" SUBSYSTEM=="block"  ENV{ID_FS_TYPE}=="ext2|ext3|ext4" \
              KERNEL=="sd[a-z][0-9]" \
              RUN+="/bin/mkdir -p /media/%k", \
              RUN+="/bin/mount -o noatime /dev/%k /media/%k"


ACTION=="remove" SUBSYSTEM=="block" KERNEL=="sd[a-z][0-9]" \
                 RUN+="/bin/umount /media/%k", RUN+="/bin/rmdir /media/%k"

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


Добавление нового пункта в меню настроек Kodi


В Kodi 17.6, за отображение пунктов меню отвечают конфигурационные xml файлы. Для добавления еще одного под пункта в меню «Settings», достаточно подкорректировать файл:
kodi/addons/skin.estuary/xml/Settings.xml

где skin.estuary — тема оформления меню по умолчанию

описание пункта выглядит так:

<item>
    <label>$LOCALIZE[13279]</label>
    <onclick>RunAddon(script.berserk.network,butnetwork)</onclick>
    <icon>icons/settings/bs-network.png</icon>
</item>

где:

label — название пункта меню
onclick — обработка событие нажатия на кнопку меню
(запуск плагина, с передачей первым аргументом строки «butnetwork»)
icon — иконка пункта меню (путь к png изображению)

Указанная выше функциональность, а также подключение нескольких дополнительных Kodi плагинов интегрирована с помощью файла bs-menu.patch

Показать/Скрыть
    diff -Naur a/addons/skin.estuary/xml/Settings.xml b/addons/skin.estuary/xml/Settings.xml
    --- a/addons/skin.estuary/xml/Settings.xml  2018-02-01 18:17:45.000000000 +0300
    +++ b/addons/skin.estuary/xml/Settings.xml  2018-03-08 12:06:50.000000000 +0300
    @@ -134,6 +134,11 @@
                <icon>icons/settings/interface.png</icon>
            </item>
            <item>
    +                   <label>$LOCALIZE[13279]</label>
    +                   <onclick>RunAddon(script.berserk.network,butnetwork)</onclick>
    +                   <icon>icons/settings/bs-network.png</icon>
    +           </item>
    +           <item>
                <label>$LOCALIZE[20077]</label>
                <onclick>ActivateWindow(SkinSettings)</onclick>
                <icon>icons/settings/skin.png</icon>
    diff -Naur a/system/addon-manifest.xml b/system/addon-manifest.xml
    --- a/system/addon-manifest.xml     2018-03-07 15:58:24.000000000 +0300
    +++ b/system/addon-manifest.xml     2018-05-14 14:06:58.000000000 +0300
    @@ -27,6 +27,7 @@
    <addon>resource.uisounds.kodi</addon>
    <addon>screensaver.xbmc.builtin.black</addon>
    <addon>screensaver.xbmc.builtin.dim</addon>
    +  <addon>screensaver.kodi.universe</addon>
    <addon>script.module.pil</addon>
    <addon>service.xbmc.versioncheck</addon>
    <addon>skin.estuary</addon>
    @@ -43,4 +44,8 @@
    <addon>xbmc.python</addon>
    <addon>xbmc.webinterface</addon>
    <addon optional="true">peripheral.joystick</addon>
    +  <addon>script.berserk.network</addon>
    +  <addon>resource.language.ru_ru</addon>
    +  <addon>script.module.requests</addon>
    +  <addon>plugin.video.youtube</addon>
    </addons>
    


Максимальные настройки буферизации для видео


В мультимедиа центре Kodi для увеличения производительности можно указать максимальные настройки буферизации:
<advancedsettings>
  <cache>
    <buffermode>1</buffermode>
    <memorysize>139460608</memorysize>
    <readfactor>20</readfactor>
  </cache>
</advancedsettings>


buffermode=1
— буферизировать запросы для всех файловых систем (включая локальную)

readfactor
— регулирует скорость загрузки на основе среднего битрейта видео. Так, например, если вы воспроизводите видео со средней скоростью передачи данных 5 Мбит/с и устанавливаете коэффициент чтения буфера на 2.0, это ограничит скорость загрузки (и, следовательно, скорость заполнения кеша) примерно до 10 Мбит/с, таким образом:

readfactor=20
снимает ограничение на скорость загрузки

memorysize =139460608
— размер буфера составляет 133 МБайт, при этом используется 133*3 оперативной памяти т.е. около 400 Мбайт оперативной памяти

Просмотр телевидения по протоколу IPTV


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

Основной функцией, для которой я собирал «Медиацентр» является функция просмотра цифрового телевидения по протоколу IPTV (Internet Protocol Television) т.е. телевидение по протоколу интернета. С помощью данной функции вы можете просматривать цифровое телевидение от вашего интернет провайдера.

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

Для поддержки IPTV в Kodi существует несколько вариантов плагинов, из которых я остановился на плагине pvr.iptvsimple

Для сборки плагина используется рецепт:

    └── kodi-plugins
        └── kodi-pvr-iptvsimple.bb

Плагин подключается/настраивается через:
Основное меню Kodi => «Дополнения» => «Мои дополнения» => «Клиенты PVR» => «PVR IPTV Simple Client»

Для проверки работы IPTV телевидения в составе Kodi, я взял несколько общедоступных новостных информационных каналов и добавил их в список в формате m3u8, а также включил автоматический запуск плагина «pvr.iptvsimple» при старте медиацентра.

Просмотр Youtube с помощью Kodi плагина


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

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

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

Плагин написан на языке python, и это штатный механизм плагинов, вам не нужно ничего компилировать, достаточно просто скопировать готовый плагин в корневой каталог с плагинами, и указать имя плагина в xml файле манифеста:
«system/addon-manifest.xml»

Плагин взят из официального репозитория, и его исходный код включен в архив:
recipes-mediacentre/kodi-plugins/files/plugin.video.youtube.tar.gz

Расположение рецепта сборки плагина см. выше в главе «состав recipes-mediacentre»

Консольное shell расширение конфигурации сети


Так как дистрибутив собираемый в рамках данной статьи является демонстрационным, то и требования к настройки «Сетевых интерфейсов» к нему минимальны. Мне не хотелось тащить для этого, какой либо тяжелый сетевой менеджер, сильно мне не понятный и сильно громоздкий, и поэтому я написал два shell скрипта, дополняющих настройку штатного механизма конфигурации:
##############################################################
# пример возможного использования в /etc/network/interfaces:
##############################################################
auto eth0
     iface eth0 inet manual
     up /etc/network/eth-manual $IFACE up
     down /etc/network/eth-manual $IFACE down

auto wlan0
     iface wlan0 inet manual
     up /etc/network/wlan $IFACE up
     down /etc/network/wlan $IFACE down


Для удобной настройки Ethernet/WLAN сетевых интерфейсов через GUI, у меня используется еще один небольшой Kodi плагин «script.berserk.network». Это почти единственный Kodi plugin, в котором я разобрался, но для этого мне пришлось его написать. Он предельно компактный и минималистичный и написан на языке «python».

Оба этих компонента, собираются с помощью рецептов:

  • recipes-berserk/bs-net/bs-net_0.1.3.bb
  • recipes-mediacentre/kodi-plugins/script-berserk-network_0.2.5.bb

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

Для автоматического подключения к WiFi точке доступа при старте системы, у меня используется udev правило: /etc/udev/rules.d/80-wifi-start.rules

SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", KERNEL=="wlan*", \
                  RUN+="/etc/network/wlan-runner $env{INTERFACE} up"

SUBSYSTEM=="net", ACTION=="remove", DRIVERS=="?*", KERNEL=="wlan*", \
                  RUN+="/etc/network/wlan-runner $env{INTERFACE} down"


Скрипт wlan-runner просто выполняет команды:
/etc/network/wlan $IFACE up
или
/etc/network/wlan $IFACE down

Рецепт сборки дистрибутива


В Yocto Project предусмотрен механизм повторного использования. Есть классы, от которых вы можете наследоваться (директива «inherit»), и есть базовые рецепты, которые вы можете подключить (директива «include»).

Покажу наследование на примере класса:
poky/meta/clasess/core-image.bbclass

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

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

Особенности образа указываются так:

IMAGE_FEATURES += "ssh-server-dropbear splash"


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

Основной базовый рецепт, который я использую:
poky/meta/recipes-core/images/core-image-minimal.bb

рецепт сборки образа
    # Project: "Berserk" - build Kodi for the Raspberry Pi platform
    # license - The MIT License (MIT)

    DESCRIPTION = "Berserk - the image for the Raspberry PI"
    LICENSE = "MIT"
    MD5_SUM = "md5=0835ade698e0bcf8506ecda2f7b4f302"
    LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;${MD5_SUM}"

    IMAGE_FEATURES += "ssh-server-dropbear splash"

    # немного увеличиваю размер rootfs в кило байтах (250000kB=~250Mb)
    IMAGE_ROOTFS_EXTRA_SPACE_append += "+ 250000"


    # Base this image on core-image-minimal
    include recipes-core/images/core-image-minimal.bb

    # Set default password for 'root' user
    inherit extrausers
    ROOTUSERNAME = "root"
    ROOTPASSWORD = "berserk"
    EXTRA_USERS_PARAMS = "usermod -P ${ROOTPASSWORD} ${ROOTUSERNAME};"


    # стартовая заставка, которая выводиться во время загрузки
    SPLASH = "psplash-berserk"

    BS_DEBUG_TOOLS = "ldd strace ltrace"

    BS_GLIBC = "glibc-thread-db \
                glibc-gconv-utf-16 \
                glibc-gconv-utf-32 \
                glibc-binary-localedata-en-us \
                glibc-binary-localedata-ru-ru \
                glibc-charmap-utf-8 \
                "

    BS_BASE = "kernel-modules \
            lsb \
            pciutils \
            parted \
            tzdata \
            dosfstools \
            ntp \
            ntpdate \
            e2fsprogs-resize2fs \
            ntfs-3g \
            ntfsprogs \
            "

    BS_WLAN = "kernel-module-rt2800usb \
            kernel-module-rt2800lib \
            kernel-module-rt2x00lib \
            kernel-module-rt2x00usb \
            kernel-module-cfg80211 \
            kernel-module-nls-utf8 \
            kernel-module-ath9k-common \
            kernel-module-ath9k-hw \
            kernel-module-ath9k-htc \
            kernel-module-ctr \
            kernel-module-ccm \
            kernel-module-arc4 \
            "

    BS_WIFI_SUPPORT = " \
            iw \
            dhcp-client \
            wireless-tools \
            wpa-supplicant \
            linux-firmware \
            "

    BS_SOFT = "mc \
            kodi \
            kodi-runner \
            kodi-settings \
            kodi-language-ru \
            kodi-pvr-iptvsimple \
            bs-net \
            tv-config \
            first-run \
            script-berserk-network \
            screensaver-kodi-universe \
            plugin-video-youtube \
            script-module-requests \
            "



    # Include modules in rootfs
    IMAGE_INSTALL += " \
        ${BS_BASE} \
        ${BS_WLAN} \
        ${BS_WIFI_SUPPORT} \
        ${BS_GLIBC} \
        ${BS_SOFT} \
        ${BS_DEBUG_TOOLS} \
        "
    

Хотел бы уточнить, что например пакет «kernel-modules», установит
все модули ядра указанные в файле defconfig в образ дистрибутива.

Но если вы что то сильно кастомизируете, то конечно все модули ядра вам могут и не понадобиться, в таком случае удобно добавлять каждый модуль по имени, как указано в переменной BS_WLAN, это как шпаргалка, указываем только то, что нужно и после проверки работы пакет «kernel-modules» убираем, проверяем и т.д.

Краткая инструкция по созданию образа дистрибутива


1) Установите зависимости Yocto Project в Ubuntu:
    sudo apt-get install -y --no-install-suggests --no-install-recommends \
        gawk wget git-core diffstat unzip texinfo gcc-multilib build-essential \
        chrpath socat cpio python python3 python3-pip python3-pexpect \
        xz-utils debianutils iputils-ping python3-git python3-jinja2 \
        libegl1-mesa libsdl1.2-dev xterm
    


2) Скачайте и установите Repo:
    mkdir ~/bin
    curl http://commondatastorage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
    chmod a+x ~/bin/repo
    


3) Загрузите проект с github:
    PATH=${PATH}:~/bin
    mkdir torvin
    cd torvin
    repo init -u https://github.com/berserktv/bs-manifest \
              -m raspberry/rocko/torvin-0.2.5.xml
    repo sync   
    


4) Соберите проект:
    ./shell.sh
    bitbake berserk-image
    


5) Запишите образ дистрибутива на карту памяти:
Собранный образ после сборки будет располагаться по пути
torvin/build/tmp/deploy/images/raspberrypi3

На последний успешно собранный образ
всегда будет указывать символическая ссылка:
berserk-image-raspberrypi3.rpi-sdimg

cам файл будет содержать достаточно длинное имя
c датой создания и временем в UTC

Пример записи образа дистрибутива с помощью команды dd

ПРЕДУПРЕЖДЕНИЕ:
все предыдущие данные на карте памяти «microSDHC»
после выполнения операции записи будут удалены.

$ sudo bash
$ cd torvin/build/tmp/deploy/images/raspberrypi3
$ dd if=berserk-image-raspberrypi3.rpi-sdimg of=/dev/sdX bs=1M
$ sync

где /dev/sdX:
X может быть a,b,c и т.д.
в зависимости от предыдущих подключенных дисковых разделов

уже собранный образ можно загрузить с моего домашнего сайта:
Для записи образа под Windows, вы можете воспользоваться
программой Win32 Disk Imager находиться здесь:
более подробную инструкцию можно посмотреть здесь:



Примечание:
    Время сборки образа дистрибутива достаточно длительное
    и может занимать от трех до N часов в зависимости от производительности
    компьютера, также в процессе сборки из "Интернета" должны быть
    загружены исходные коды всех программ входящих в дистрибутив,
    часто это полные git базы

    (т.е. время сборки также зависит от скорости подключения сети "Интернет")

    например на машине: Процессор       - Intel(R) Core(TM) i5-3570 CPU @ 3.40GHz
                        ОЗУ             - 8 Гбайт
                        Жесткий диск    - внешний USB-3.0  1Tбайт

                        Время сборки    - 4 часа 05 минут
                        Размер образа   - 1 Гбайт 

                        размер в архиве - 274.8 Mбайт 

    Размер каталога torvin/build после завершения сборки
    (cache сборки, исходный код программ, промежуточные файлы сборки, объектные файлы,
    дополнительные мета данные, файлы пакетов и т.п.)
                                        - Занимает примерно 42 ГБайт

    Размер каталога torvin/downloads    - Занимают 9.1 Гбайт
    (git базы приложений и tar.gz архивы)

    Размер rootfs раздела образа        - 550 Мбайт
        из них каталог /lib/firmware    - 212 Мбайт
                        /lib/modules    - 53  Мбайт

    примечание: вы можете ставить не все модули и firmware (а только нужные вам)
                тем самым можно значительно уменьшить размер дистрибутива,
                минимум на 200 Мб
    


Постскриптум


Возможности OpenSource за последние годы только увеличиваются.

А возможности эти не малые, за примерами даже не надо далеко ходить. Вряд ли та же «Microsoft» ожидала, что OpenSource технологии выбросят ее с рынка мобильных операционных систем. Я имею ввиду ОС от Google — «Android», которая в одночасье выбросила «Пионера» мобильных систем на обочину. И не понятно, сможет ли Microsoft, снова на него вернуться.

Конечно «Google», огромная корпорация с почти не ограниченными финансами и великолепными разработчиками, но все-же, как говориться «без Ядра и не туда и не сюда».

Лучшие OpenSource проекты со временем становятся произведением искусства (например Kodi, Openelec/libre и т.д.)

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

Побольше вам сборок хороших и разных, и помните «мир интернета вещей наступает».

Let's block ads! (Why?)

Хабрастатистика: исследуем лучшие хабы

Привет Хабр.

В предыдущей части была проанализирована посещаемость Хабра по основным параметрам — количеству статей, их просмотрам и рейтингам. Однако вопрос популярности разделов сайта остался не рассмотренным. Стало интересно рассмотреть это более подробно, и найти самые популярные и самые непопулярные хабы. Наконец, я рассмотрю «geektimes-эффект» более подробно, и в завершении читатели получат новую подборку лучших статей по новым рейтингам.

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

Сбор данных


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

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

2018-12-18T12:43Z,https://habr.com/ru/post/433550/,"Мессенджер Slack — причины выбора, косяки при внедрении и особенности сервиса, облегчающие жизнь",votes:7,votesplus:8,votesmin:1,bookmarks:32,
views:8300,comments:10,user:ReDisque,karma:5,subscribers:2,hubs:productpm+soft
...

Получим список основных тематических хабов сайта.

def get_as_str(link: str) -> Str:
    try:
        r = requests.get(link)
        return Str(r.text)
    except Exception as e:
        return Str("")

def get_hubs():
    hubs = []
    for p in range(1, 12):
        page_html = get_as_str("https://habr.com/ru/hubs/page%d/" % p)
        # page_html = get_as_str("https://habr.com/ru/hubs/geektimes/page%d/" % p)  # Geektimes
        # page_html = get_as_str("https://habr.com/ru/hubs/develop/page%d/" % p)  # Develop
        # page_html = get_as_str("https://habr.com/ru/hubs/admin/page%d" % p)  # Admin
        for hub in page_html.split("media-obj media-obj_hub"):
            info = Str(hub).find_between('"https://habr.com/ru/hub', 'list-snippet__tags') 
            if "*</span>" in info:
                hub_name = info.find_between('/', '/"')
                if len(hub_name) > 0 and len(hub_name) < 32:
                    hubs.append(hub_name)
    print(hubs)

Функция find_between и класс Str выделяют строку между двух тегов, я использовал их ранее. Тематические хабы отмечены "*", так что их легко выделить, можно также раскомментировать соответствующие строки, чтобы получить разделы других категорий.

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

hubs_profile = {'infosecurity', 'programming', 'webdev', 'python', 'sys_admin', 'it-infrastructure', 'devops', 'javascript', 'open_source', 'network_technologies', 'gamedev', 'cpp', 'machine_learning', 'pm', 'hr_management', 'linux', 'analysis_design', 'ui', 'net', 'hi', 'maths', 'mobile_dev', 'productpm', 'win_dev', 'it_testing', 'dev_management', 'algorithms', 'go', 'php', 'csharp', 'nix', 'data_visualization', 'web_testing', 's_admin', 'crazydev', 'data_mining', 'bigdata', 'c', 'java', 'usability', 'instant_messaging', 'gtd', 'system_programming', 'ios_dev', 'oop', 'nginx', 'kubernetes', 'sql', '3d_graphics', 'css', 'geo', 'image_processing', 'controllers', 'game_design', 'html5', 'community_management', 'electronics', 'android_dev', 'crypto', 'netdev', 'cisconetworks', 'db_admins', 'funcprog', 'wireless', 'dwh', 'linux_dev', 'assembler', 'reactjs', 'sales', 'microservices', 'search_technologies', 'compilers', 'virtualization', 'client_side_optimization', 'distributed_systems', 'api', 'media_management', 'complete_code', 'typescript', 'postgresql', 'rust', 'agile', 'refactoring', 'parallel_programming', 'mssql', 'game_promotion', 'robo_dev', 'reverse-engineering', 'web_analytics', 'unity', 'symfony', 'build_automation', 'swift', 'raspberrypi', 'web_design', 'kotlin', 'debug', 'pay_system', 'apps_design', 'git', 'shells', 'laravel', 'mobile_testing', 'openstreetmap', 'lua', 'vs', 'yii', 'sport_programming', 'service_desk', 'itstandarts', 'nodejs', 'data_warehouse', 'ctf', 'erp', 'video', 'mobileanalytics', 'ipv6', 'virus', 'crm', 'backup', 'mesh_networking', 'cad_cam', 'patents', 'cloud_computing', 'growthhacking', 'iot_dev', 'server_side_optimization', 'latex', 'natural_language_processing', 'scala', 'unreal_engine', 'mongodb', 'delphi',  'industrial_control_system', 'r', 'fpga', 'oracle', 'arduino', 'magento', 'ruby', 'nosql', 'flutter', 'xml', 'apache', 'sveltejs', 'devmail', 'ecommerce_development', 'opendata', 'Hadoop', 'yandex_api', 'game_monetization', 'ror', 'graph_design', 'scada', 'mobile_monetization', 'sqlite', 'accessibility', 'saas', 'helpdesk', 'matlab', 'julia', 'aws', 'data_recovery', 'erlang', 'angular', 'osx_dev', 'dns', 'dart', 'vector_graphics', 'asp', 'domains', 'cvs', 'asterisk', 'iis', 'it_monetization', 'localization', 'objectivec', 'IPFS', 'jquery', 'lisp', 'arvrdev', 'powershell', 'd', 'conversion', 'animation', 'webgl', 'wordpress', 'elm', 'qt_software', 'google_api', 'groovy_grails', 'Sailfish_dev', 'Atlassian', 'desktop_environment', 'game_testing', 'mysql', 'ecm', 'cms', 'Xamarin', 'haskell', 'prototyping', 'sw', 'django', 'gradle', 'billing', 'tdd', 'openshift', 'canvas', 'map_api', 'vuejs', 'data_compression', 'tizen_dev', 'iptv', 'mono', 'labview', 'perl', 'AJAX', 'ms_access', 'gpgpu', 'infolust', 'microformats', 'facebook_api', 'vba', 'twitter_api', 'twisted', 'phalcon', 'joomla', 'action_script', 'flex', 'gtk', 'meteorjs', 'iconoskaz', 'cobol', 'cocoa', 'fortran', 'uml', 'codeigniter', 'prolog', 'mercurial', 'drupal', 'wp_dev', 'smallbasic', 'webassembly', 'cubrid', 'fido', 'bada_dev', 'cgi', 'extjs', 'zend_framework', 'typography', 'UEFI', 'geo_systems', 'vim', 'creative_commons', 'modx', 'derbyjs', 'xcode', 'greasemonkey', 'i2p', 'flash_platform', 'coffeescript', 'fsharp', 'clojure', 'puppet', 'forth', 'processing_lang', 'firebird', 'javame_dev', 'cakephp', 'google_cloud_vision_api', 'kohanaphp', 'elixirphoenix', 'eclipse', 'xslt', 'smalltalk', 'googlecloud', 'gae', 'mootools', 'emacs', 'flask', 'gwt', 'web_monetization', 'circuit-design', 'office365dev', 'haxe', 'doctrine', 'typo3', 'regex', 'solidity', 'brainfuck', 'sphinx', 'san', 'vk_api', 'ecommerce'}


Для сравнения, разделы geektimes выглядят скромнее:
hubs_gt = {'popular_science', 'history', 'soft', 'lifehacks', 'health', 'finance', 'artificial_intelligence', 'itcompanies', 'DIY', 'energy', 'transport', 'gadgets', 'social_networks', 'space', 'futurenow', 'it_bigraphy', 'antikvariat', 'games', 'hardware', 'learning_languages', 'urban', 'brain', 'internet_of_things', 'easyelectronics', 'cellular', 'physics', 'cryptocurrency', 'interviews', 'biotech', 'network_hardware', 'autogadgets', 'lasers', 'sound', 'home_automation', 'smartphones', 'statistics', 'robot', 'cpu', 'video_tech', 'Ecology', 'presentation', 'desktops', 'wearable_electronics', 'quantum', 'notebooks', 'cyberpunk', 'Peripheral', 'demoscene', 'copyright', 'astronomy', 'arvr', 'medgadgets', '3d-printers', 'Chemistry', 'storages', 'sci-fi', 'logic_games', 'office', 'tablets', 'displays', 'video_conferencing', 'videocards', 'photo', 'multicopters', 'supercomputers', 'telemedicine', 'cybersport', 'nano', 'crowdsourcing', 'infographics'}


Аналогично были сохранены остальные хабы. Теперь несложно написать функцию, которая возвращает результат, относится статья к geektimes или к профильному хабу.
def is_geektimes(hubs: List) -> bool:
    return len(set(hubs) & hubs_gt) > 0

def is_geektimes_only(hubs: List) -> bool:
    return is_geektimes(hubs) is True and is_profile(hubs) is False

def is_profile(hubs: List) -> bool:
    return len(set(hubs) & hubs_profile) > 0


Аналогичные функции были сделаны для других разделов («разработка», «администрирование» и пр).

Обработка


Пора приступать к анализу. Загружаем датасет и обрабатываем данные хабов.
def to_list(s: str) -> List[str]:
    # "user:popular_science+astronomy" => [popular_science, astronomy]
    return s.split(':')[1].split('+')

def to_date(dt: datetime) -> datetime.date:
    return dt.date()

df = pd.read_csv("habr_2019.csv", sep=',', encoding='utf-8', error_bad_lines=True, quotechar='"', comment='#')
dates = pd.to_datetime(df['datetime'], format='%Y-%m-%dT%H:%MZ')
dates += datetime.timedelta(hours=3)
df['date'] = dates.map(to_date, na_action=None)
hubs = df["hubs"].map(to_list, na_action=None)
df['hubs'] = hubs
df['is_profile'] = hubs.map(is_profile, na_action=None)
df['is_geektimes'] = hubs.map(is_geektimes, na_action=None)
df['is_geektimes_only'] = hubs.map(is_geektimes_only, na_action=None)
df['is_admin'] = hubs.map(is_admin, na_action=None)
df['is_develop'] = hubs.map(is_develop, na_action=None)


Теперь мы можем сгруппировать данные по дням и вывести число публикаций по разным хабам.
g = df.groupby(['date'])
days_count = g.size().reset_index(name='counts')
year_days = days_count['date'].values
grouped = g.sum().reset_index()
profile_per_day_avg = grouped['is_profile'].rolling(window=20, min_periods=1).mean()
geektimes_per_day_avg = grouped['is_geektimes'].rolling(window=20, min_periods=1).mean()
geektimesonly_per_day_avg = grouped['is_geektimes_only'].rolling(window=20, min_periods=1).mean()
admin_per_day_avg = grouped['is_admin'].rolling(window=20, min_periods=1).mean()
develop_per_day_avg = grouped['is_develop'].rolling(window=20, min_periods=1).mean()


Выводим количество опубликованных статей с помощью Matplotlib:

Я разделил в графике статьи «geektimes» и «geektimes only», т.к. статья может принадлежать к обеим разделам одновременно (например «DIY» + «микроконтроллеры» + «С++»). Обозначением «profile» я выделил профильные статьи сайта, хотя возможно, английский термин profile для этого не совсем верный.

В предыдущей части спрашивали про «geektimes-эффект». Выведем отдельно статьи geektimes:

df_gt = df[(df['is_geektimes_only'] == True)]
group_gt = df_gt.groupby(['date'])
days_count_gt = group_gt.size().reset_index(name='counts')
grouped = group_gt.sum().reset_index()
year_days_gt = days_count_gt['date'].values
view_gt_per_day_avg = grouped['views'].rolling(window=20, min_periods=1).mean()


Результат интересный. Примерное соотношение просмотров статей geektimes к общему где-то 1:5. Но если общее число просмотров заметно колебалось, то просмотр «развлекательных» статей держался примерно на одном уровне.

Также можно заметить, что общее число просмотров статей раздела «geektimes» после изменения правил все же упало, но «на глаз», не больше чем на 5% от общих значений.

Интересно посмотреть среднее число просмотров на статью:

Для «развлекательных» статей оно примерно на 40% выше среднего. Наверно это неудивительно. Провал в начале апреля мне непонятен, может так и было, или это какая-то ошибка парсинга, а может кто-то из авторов geektimes ушел в отпуск ;).

Хабы


Перейдем к обещанному анализу хабов. Выведем топ 20 хабов по числу просмотров:
hubs_info = []
for hub_name in hubs_all:
    mask = df['hubs'].apply(lambda x: hub_name in x)
    df_hub = df[mask]

    count, views = df_hub.shape[0], df_hub['views'].sum()
    hubs_info.append((hub_name, count, views))

# Draw hubs
hubs_top = sorted(hubs_info, key=lambda v: v[2], reverse=True)[:20]
top_views = list(map(lambda x: x[2], hubs_top))
top_names = list(map(lambda x: x[0], hubs_top))

plt.rcParams["figure.figsize"] = (8, 6)
plt.bar(range(0, len(top_views)), top_views)
plt.xticks(range(0, len(top_names)), top_names, rotation=90)
plt.ticklabel_format(style='plain', axis='y')
plt.tight_layout()
plt.show()

Результат:

На удивление, самым популярным по просмотрам оказался хаб «Информационная безопасность», также в топ-5 лидеров входят «Программирование» и «Popular science».

Антитоп занимает Gtk и Cocoa.

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

Рейтинг


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

Информационная безопасность

Как я год не работал в Сбербанке 304000 просмотров, 599 комментариев, рейтинг +457.0/-14.0
Выброшенные на помойку умные лампочки — ценный источник личной информации 232000 просмотров, 147 комментариев, рейтинг +75.0/-11.0
Мошенники и ЭЦП — всё очень плохо 176000 просмотров, 778 комментариев, рейтинг +356.0/-0.0
Как Мегафон спалился на мобильных подписках 166000 просмотров, 676 комментариев, рейтинг +624.0/-2.0
Взлом вк, двухфакторная аутентификация не спасет 148000 просмотров, 332 комментария, рейтинг +124.0/-17.0
Как браузер помогает товарищу майору 132000 просмотров, 321 комментарий, рейтинг +246.0/-19.0
Крупнейший дамп в истории: 2,7 млрд аккаунтов, из них 773 млн уникальных 123000 просмотров, 154 комментария, рейтинг +86.0/-5.0
Дорогая, мы убиваем Интернет 121000 просмотров, 933 комментария, рейтинг +392.0/-83.0
'Мобильный контент' бесплатно, без смс и регистраций. Подробности мошенничества от Мегафона 114000 просмотров, 478 комментариев, рейтинг +488.0/-8.0
Сканер портов в личном кабинете Ростелекома 111000 просмотров, 194 комментария, рейтинг +300.0/-8.0

Программирование

Про одного парня 167000 просмотров, 249 комментариев, рейтинг +239.0/-33.0
Чем быстрее вы забудете ООП, тем лучше для вас и ваших программ 129000 просмотров, 1271 комментарий, рейтинг +131.0/-63.0
Почему Senior Developer'ы не могут устроиться на работу 119000 просмотров, 901 комментарий, рейтинг +151.0/-14.0
Старикам здесь не место? Программируем после тридцати пяти 116000 просмотров, 649 комментариев, рейтинг +222.0/-16.0
Новые языки программирования незаметно убивают нашу связь с реальностью 106000 просмотров, 764 комментария, рейтинг +164.0/-52.0
Чему я научился на своём горьком опыте (за 30 лет в разработке ПО) 101000 просмотров, 128 комментариев, рейтинг +178.0/-9.0
Самые редкие и самые дорогие языки программирования 82900 просмотров, 119 комментариев, рейтинг +38.0/-10.0
Курс лекций по JavaScript и Node.js в КПИ 80300 просмотров, 14 комментариев, рейтинг +34.0/-2.0
ІТ термины на примере процесса выращивания картошки 78000 просмотров, 86 комментариев, рейтинг +84.0/-14.0
256 строчек голого C++: пишем трассировщик лучей с нуля за несколько часов 77600 просмотров, 124 комментария, рейтинг +241.0/-0.0

Научно-популярное

Что курил конструктор: необычное огнестрельное оружие 236000 просмотров, 123 комментария, рейтинг +119.0/-9.0
Учёные нашли самое старое живое позвоночное на Земле 234000 просмотров, 212 комментариев, рейтинг +82.0/-14.0
Сериал 'Чернобыль': смотреть и думать 173000 просмотров, 803 комментария, рейтинг +164.0/-25.0
12-летний подросток провёл реакцию ядерного синтеза в домашней лаборатории 145000 просмотров, 280 комментариев, рейтинг +126.0/-29.0
Сказ о сплаве Розе и отвалившейся КРЕНке 134000 просмотров, 244 комментария, рейтинг +217.0/-1.0
Увеличь это! Современное увеличение разрешения 134000 просмотров, 235 комментариев, рейтинг +377.0/-1.0
Софт для Boeing-737 Max писался аутсорсерами, зарабатывающими $9 в час 126000 просмотров, 560 комментариев, рейтинг +153.0/-6.0
Не нервничай, не спеши, не перебивай: история одной трагедии 121000 просмотров, 384 комментария, рейтинг +242.0/-4.0
Математики обнаружили идеальный способ перемножения чисел 108000 просмотров, 222 комментария, рейтинг +173.0/-10.0
Новые языки программирования незаметно убивают нашу связь с реальностью 106000 просмотров, 764 комментария, рейтинг +164.0/-52.0

Карьера

Как я год не работал в Сбербанке 304000 просмотров, 599 комментариев, рейтинг +457.0/-14.0
I ruin developers' lives with my code reviews and I'm sorry 187000 просмотров, 21 комментарий, рейтинг +37.0/-3.0
Король разработки 179000 просмотров, 668 комментариев, рейтинг +315.0/-60.0
Про одного парня 167000 просмотров, 249 комментариев, рейтинг +239.0/-33.0
На пенсию в 22 158000 просмотров, 927 комментариев, рейтинг +259.0/-100.0
Как заменить лампочку на рабочем месте так, чтобы тебя не уволили? 139000 просмотров, 762 комментария, рейтинг +200.0/-20.0
Инновации по-русски 128000 просмотров, 612 комментариев, рейтинг +480.0/-33.0
Почему Senior Developer'ы не могут устроиться на работу 119000 просмотров, 901 комментарий, рейтинг +151.0/-14.0
'Сгоревшие' сотрудники: есть ли выход? 117000 просмотров, 398 комментариев, рейтинг +210.0/-14.0
Старикам здесь не место? Программируем после тридцати пяти 116000 просмотров, 649 комментариев, рейтинг +222.0/-16.0

Законодательство в IT

Мошенники и ЭЦП — всё очень плохо 176000 просмотров, 778 комментариев, рейтинг +356.0/-0.0
Как Мегафон спалился на мобильных подписках 166000 просмотров, 676 комментариев, рейтинг +624.0/-2.0
Инновации по-русски 128000 просмотров, 612 комментариев, рейтинг +480.0/-33.0
'Мобильный контент' бесплатно, без смс и регистраций. Подробности мошенничества от Мегафона 114000 просмотров, 478 комментариев, рейтинг +488.0/-8.0
Как власти Казахстана пытаются прикрыть свой провал с внедрением сертификата 111000 просмотров, 77 комментариев, рейтинг +122.0/-14.0
Как Protonmail блокируется в России 102000 просмотров, 398 комментариев, рейтинг +418.0/-7.0
Закон об изоляции Рунета принят Госдумой в трех чтениях 88200 просмотров, 878 комментариев, рейтинг +73.0/-18.0
Как программист банк выбирал и договора читал 87200 просмотров, 611 комментариев, рейтинг +166.0/-9.0
Минкомсвязи одобрило законопроект об изоляции рунета 83600 просмотров, 364 комментария, рейтинг +79.0/-9.0
Развёрнутый ответ на комментарий, а также немного о жизни провайдеров в РФ 74700 просмотров, 389 комментариев, рейтинг +290.0/-1.0

Веб-девелопмент

Старикам здесь не место? Программируем после тридцати пяти 116000 просмотров, 649 комментариев, рейтинг +222.0/-16.0
Как делать сайты в 2019 году 110000 просмотров, 278 комментариев, рейтинг +233.0/-11.0
Изучаем Docker, часть 1: основы 91300 просмотров, 24 комментария, рейтинг +52.0/-10.0
Курс лекций по JavaScript и Node.js в КПИ 80300 просмотров, 14 комментариев, рейтинг +34.0/-2.0
Стажёр Вася и его истории об идемпотентности API 68900 просмотров, 160 комментариев, рейтинг +216.0/-3.0
Понимание джойнов сломано. Это точно не пересечение кругов, честно 65900 просмотров, 223 комментария, рейтинг +138.0/-41.0
Почему не нужно тратить свое время на создание нишевых тематических сайтов 62700 просмотров, 243 комментария, рейтинг +179.0/-13.0
Делаем современное веб-приложение с нуля 62200 просмотров, 122 комментария, рейтинг +56.0/-8.0
Темный день для Vue.js 60800 просмотров, 133 комментария, рейтинг +77.0/-6.0
Зачем современную веб-разработку так усложнили? Часть 1 57700 просмотров, 319 комментариев, рейтинг +101.0/-6.0

GTK

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

Использование GtkApplication. Особенности отрисовки librsvg 1700 просмотров, 9 комментариев, рейтинг +9.0/-1.0

Заключение


Заключения не будет. Всем приятного чтения.

Let's block ads! (Why?)