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

Разработка под linux

Свой личный SMS-шлюз. Часть 1 цели, задачи, сборка и тестирование

29.04.2021 12:05:53 | Автор: admin


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

Начнем мы статью с вопроса для чего отправлять SMS, ведь на дворе 2021 год? Да, мы уже привыкли к различным мессенджерам, уведомлениям в чат-боты, но SMS до сих пор обладает наиболее гарантированным уровнем доставки. Нет зависимости от наличия интернета и сообщение поступит даже в сети 2G и при слабом сигнале и на устройство без доступа к интернет. Таким образом, если планируется отправлять сообщения, которые гарантированно должны быть доставлены и критичны к времени доставки, то SMS это возможно лучший выбор.

Под SMS-шлюзом мы понимаем программно-аппаратное устройство подключенное к сети сотового оператора и позволяющее производить автоматизированную отправку сообщений.

Где можно применять это решение?


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

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

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

Плюсы и минусы собственного шлюза


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

Плюс

  • Очень низкая цена за отправленное сообщение в районе 5 копеек за сообщение (при отправке 1000), против 2.65 на коммерческих шлюзах

Минусы

  • Отсутствует возможность задать имя в качестве отправителя будет номер телефона, а не Baton.ru, например. Но есть способ как решить это красиво и мы рассмотрим это в статье.
  • Отправка сообщения не чаще чем 1 раз в 10 секунд. Решается увеличением количества модемов.
  • Нет защиты от сбоев как на аппаратном, так и программном уровне. Объективности ради стоит отметить, что платные шлюзы тоже примерно раз в неделю сообщают о проблемах с доставкой определенным операторам. Мой опыт использования такого шлюза показывает всего один сбой за более чем 12 месяцев.

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

Устанавливаем Gammu, подключаем модемы


В качестве аппаратного ядра системы я буду использовать Orange Pi PC с Armbian просто потому, что он у меня есть и ничем не занят. Свою версию вы можете сделать на основе RPi, компьютера/сервера на Linux и даже виртуальной машины с проброшенными внутрь USB-портами это не имеет особого значения, главное мы будем использовать Linux.

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

Итак, подключимся к серверу, обновим систему и установим gammu:

ssh root@<IP_вашего_сервера>apt updateapt upgrade -yapt install gammu -y

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



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



На скрине выше видно, что установленный модем usb 5-1, Alcatel, имеет пять каналов. Теперь нам нужно определить какие из них используются для связи. Сделать это не сложно. Пишем в терминале:

screen /dev/ttyUSB1

В открывшемся окне вбиваем АТ и если в ответ получили ОК, то запоминаем этот порт это то, что нужно. Выходим CTRL+A затем D
Обратите внимание, что найдя один порт, все равно нужно проверить оставшиеся ttyUSB2, ttyUSB3, и далее. Например, используемый мной Alcatel, имеет 2 независимых канала на ttyUSB4 и ttyUSB5, что позволяет одновременно отправлять два SMS-сообщения через один модем.

Настройка Gammu


Итак мы определились с портами и теперь нужно настроить Gammu. Есть утилита конфигурации gammu-config, но мы не будем ее использовать, а запишем сразу данные в конфигурационный файл.

nano ~/.gammurc

дописываем в нижней части файла данные конфигураций. Каждый блок [gammu] это канал модема. [gammu] это канал по умолчанию, [gammu1] первый канал и т.д. Далее мы еще коснемся этого, когда будем отправлять сообщение. Итого я добавляю 3 доступных канала от двух своих модемов:

[gammu]port = /dev/ttyUSB0model = atconnection = atsynchronizetime = yeslogfile =logformat = nothinguse_locking = yesgammuloc =[gammu1]port = /dev/ttyUSB4model = atconnection = atsynchronizetime = yeslogfile =logformat = nothinguse_locking = yesgammuloc =[gammu2]port = /dev/ttyUSB5model = atconnection = atsynchronizetime = yeslogfile =logformat = nothinguse_locking = yesgammuloc =

Краткое описание настроек:

  • model тип модема и как gammu следует общаться с модемом, at с помощью AT-команд
  • connection = at тип соединения. Подключаемся на скорости 9600
  • use_locking говорит gammu, что нужно блокировать доступ к модему на время работы с ним. Иначе возможны различные ошибки и сбои


Вид настроек в терминале

Проверка работы


Gammu настроена и теперь мы можем протестировать отправку сообщений.

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

gammu sendsms TEXT +70001234567 -text "Test message"



Теперь рассмотрим варианты поинтереснее с использованием дополнительных аргументов

Если в системе несколько модемов, то добавляем нужный порт аргументом "-s <номер_порта>". Мы затрагивали этот момент, когда заполняли настройки. Нумерация начинается с 0 и в нашем случае это промежуток 0-2.

gammu -s 1 sendsms TEXT +70001234567 -text "Test message"

Отправка сообщений в PDU-формате (кириллица, прочие языки и спецсимволы) "-unicode"

gammu -s 1 sendsms TEXT +70001234567 -unicode -text "Тестовое сообщение"

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

gammu -s 1 sendsms TEXT +70001234567 -unicode -autolen 5 -text "Тестовое сообщение"

Есть очень интересный аргумент, который полезен, если вы отправляете одноразовые коды "-replacemessages 1". Суть его работы заключается в следующем мы указываем телефону, что при получении сообщения он должен заменить сообщений с ID 1, если оно имеется в полученных на то, что поступило сейчас. Таким образом телефон автоматически затирает все ранее полученные сообщения и в переписке всегда отображается только последнее полученное. Единственное, решить стоит его использовать или нет, желательно в самом начале, чтобы всегда заменять ID 1

gammu -s 1 sendsms TEXT +70001234567 -unicode -autolen 5 -flash -replacemessages 1 -text "Тестовое сообщение"



А теперь, самый интересный, на мой взгляд аргумент "-flash".

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

gammu -s 1 sendsms TEXT +70001234567 -unicode -autolen 5 -flash -replacemessages 1 -text "Тестовое сообщение"


Всё!

В следующей статье


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

Подробнее..

Свой личный SMS-шлюз. Часть 2 создаём API и форму отправки

03.05.2021 12:05:25 | Автор: admin

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

Если вы не знакомы с первой частью, советую сначала ознакомиться с ней:
Свой личный SMS-шлюз. Часть 1 цели, задачи, сборка и тестирование

Постановка задачи


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

На чем будем писать backend
Тут все просто, что умеем, на том и пишем, поэтому в моем случае PHP

Авторизация
Конечно. Сервис будет смотреть в интернет, поэтому авторизация обязательна.

Один пользователь одна sim-карта?
Конечно нет. У нас сервис для личного пользования и мы хотим иметь один логин, но при этом отправлять с нескольких номеров. Но если появится необходимость выделить один шлюз под конкретный сервис, мы должны иметь возможность добавления пользователей.

Как мы хотим общаться с этим API, откуда будут попадать запросы
Общение будет через POST/GET. Запросы могут отправляться различными устройствами, в том числе и теми, которые не умеют POST или заморочно реализовать, поэтому принимать и обрабатывать будем $_REQUEST. Также мы хотим иметь возможность отправки сообщений через простую форму на сайте.

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

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

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

Задача на разработку поставлена, цель ясна, итак приступим


Первое что мы сделаем, определим структуру база данных. Без нее, при наших потребностях никак. Использовать будем MySQL.
Дальше нужно будет написать пару классов, к которым мы были обращаться.

Приступим к созданию БД и создание таблиц
Я буду использовать несколько таблиц для:

  • users данные пользователей
  • smsc_gateway данных шлюзов
  • gateway_smscount счётчик отправленных сообщений по каждому шлюза в конкретный месяц
  • sms_queue очередь отправки сообщений
  • sms_archive история сообщений

Структура БД дамп таблиц
# Дамп таблицы users# ------------------------------------------------------------CREATE TABLE `users` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `uuid` varchar(10) NOT NULL,  `login` varchar(50) NOT NULL,  `password` varchar(32) NOT NULL,  `comment` varchar(200) NOT NULL,  `status` varchar(11) DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;# Дамп таблицы smsc_gateway# ------------------------------------------------------------CREATE TABLE `smsc_gateway` (  `id` int(20) NOT NULL AUTO_INCREMENT,  `gw_phone` varchar(11) NOT NULL DEFAULT '',  `uuid` varchar(10) NOT NULL,  `host` varchar(15) NOT NULL DEFAULT '',  `port` varchar(5) NOT NULL,  `password` varchar(12) DEFAULT '',  `maxcount` varchar(6) NOT NULL,  `status` int(1) NOT NULL,  `gateway_id` int(2) DEFAULT NULL,  `state` varchar(11) DEFAULT NULL,  `comment` varchar(100) NOT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;# Дамп таблицы gateway_smscount# ------------------------------------------------------------CREATE TABLE `gateway_smscount` (  `id` int(20) NOT NULL AUTO_INCREMENT,  `gw_phone` varchar(11) NOT NULL DEFAULT '',  `date` varchar(10) NOT NULL,  `count` int(6) NOT NULL DEFAULT '0',  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;# Дамп таблицы sms_queue# ------------------------------------------------------------CREATE TABLE `sms_queue` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `uuid` varchar(11) NOT NULL DEFAULT '',  `dateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,  `status` varchar(10) DEFAULT NULL,  `data` varchar(500) NOT NULL DEFAULT '',  `phone` varchar(11) NOT NULL DEFAULT '',  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;# Дамп таблицы sms_archive# ------------------------------------------------------------CREATE TABLE `sms_archive` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `gateway_id` varchar(11) DEFAULT NULL,  `dateTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,  `data` varchar(500) DEFAULT NULL,  `status` varchar(11) DEFAULT NULL,  `phone` varchar(11) DEFAULT NULL,  `uuid` varchar(11) DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;



Стоит подробнее остановиться на таблице с данными шлюза и используемых полей smsc_gateway. Здесь мы используем:

  • uuid id пользователя, которому назначен данный шлюз
  • host ip-адрес компьютера с модемами для подключения по ssh
  • port порт мы тоже укажем, так как желательно не использовать стандартный 22-й, а также это позволит при необходимости использовать port forwarding
  • password пароль ssh
  • gw_phone фактической номер телефона
  • maxcount ограничение на количество отправляемых сообщений
  • status статус. 1 активен, 0 заблокирован.
  • gateway_id канал этого модема в Gammu (помните, у нас может быть несколько модемов)
  • state статус шлюза. lock заблокирован.
  • comment свободное поле с комментарием, чтобы просто делать заметки, если нужно

Классов будет всего 5:

  1. Авторизация пользователя Users_Auth.class.php
  2. Работа с PDO MYSQL_PDO.class.php
  3. Обработчик по работе с входящими данными SMS SMS_data_handle.class.php
  4. Работа с Gammu Gammu_SMS.class.php
  5. Возврат http ответов в json http_response.class.php

Дальше я буду объяснять как поток данных будет ходить по API опираясь на базу данных. Мне кажется так нагляднее и понятнее. Также я приведу куски этого кода под спойлерами.

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

Пользователь отправляет запрос с параметрами:

.../smsc.php?login={user_name}&pass={user_password}&tel={phone_number}&msg={message}&flash=1&replacemessages_id=1

Значения flash и replacemessages мы рассматривали в прошлой статье. В {phone_number} можно указать несколько номеров телефонов через ",". + в номере телефона указывать не нужно, но обязательно указывать номер в международном формате (для России это 7). Так же в стоку можно добавить еще один параметр &attempts={число} количество попыток внутри одной отправки, то есть, если можем при отправке вернул ошибку, пытаться ли отправить тут же еще раз?

Вот, что происходит под капотом smsc.php

<?phprequire_once __DIR__.'/functions/config.php';XSS_secure();if (!Users_Auth::do($PDO)) http_response::return(401, ["description" => "User not found or login / password is incorrect"]);$sms_handle = new SMS_data_handle($PDO);$sms_handle->save();function XSS_secure() {     function replace($arr) {        $filter = array("<", ">");        $filter_replace = array("<", ">");         for ($i=0; $i < count($filter) ; $i++) {            $str = str_replace($filter[$i], $filter_replace[$i], $arr);        }    return $str;    }     if ($_GET) $_GET = replace($_GET);    if ($_POST) $_POST = replace($_POST); }?>

Первым делом мы подключаем файл с настройками config.php:

require_once __DIR__.'/functions/config.php';

Содержание файла:

<?php // ini_set('error_reporting', E_ALL);// ini_set('display_errors', 1);// ini_set('display_startup_errors', 1); spl_autoload_register(function ($class_name) {    require_once "classes/{$class_name}.class.php";}); $PDO_param = ['db_host' => '__', // database hostname or ip'db_name' => '__', // database name'db_user' => '__', // database username'db_pass' => '__', // databse user password'db_charset' => 'utf8','pdo_opt' => [        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_LAZY,        PDO::ATTR_EMULATE_PREPARES => false,        ]]; $PDO = new MYSQL_PDO($PDO_param); ?>

делаем небольшую проверку на XSS, а далее проверяем авторизацию, вызывая метод класса Users_Auth::do($PDO):

class Users_Auth{    static function do($PDO)    {      if (!@$_REQUEST['login'] || !@$_REQUEST['pass']) http_response::return(403, ['description' => 'Check your login and password']);     // Проверяем авторизацию      $user = $PDO->query("SELECT id, status FROM users WHERE login= ? AND password= ?", [$_REQUEST['login'], md5(trim($_REQUEST['pass']))]);      if ($user->rowCount() == 0)          return 0;          // http_response::return(401, ["description" => "User not found. Login and password is incorrect"]);      $row = $user->fetch();      if ($row->status != 'active')          return 0;          // http_response::return(401, ["description" => "User status: {$row->status}"]);      return 1;    }}

Если получили false авторизация не удалась, возвращаем код и описание ошибки в json, если необходимо.

Если авторизация успешная вызываем $sms_handle->save(), проверяем переданы ли обязательные параметры телефон и текст сообщения, проверяем в БД статус пользователя, разбираем строку запроса и приводим в нужный нам вид, удаляем пробелы и "+" из номера телефона, а также разделяем их по запятой. Таким образом получаем массив номеров телефонов и текста сообщения, которое нужно на них отправить. Делаем из этого json и сохраняем в таблицу очереди на отправку. Проверка на наличие телефона обязательна. Если попытаться отправлять сообщения без указания номера телефона, возникнет ошибка в Gammu, и шлюз будет занят на несколько секунд. Когда шлюз освободится возникнет аналогичная повторная ситуация, что в свою очередь создаст так называемую пробку и последующие сообщения из очереди просто не смогут уйти.

public function save() {if (empty($_REQUEST['tel'])) http_response::return(400, ["success" => false, "description" => "Phone number is empty"]);if (empty($_REQUEST['msg'])) http_response::return(400, ["success" => false, "description" => "Message is empty"]);$msg = $_REQUEST['msg'];$search = array(' ', '+');$replace = array('', '');$phone_array = explode(",", str_replace($search, $replace, $_REQUEST['tel']));$query = $this->PDO->query("SELECT uuid FROM users WHERE login = ?", [$_REQUEST['login']]);$user_uuid = $query->fetch();$data = [    'message' => $msg,    'flash' => @$_REQUEST['flash'],    'replacemessages_id' => @$_REQUEST['replacemessages_id'],    'attempts' => @$_REQUEST['attempts']];$data['attempts'] = (@$_REQUEST['attempts']) ?: 1;foreach ($phone_array as $phone) {    $data['phone'] = $phone;    $this->PDO->query("INSERT INTO sms_queue SET uuid= ?, data= ?, phone= ?", [$user_uuid->uuid, json_encode($data, JSON_UNESCAPED_UNICODE), $phone]);}http_response::return(200, ["success" => true, "description" => "Saved to queue"]);  }

Дальше мы используем простой скрипт, который поставим в cron и будем вызывать раз в 5-10 секунд (по вкусу) send_queue.php

<?php require_once __DIR__.'/config.php'; $sms_handle = new SMS_data_handle($PDO);$sms_handle->send(); /*add follow lines to cron  crontab -edon't forget to replace <full_path_to> to your really path* * * * * ( php /<full_path_to>/send_queue.php )* * * * * ( sleep 10 ; php /<full_path_to>/send_queue.php )* * * * * ( sleep 20 ; php /<full_path_to>/send_queue.php )* * * * * ( sleep 30 ; php /<full_path_to>/send_queue.php )* * * * * ( sleep 40 ; php /<full_path_to>/send_queue.php )* * * * * ( sleep 50 ; php /<full_path_to>/send_queue.php )*/?>

Он будет обращаться к методу класса обработчика сообщений SMS_data_handle->send(). Здесь уже начинается самое интересное.

Мы получаем сообщение за последние 10 минут без тегов статуса. Если нашли такое, ставим на него тег process и берём в работу.

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

Далее мы создаем объект класса $send_proc = new Gammu_SMS($param) с параметрами и обращаемся к методу $send_proc->send($attr) с атрибутами

Весь код метода send():

public function send($с = 1) {     $sended_sms = 0;    for ($i = 0; $i < $с; $i++) {       $sms_queue = $this->get_sms_queue(1);      if (!$sms_queue->rowCount()) http_response::return(200, ["description" => "Nothing to do. Sent. count: {$sended_sms}"]);       $sms_count = $sms_queue->rowCount();      $msg_row = $sms_queue->fetch();       $this->PDO->query("UPDATE sms_queue SET status = ? WHERE id = ?", ["process", $msg_row->id]);       $user_gateway = ($this->get_gateway($msg_row->uuid));       if (!$user_gateway) {      $this->PDO->query("UPDATE sms_queue SET status = NULL WHERE id= ?", [$msg_row->id]);      http_response::return(403, ["description" => "Not active gateways or get limit of message count"]);      }       $this->gateway_lock($user_gateway->id);       $param = [      'host' => $user_gateway->host,      'port' => $user_gateway->port,      'login' => 'root',      'password' => $user_gateway->password,      ];       $sms_data = json_decode($msg_row->data);      $sms_data->message = $sms_data->message;       $attr = [      'phone' => $sms_data->phone,      'message' => $sms_data->message,      'attempts' => $sms_data->attempts,      'flash' => $sms_data->flash,      'replacemessages_id' => $sms_data->replacemessages_id,      'gateway' => $user_gateway->id,      ];      // sleep(5);      $send_proc = new Gammu_SMS($param);      if ($send_proc->send($attr)) {      $this->sms_2archive($msg_row->id, $user_gateway->id);      $this->update_gwcount($user_gateway->gw_phone);      $sended_sms++;       } else {      $this->PDO->query("UPDATE sms_queue SET status = NULL WHERE id= ?", [$msg_row->id]);      }     $this->gateway_release($user_gateway->id);     }     http_response::return(200, ["success" => true, "description" => "Message sent. Count: {$sended_sms}"]);   }

Если объект вернул true, то переносим сообщение в архив и увеличиваем счётчик отправленных сообщений. Иначе снимаем тег proccess и через некоторое время будет повторная попытка отправки по cron.

Особо внимательные заметили, что мы вызываем метод с дефолтным параметром равным одному send($с = 1). Параметр $c заложен на перспективу и позволяет нам, в случае необходимости получать пачку сообщений из базы данных и обрабатывать их отправку в цикле. Для этого в файле, вызываемом в cron нужно в вызове метода указать число сообщений для выборки их БД $sms_handle->send({число});

Обратим внимание еще на один момент. В файле smsc.php есть возможность отправлять сообщения сразу после того, как оно было добавлено в БД. Для этого нужно раскомментировать следующую строку:

// $sms_handle->send();

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

Теперь наш шлюз работает через API и умеет отправлять сообщения.

Ну и бонусом мы сделаем простую форму для отправки сообщений с сайта. Ее код не нуждается в пояснении, она просто принимает от вас тест и отправляет POST-запрос на указанный нами скрипт. Единственное в блоке отправки ajax нужно заменить url: "/<*.php>" на адрес вашего скрипта smsc.php

Итак подведем итоги проделанной работы. Мы создали аппаратную платформу, научились отправлять сообщения через терминал и расширили возможности системы собственным API для легкого доступа к шлюзу устройств способных отправлять GET/POST-запросы. Хранить историю и балансировать нагрузку между картами и прочее. Все это сильно упрощает работы со шлюзом и позволяет хранить все в одном сервисе.
Внимание, я не претендую на великолепную красоту кода и буду рад любой объективной критике для понимания своих ошибок (в случае наличия) и совершенствования навыков.
Репозиторий данного проекта на Github

Подробнее..

Перевод Заметки о Unix сильные и слабые стороны errno в традиционных Unix-окружениях

09.05.2021 18:11:45 | Автор: admin
Недавно я мимоходом отметил, что errno был, в целом, хорошим интерфейсом в Unix-системах до появления в них многопоточности. Кого-то подобное высказывание может удивить, поэтому сегодня предлагаю поговорить о сильных и слабых сторонах errno в традиционных Unix-окружениях, таких, как V7 Unix.



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

(Современный C способен на такие фокусы, как возврат двухэлементной структуры в паре регистров, но этого нельзя сказать о более старых и более простых версиях C, используемых, как минимум, в Research Linux V7.)

Некоторые системные вызовы C-библиотек Unix в V7 могли возвращать сведения об ошибке в виде специального значения, и, вероятно, нельзя говорить о том, что все они поддерживали подобную возможность (в V7 действовали ограничения на количество файлов, да и адресное пространство на PDP-11 тоже было достаточно ограниченным). Даже если бы это поддерживали все вызовы, это привело бы к необходимости писать больше кода в случаях, когда нужно было проверять возвращаемые значения команд вроде open() или sbrk(). В C-коде пришлось бы проверять то, в каком диапазоне значений находится возвращаемое значение, или другие характеристики этого значения.

(Реальные системные вызовы в V7 Unix и до неё использовали метод оповещения об ошибках, спроектированный для ассемблера, когда ядро было настроено на возврат в регистр r0 либо результата системного вызова, либо номера ошибки, и на выполнение установок, зависящих от того, что именно было возвращено. Почитать об этом можно в справке по dup для V4, которая написана в те времена, когда к Unix ещё готовили серьёзную ассемблерную документацию. C-библиотека V7 сделана так, что при возникновении ошибки делается запись в errno и возвращается -1. Почитайте, например, libc/sys/dup.s вместе с libc/crt/cerror.s.)

Слабая сторона errno заключается в том, что это самостоятельное глобальное значение. То есть оно может быть случайно перезаписано в том случае, если между моментом, когда в него, интересующим нас системным вызовом, были записаны сведения об ошибке, и моментом, когда мы решили воспользоваться errno, что-то ещё записало в него сведения о собственной ошибке. Подобное легко может произойти тогда, когда, после сбоя, выполняется прямое или непрямое обращение из обычного кода к какому-нибудь системному вызову, который тоже даёт сбой. Классической ошибкой такого рода была попытка сделать проверку того, является ли стандартный вывод (или стандартный вывод ошибки) терминалом. Делается это путём выполнения на нём TTY-вызова ioctl(). Когда вызов ioctl() завершится с ошибкой, исходное значение errno будет перезаписано значением ENOTTY, и причина ошибки, из-за которой завершился вызов open() или какой-то другой вызов, будет описана таинственным сообщением not a typewriter (cf).

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

(В V7 не было сигнала SIGCHLD, но он был в BSD. Это так из-за того, что в BSD появилась система управления заданиями, что и привело к необходимости наличия подобного сигнала. Но это уже совсем другая история.)

В целом же я полагаю, что errno был хорошим интерфейсом, учитывая ограничения традиционной Unix, когда не было многопоточности или нормальных способов возврата нескольких значений из вызовов C-функций. Хотя у него есть и минусы, и слабые стороны, их обычно можно было обойти, и обычно они не слишком часто давали о себе знать. API errno стал выглядеть весьма нескладно только тогда, когда в Unix появилась многопоточность, и в одном адресном пространстве могло присутствовать несколько сущностей, одновременно выполняющих системные вызовы. Как и большая часть того, что имеется в Unix (в особенности в эру Research Unix V7), это не идеальное, хотя и вполне приемлемое решение.

Сталкивались ли вы с проблемами errno?

Подробнее..

Перевод Заметки о Unix надёжная работа с API C-библиотеки Unix возможна только из программ, написанных на C

14.05.2021 20:06:19 | Автор: admin
Для того чтобы полностью реализовать требования системы верификации источника системных вызовов, разработчики OpenBSD хотят, чтобы Go выполнял бы системные вызовы через C-библиотеку, а не напрямую, из собственной среды выполнения (а у Go есть некоторые причины поступать именно так). На первый взгляд это кажется не особенно серьёзной проблемой. Это, конечно, немного неудобно, но у языка вроде Go должна быть возможность просто выполнять вызовы обычных функций из C-библиотеки, вроде open() (и использовать ABI вызова C-функций). К сожалению, не так всё просто, так как очень часто фрагменты обычного API C-библиотеки, на самом деле, реализованы в препроцессоре C. Из-за этого API C-библиотеки нельзя надёжно использовать для решения обычных задач без написания собственного связующего кода на C.



Звучит это, пожалуй, дико, поэтому позвольте мне проиллюстрировать это на примере всеми любимого значения errno, к которому обращаются для получения кода ошибки в том случае, если системный вызов даёт сбой (им же пользуются и для получения кодов ошибок от некоторых библиотечных вызовов). В этом материале я рассказывал о том, что в современных условиях механизм errno должен быть реализован так, чтобы у разных потоков были бы разные значения errno, так как они могут в одно и то же время выполнять различные системные вызовы. Это требует наличия у потоков собственных локальных хранилищ, а к такому хранилищу нельзя обратиться так же, как к простой переменной. Доступ к нему должен быть организован через специальный механизм, поддерживаемый средой выполнения C. Вот объявления errno из OpenBSD 6.6 и из текущей версии Fedora Linux с glibc:

/* OpenBSD */int *__errno(void);#define errno (*__errno())/* Fedora glibc */extern int *__errno_location (void) __THROW __attribute_const__;# define errno (*__errno_location ())

В обоих этих случаях переменная errno, на самом деле, представлена определением препроцессора. Это определение ссылается на внутренние недокументированные функции C-библиотеки (на что указывают два символа подчёркивания в их именах), которые не входят в состав общедоступного API. Если скомпилировать код, написанный на C, рассчитанный на работу с этим API errno (включив в код errno.h), то он будет работать, но это единственный официальный способ работы с errno. Нет некоей обычной переменной errno, которую можно загрузить в среде выполнения своего языка, например, после вызова функции open(). А если вызвать __errno или ____errno_location в своей среде выполнения, то это будет означать использование внутреннего API, который в будущем вполне может измениться (хотя он, вероятно, не изменится). Для того чтобы создавать надёжные среды выполнения языков программирования, которые ориентированы на общедоступный API С-библиотеки, недостаточно просто вызвать экспортированную функцию вроде open(); нужно ещё написать и скомпилировать собственную маленькую C-функцию, которая просто возвращает среде выполнения errno.

(Тут, помимо errno, могут быть и другие важные моменты; я предлагаю самостоятельно поискать их тем, кому интересна эта тема.)

Это, конечно, не какая-то новая проблема Unix. С первых дней stdio в V7 некоторые из функций stdio были реализованы в stdio.h в виде макросов препроцессора. Но в течение долгого времени никто не настаивал на том, чтобы единственным официально поддерживаемым способом выполнения системных вызовов было бы их выполнение из C-библиотеки, что позволяло обойти нечто вроде того, что представляет собой современный механизм errno, в тех случаях, когда не нужна совместимость с C-кодом.

(До того, как в Unix появилась многопоточность, сущность errno была представлена простой переменной и, в целом, выглядела как хороший, хотя и не идеальный интерфейс.)

Пользовались ли вы когда-нибудь недокументированными API?


Подробнее..

Релиз Linux 5.12, подробности для локалхоста

17.05.2021 12:14:39 | Автор: admin

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

Патч размером 39 MiB содержит труд 1873 разработчиков, из них 262 новых. Усилиями сообщества добавлено 518307 и удалено 313155 строк кода. Больше всего отличились следующие компании:

  • Intel;
  • Linaro;
  • Red Hat;
  • Google;
  • Huawei Technologies;
  • Facebook;
  • NVIDIA;
  • AMD;
  • SUSE;

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

- Графика


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

Intel VRR / Adaptive-Sync


Для чипов Intel Tiger Lake (Gen 12) и более новых добавлена поддержка Variable Rate Refresh (VRR) / Adaptive-Sync. Данная функциональность пока существует лишь для DisplayPort / eDP подключений. HDMI Forum закрыл доступ к спецификациям, и по этой причине ощутить преимущества адаптивной синхронизации смогут только обладатели eDP дисплеев с VRR функционалом.

Разгон GPU Radeon RX 6000


В реализации RDNA 2 (Sienna Cichlid) на ОС Linux одной из отсутствующих функций была поддержка OverDrive разгона для графических карт серии Radeon RX 6800/6900. Наконец в новой версии ядра эта возможность реализована в драйвере amdgpu. Разгон Sienna Cichlid выполняется с использованием тех же атрибутов sysfs, которые в настоящее время разгоняют AMD GPU OverDrive.

Пиксельный формат FP16 для DCE


Благодаря патчам независимого разработчика Марио Кляйнеру в ядре появилась поддержка пиксельного формата FP16 для старых поколений графических процессоров ADM Radeon. Исправления затрагивают Display and Compositing Engine с 8-й по 11-ю версию. Это позволяет снять ограничения на неспособность старых устройств масштабировать фреймбуфер формата FP16.

AC/DC для GPU Radeon и прочие обновления для amdgpu


Включение функции ACDC на мобильных графических процессорах Radeon, где сила тока и тактовый диапазон изменяются в зависимости от того, работает ли ноутбук в режиме питания переменного или постоянного тока. При включенном бите функции микропрограммное обеспечение берет на себя управление обработкой функции. Кроме того для APU Van Gogh и GPU Navy 22, a. k. a. Navy Flounder появилась возможность сброса GPU.

- Файловые системы


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

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


Зонированные блочные устройства обладают непривычными свойствами, это сделано с умыслом и во имя более плотной записи данных. Эти устройства разделены на зоны, в которых не возможен произвольный доступ для операций записи. Запись в таких зонах осуществляется только последовательно, от первого блока и до последнего.
image
Единственный способ перезаписи данных в последовательной зоне это сброс указателя записи в начало зоны, что приведет к немедленному удалению всего содержимого. С другой стороны, полностью поддерживается произвольный доступ для чтения. Для файловых систем это создает определенные проблемы, так как разработка драйверов ведется с учетом того, что блоки хранения могут быть записаны в произвольном порядке. До сих пор поддержка зонированных устройств практически отсутствовала в основных ФС, однако с релизом Linux 5.12 btrfs получил эту функциональность.

Перенос идентификаторов при монтировании


Добавлен новый системный вызов mount_setattr(2), который дает возможность переносить идентификаторы прав с одного mount на другой для fat, ext4 и xfs. Перенос ID при монтировании файловой системы полезен для ряда серверных и DevOps сценариев. В частности ФС, в которых нет самой концепции владельца ресурса, как например vfat, могут использовать данную функциональность для реализации DAC (discretionary access control) проверок.

Режим ранней записи в NFS


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

Опции mount.tfs дополнились новыми элементами:

  • writes=lazy текущий способ записи по умолчанию.
  • writes=eager нестабильные операции ввода немедленно отправляются на сервер.
  • writes=wait нестабильные операции ввода немедленно отправляются на сервер и затем ядро ожидает ответа на запрос.


XFS


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

F2FS


Добавлена поддержка выбора уровня сжатия для алгоритма LZ4, также реализована новая опция монтирования checkpoint_merge. Еще в sysfs появились новые элементы:

  • /sys/fs/f2fs/<dev name>/stat/sb_status для отображения статуса суперблока в режиме реальном времени в шестнадцатеричном формате;
  • /sys/fs/f2fs/<disk>/ckpt_thread_ioprio для изменения приоритета ввода-вывода службы совмещения контрольных точек.


CIFS


В настоящее время NFS и CIFS в Linux имеют параметр монтирования actimeo для управления кэшированием метаданных но в то же время CIFS не имеет сопутствующих параметров монтирования для того, чтобы различать атрибуты каталога кэширования и файлы. Теперь же в CIFS эти опции добавлены: acregmax для управления кэшированием файлов и acdirmax для управления кэшированием атрибутов каталогов.

- Сетевая подсистема


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

  • добавлены уведомления при изменении флагов маршрутизации на сетевом оборудовании;
  • netfilter, реализована структура владения таблицами, позволяющая пользователям привязывать таблицу вместе с содержимым к процессу через сокет netlink;
  • TCP, изменения связанные с рандомизацией портов в протоколах транспортного уровня (RFC-6056).
  • в IGMPv3 появилась поддержка механизма Explicit Host Tracking.
  • cfg80211/mac80211, у пользователей появилась возможность отключить режим HE, аналогично тому, как можно отключить VHT и HT.
  • hsr, реализована поддержка EntryForgetTime, благодаря чему удалось устранить проблемы связи с Cisco IE в режиме Redbox;
  • ipvs, новый алгоритм балансировки нагрузок, основанный на случайном выборе двух серверов, в зависимости от взвешенных оценок.


Опрос NAPI в потоках ядра


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

Благодаря исправлениям Wei Wang-а, а также Paolo Abeni, Felix Fietkau, и Jakub Kicinski ядро может по выбору создать отдельный поток для каждого сетевого интерфейса с поддержкой NAPI. После этого опрос NAPI будет выполняться в контексте этого потока, а не в программном прерывании. Это пока еще не избавление от softirq, но шаг в нужном направлении. Новое свойство активируется из sysfs через параметр /sys/class/net/<iface>/threaded.

Multipath TCP


MPTCP является набором расширений (RFC-6824) протокола TCP, с помощью которых данные для одного соединения передаются через разные сетевые интерфейсы по нескольким IP маршрутам одновременно. В новой версии появилась поддержка MP_PRIO для выставления приоритета определенным потоком нижнего уровня. Данный атрибут также определен в RFC-6824. Также улучшена возможность отображения IPv4 адресов в пространство IPv6.

Internet Protocol


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

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

- Безопасность


Сюда вошли исправления, связанные с Security Enhanced Linux, KFENCE, Trusted Protection Module и Integrity Management architecture.

KFENCE


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

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

IMA


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

Криптография


Из кода ядра удалены некоторые алгоритмы хэшей:

  • RIPE-MD 128;
  • RIPE-MD 256;
  • RIPE-MD 320;
  • Tiger 128;
  • Tiger 160;
  • Tiger 190.

Такая же судьба постигла алгоритм потокового шифрования Salsa20, а blake2 подтянулся до blake2s.

В картах eMMC стало возможным использовать встроенное шифрование.

- Nintendo 64


Хорошие новости для ретро-игроманов, в новой версии Linux наконец-то появилась поддержка игровой консоли Nintendo пятого поколения.
image
В далеком 1996 г компания выпустила игровую приставку Nintendo 64. Консоль была оснащена 64-битным процессором NEC VR4300 (MIPS R4300i) с тактовой частотой 93.75 МГц и стала самой производительной среди приставок середины 1990-х гг.

- Чипы и чипсеты


В Linux 5.12 было значительное число обновлений для архитектуры x86 и ARM.

x86


В настоящее время существует созданная сообществом, с помощью обратной разработки, поддержка Microsoft Surface System Aggregation Module (SAM). The system aggregation module в своей основе это встроенный контроллер, найденный в более поздних версиях аппаратного обеспечения Surface. SAM отвечает за обработку состояния батареи, тепловую отчетность, ввод с сенсорной панели, поведение скрытой клавиатуры и многое другое.

Платформа Lenovo IdeaPad получила поддержку:

  • подсветка клавиатуры;
  • всегда на зарядке USB;
  • повторное включение управления сенсорной панелью.

Также ACPI драйвер Lenovo ThinkPad теперь может управлять языковыми настройками клавиатуры и управлять режимами энергопотребления. Lenovo ThinkPad X1 Tablet Gen 2 обзавелся драйвером с поддержкой подсистемы HID.

ARM


В число поддерживаемых платформ, плат и устройств добавлены:

  • ASUS ASUS Zenfone 2 Laser смартфон;
  • Beacon i.MX8M Nano;
  • Beacon EmbeddedWorks (на платах RZ/G2H и RZ/G2N);\
  • Boundary Devices i.MX8MM Nitrogen SBC;
  • BQ Aquaris X5 a. k. a. Longcheer L8910 смартфон;
  • Intel eASIC N5X;
  • NanoPi M4B;
  • OnePlus6 / OnePlus6T смартфон;
  • Netgear R8000P Wi-Fi маршрутизатор;
  • PineTab Early Adopter tablet;
  • Plymovent BAS;
  • Plymovent M2M;
  • Purism Librem5 Evergreen смартфон;
  • Samsung GT-I907 смартфон;
  • Snapdragon 888 / SM8350;
  • Snapdragon MTP;
  • Sony Xperia Z3+/Z4/Z5 смартфоны.

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

  • efm32;
  • picoxcell;
  • prima2/atlas;
  • tango;
  • u300;
  • zte zx.

ARM64 также имеет немало обновлений для SoC драйвером, также в подсистеме KVM обновился код PMU (Performance Monitoring Unit) до ARMv8.

RISC-V


Появилась начальная поддержка SoC SiFive FU740, анонсированного в конце прошлого года. Этот чип используется на плате разработки HiFive Unmatched. Другим новшеством для платформы стала поддержка технологии NUMA.


Подробнее..

Перевод Floppinux Linux, умещенный на дискету

26.05.2021 12:10:08 | Автор: admin

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

Уже более 7 лет я использую Linux в качестве основной ОС. С этой системой я экспериментирую с момента появления Fedora и Ubuntu и все еще помню получение бесплатных Live-CD от Canonical. Сейчас Linux уже установлена на всех моих компьютерах, включая Raspberry Pi и смартфоны.
Я даже администрирую два сервера IBM, которые также работают на Linux. Но при всем при этом мне до сих пор еще многое неизвестно о его внутреннем устройстве. В итоге я решил обогатить свои знания, реализовав забавный и в то же время полезный мини-проект.

Введение


Я с нуля создал встраиваемый дистрибутив Linux, уместив его всего на одну дискету. На момент написания он занимает около 1Мб, так что остается еще примерно 400Кб для дополнительного ПО.

Этот дистрибутив может загружаться на 486DX с 24 Мб ОЗУ (при меньшем объеме с помощью QEMU не загрузился). Через эмулятор загрузка происходит практически мгновенно. Что же касается современного железа, не обремененного программной нагрузкой, то единственное, что ограничивает скорость загрузки это скорость самого дисковода. Ее максимальный показатель составляет 125Кб/с, но в реальности даже меньше.

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


FLOPPINUX, запущенный на Asus Eee PC 701SD Intel Celeron-M 900МГц с 512Мб ОЗУ

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

Если у вас есть желание проделать нечто подобное, то эта статья для вас.

Выбор приложения


Первым приложением, которое я хочу запустить, будет создаваемый мной олдскульный журнал Nomad Diskmag, который я планирую выпускать на дискетах. Для ПК я разработал приятный GUI с помощью PyGame. Что касается моего встраиваемого проекта, то для него я заменю фронтенд на скрипт bash. Статьи в обеих версиях представляют простые файлы .txt, поэтому все что нужно это создать обложку, содержание и выполнить cat для вывода тела каждого файла (используя less для вывода страниц).

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

Цели проекта



Красочные прозрачные дискеты!

Очевидная и наиболее важная цель уместить все (ОС + ПО) на одну дискету или в 1440 Кб. В остальном же их можно описать так:

  • Последнее ядро Linux.
  • Минимум инструментов, необходимых для поддержки встраиваемого приложения.
  • Документация с легкими и понятными шагами для воспроизведения сборки.
  • Ну и, как обычно, открытый исходный код.

Будущие дополнения:

  • Возможность монтировать другую дискету для сохранения файлов.
  • Текстовый редактор nano (или подобный).

Сборка дистрибутива FLOPPINUX



Gold Master Floppy для FLOPPINUX VERSION 0.1.0

x86_64 и x86


Компилировать 32-битный код на 64-битной системе не очень удобно, и чтобы упростить процесс, я просто проделываю это на старом ноутбуке с 32-битным ЦПУ.

Также можно использовать VirtualBox с 32-битной системой.

Если же вы хотите использовать 64-битную хост-систему, добавляйте к командам ARCH=x86. Вот пример:

make ARCH=x86 tinyconfig

EPUB


Это руководство доступно во многих форматах, включая EPUB. Так что для удобства чтения можете загрузить его на eBook.

Ссылка для скачивания: https://archive.org/details/floppinux-manual/

Рабочая директория


Создайте директорию, где будете хранить все файлы.

mkdir ~/my-linux-distro/cd ~/my-linux-distro/

Ядро


Я использую последнюю версию, которая объединяет в себе старые и новые технологии. На данный момент это Kernel 5.13.0-rc2.

Получение ресурсов:

git clone --depth=1 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitcd linux

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

make tinyconfig

Теперь нужно добавить поверх нее дополнительные настройки:

make menuconfig

Из меню выберите следующие опции:

  • Processor type and features > Processor family > 486
  • Device Drivers > Character devices > Enable TTY
  • General Setup > Configure standard kernel features (expert users) > Enable support for printk
  • General Setup > Initial RAM filesystem and RAM disk (initramfs/initrd)
  • Executable file formats > Kernel support for ELF binaries
  • Executable file formats > Kernel support for scripts starting with #!

Далее выходим из конфигурации с сохранением настроек в .config. А теперь компиляция:

make bzImage

Скорость процесса будет зависеть от скорости вашего ЦПУ. В конечном итоге ядро будет создано в arch/x86/boot/bzImage. Переместите его в основную директорию.

mv arch/x86/boot/bzImage ../

Инструменты


Без инструментов ядро будет просто загружаться, и вы ничего не сможете делать. Одной из самых популярных и легковесных утилит является BusyBox. Она заменяет (более объемные) инструменты GNU функциональностью, которой достаточно для процессов встраивания.

Последнюю версию этого продукта можно найти в соответствующем разделе их сайта https://busybox.net/downloads/. На данный момент это 1.33.1. Скачайте файл, извлеките его и смените каталог:

wget https://busybox.net/downloads/busybox-1.33.1.tar.bz2tar xjvf busybox-1.19.3.tar.bz2cd busybox-1.33.1/

Как и для ядра, здесь тоже требуется создать стартовую конфигурацию:

make allnoconfig

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

make menuconfig

Я выбрал следующие:

  • Settings > Build static binary (no shared libs)
  • Coreutils > cat, du, echo, ls, sleep, uname (change Operating system name to anything you want)
  • Console Utilities > clear
  • Editors > vi
  • Init Utilities > poweroff, reboot, init, Support reading an inittab file
  • Linux System Utilities > mount, umount
  • Miscellaneous Utilities > less
  • Shells > ash

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

makemake install

Эта команда создаст файловую систему со всеми файлами в _install. Переместите ее в основной каталог. Лично я при этом также изменяю имя.

mv _install ../filesystem

Файловая система


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

cd ../filesystemmkdir -pv {dev,proc,etc/init.d,sys,tmp}sudo mknod dev/console c 5 1sudo mknod dev/null c 1 3

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

cat >> welcome << EOFSome welcome text...EOF

Файл Inittab, обрабатывающий запуск, выход и перезапуск:

cat >> etc/inittab << EOF::sysinit:/etc/init.d/rc::askfirst:/bin/sh::restart:/sbin/init::ctrlaltdel:/sbin/reboot::shutdown:/bin/umount -a -rEOF

И сам скрипт init:

cat >> etc/init.d/rc << EOF#!/bin/shmount -t proc none /procmount -t sysfs none /sysclearcat welcome/bin/shEOF

Сделайте init исполняемым и установите владельца всех файлов как root:

chmod +x etc/init.d/rcsudo chown -R root:root .

В завершении упакуйте директорию в один файл:

find . | cpio -H newc -o | gzip -9 > ../rootfs.cpio.gz

В правильности проделанного можете убедиться, запустив QEMU из основной директории:

qemu-system-i386 -kernel bzImage -initrd rootfs.cpio.gz

Теперь можно записывать систему на дискету.

Загрузочный образ


Создайте загрузочный файл syslinux, который будет указывать на созданное ядро и файловую систему:

cat >> syslinux.cfg << EOFDEFAULT linuxLABEL linuxSAY [ BOOTING FLOPPINUX VERSION 0.1.0 ]KERNEL bzImageAPPEND initrd=rootfs.cpio.gzEOF


chmod +x syslinux.cfg

Создайте пустой образ дискеты:

dd if=/dev/zero of=floppinux.img bs=1k count=1440mkdosfs floppinux.imgsyslinux --install floppinux.img

Смонтируйте его, после чего скопируйте туда syslinux, ядро и файловую систему:

sudo mount -o loop floppinux.img /mntsudo cp bzImage /mntsudo cp rootfs.cpio.gz /mntsudo cp syslinux.cfg /mntsudo umount /mnt

Готово!

Теперь у вас есть собственный образ дистрибутива floppinux.img, готовый к записи на дискету и загрузке на физическом устройстве.

Запись


Если у вас есть встроенный дисковод:

sudo dd if=floppinux.img of=/dev/fd0

У меня возникли сложности с записью образа на внешний дисковод из под Linux, поэтому я использовал инструмент diskwrite в Windows. Проблему я выявил позднее. Если у вас тоже USB-дисковод, то он будет отображаться как /dev/hd*. Команда на моем ПК:

sudo dd if=floppinux.img of=/dev/sdb

Весь процесс занял меньше трех минут.

1474560 bytes (1,5 MB, 1,4 MiB) copied, 164,476 s, 9,0 kB/s

Первая загрузка!


Загрузка Floppinux на Fujitsu Siemens P1610 Intel Core Solo 1.2 ГГц с 1 Гб ОЗУ:

Общая сводка


Объем диска: 1440Кб / 1.44Мб
Размер ядра: 632Кб
Инструменты: 552Кб
Оставшееся свободное место (du -h): 272Кб

Ссылки для скачивания


Если вы не хотите заморачиваться со всем этим, то просто скачайте мои файлы:

Версия 0.1.0
Голая система, готовая для кастомизации.


Запуск


<source lang="bash">qemu-system-i386 -fda floppinux.img

Версия 0.2.1


FLOPPINUX Version 0.2.0 новый логотип и загрузочный образ

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

Подробнее об этом я написал в дополнении Floppinux Update 0.2.1


Добавление приложения


Теперь, когда у нас есть встраиваемый дистрибутив, пора найти ему применение. Загружается он очень быстро (после загрузки дисковода) и может легко выполнять любое скомпилированное приложение. Я же хочу поиграться со скриптами, поэтому вместо скомпилированной программы добавлю скрипты .sh. Далее процесс будет таким же.

  • Обновите файлы в каталоге /filesystem/
  • Сожмите файл rootfs
  • Смонтируйте образ дистрибутива
  • Замените файл rootfs
  • Размонтируйте образ
  • (необязательно) запишите новый образ на дискету
  • Загрузите новую систему с обновленным ПО

Режим KIOSK


FLOPPINUX запускает любое приложение, находящееся в /home/main. Измените этот путь для запуска вашей программы.

Ресурсы


Репозиторий GitHub https://github.com/w84death/floppinux


Обсуждение (англ.)


https://news.ycombinator.com/item?id=27247612


Подробнее..

Перевод 30 лет Линукса. Интервью с Линусом Торвальдсом. Часть 1

04.05.2021 10:05:07 | Автор: admin


Тридцать лет назад Линусу Торвальдсу был 21 год, он был студентом Хельсинского университета. Именно тогда он впервые выпустил ядро Linux.Анонс этого события начинался так: Я делаю (свободную) операционную систему (просто в качестве хобби, большой и профессиональной она не будет). Три десятилетия спустявсе топ-500 суперкомпьютеров в мире работают под Linux, равно как и более 70% всех смартфонов. Linux явно стал и большим, и профессиональным.

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

Следующее интервью одна из серии бесед с лидерами опенсорса. Линус Торвальдс ответил на вопросы по электронной почте, поразмышляв о том, что он узнал за годы руководства большим опенсорсным проектом. В первой части акцент сделан на разработке ядра Linux и Git. [Linux] был личным проектом, который вырос не из какой-нибудь большой мечты создать новую операционную систему, объясняет Линус, а в буквальном смысле несколько спонтанно, ведь я изначально просто сам хотел разобраться во входах и выходах моего нового железа для ПК.

Что касается создания Git и его последующей передачи Джунио Хамано для дальнейшей доработки и поддержки, Линус отметил: Не собираюсь утверждать, что программирование это искусство, поскольку на самом деле это большей частью просто хорошая инженерия. Я горячо верю в мантру Томаса Эдисона об одном проценте таланта и девяноста девяти процентах упорного труда: почти все зависит от мелких деталей и ежедневной рутинной работы. Но есть и эта эпизодическая составляющая, называемая талант, этот хороший вкус, который сводится не только к решению какой-либо задачи, но и к стремлению решить ее чисто, аккуратно и да, даже красиво. У Джунио есть как раз такой хороший вкус.

Итак, читайте первую часть этого интервью (есть и вторая). В оригинале она выходит через неделю после первой, и во второй части Линус исследует те уроки и озарения, которые приобрел за три десятилетия во главе разработки ядра Linux.

Разработка ядра Linux


Джереми Эндрюс: Linux повсюду, он вдохновил целый мир опенсорса. Разумеется, так было не всегда. Вы прославились тем, что выпустили ядро Linux еще в 1991 году, скромно сообщив об этом в Usenet в разделе comp.os.minix. Десять лет спустя вы написали увлекательную и глубоко личную книгу под названием Ради удовольствия: Рассказ нечаянного революционера, где разобрали большую часть этой истории. В августе этого года Linux празднует тридцатилетие! Это захватывающе, поздравляем! В какой момент на вашем пути вы осознали, что Linux это уже гораздо больше, чем просто хобби?

Линус Торвальдс: возможно, прозвучит слегка потешно, но, на самом деле, это произошло очень рано. Уже к концу девяносто первого (и определенно к началу девяносто второго) Linux вырос значительно сильнее, чем я ожидал.


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

Думаю, X11 была портирована на Linux где-то в апреле девяносто второго (не верьте мне на слово, когда я припоминаю даты слиииишком давно дело было), а еще один серьезный шаг свершился, когда у системы вдруг появился GUI и целый новый набор возможностей.

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

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

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

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

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

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

К чему это привело: мало того, что люди стали распространять Linux на собраниях в группах пользователей Unix, но и в считанные месяцы появились первые дистрибутивы для дискет, например, SLS и Slackware.

По сравнению с теми первыми, по-настоящему фундаментальными изменениями, все дальнейшие можно считать пошаговыми. Разумеется, некоторые из этих шагов были весьма велики (систему взяла на вооружение IBM, под мою систему портировали Oracle DB, состоялись первичные коммерческие предложения Red Hat, Android расцвел на смартфонах, т.д.), но лично мне эти события все равно казались не столь революционными, как люди, которых я даже не знаю, уже используют Linux.

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


ЛТ: Ничуть.

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

Но не менее важно, что я на 100% уверен: именно такая лицензия во многом определила успех Linux (и Git, если уж на то пошло). Думаю, всем причастным гораздо приятнее знать, что все в равных правах, и никого такая лицензия не выделяет.

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

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

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

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

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

При таком подходе все честны. Включая меня. Каждый может сделать форк проекта и пойти своим путем, сказать: пока, Линус, у меня есть своя версия Linux, и ее поддержку я беру на себя. Я особенный только потому что и только до тех пор, пока люди доверяют мне за хорошо сделанную работу. Именно так и должно быть.

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

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

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

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

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

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


ЛТ: В те времена в сообществе еще бушевали серьезные флеймы по поводу BSD и GPL (думаю, отчасти они разжигались из-за того, что у rms настоящий талант бесить людей), так что я встречал разные дискуссии на тему лицензирования только в разных новостных группах usenet, которые я читал (такие источники, какcomp.arch,comp.os.minixи т.д.).

Но двумя основными поводами были, пожалуй, банальный gcc который очень и очень поспособствовал тому, чтобы Linux набрал ход, поскольку мне был абсолютно необходим компилятор для C и Ларс Виржениус (Ласу), другой шведскоязычный студент с факультета компьютерных наук, с которым мы учились в университете на одном курсе (шведскоязычное меньшинство в Финляндии очень невелико).

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

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

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

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

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

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

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

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

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

Дж.А.: Какова ваша рабочая обстановка? Например, комфортнее ли вам работать в затемненной комнате, где ничего не отвлекает, либо в комнате с видовым окном? Вы склонны работать в тишине или под музыку? Какое аппаратное обеспечение вы обычно используете? Выполняете ревью кода в vi, в окне терминала или в навороченной IDE? И есть ли такой дистрибутив Linux, который вы предпочитаете для данной работы?


ЛТ: не могу сказать, что у меня в комнате темно, но я действительно прикрываю шторами окно у рабочего места, поскольку яркий солнечный свет мне не нравится (правда, в этот сезон в Орегоне его и так не слишком много;). Так что никаких панорам, только (заваленный) стол с двумя 4k мониторами имощным ПК под столом. И еще пара ноутбуков под рукой, для тестирования и на случай, если какая-то работа прилетит мне в дороге.

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

Вся работа делается в традиционном терминале, хотя, я и не пользуюсь 'vi'. Я работаю с этим убогим micro-emacs, который не имеет ничего общего с emacs от GNU, с той оговоркой, что некоторые привязки клавиш у них похожи. Я привык работать с этим редактором еще в Хельсинском университете, будучи юнцом, и так и не смог от него отучиться, хотя, подозреваю, вскоре мне придется это сделать. Несколько лет я сварганил для него (очень ограниченную) поддержку utf-8, но редактор уже старый, и во всех его аспектах сквозит, что написан он был в 1980-е, а та версия, которой пользуюсь я это форк, не поддерживаемый с середины 90-х.

В Хельсинском университете этот редактор использовался, поскольку он работал под DOS, VAX/VMSиUnix, почему и мне довелось с ним познакомиться. А теперь он просто вшит мне в пальцы. На самом деле, давно пора переключиться на какую-то альтернативу, которая исправно поддерживается и как следует воспринимает utf-8. Пожалуй, попробую 'nano'. Мой же наспех слепленный антикварный мусор работает на том уровне вполне приемлемо, что у меня не возникало острой нужды переучивать мои старые пальцы на новые фокусы.

Итак, моя настольная рабочая среда весьма безыскусна: открыто несколько текстовых терминалов, еще браузер с почтой (плюс еще несколько вкладок, в основном с техническими и новостными сайтами). Я хочу, чтобы значительная часть рабочего стола была свободна, поскольку привык работать с достаточно большими окнами терминалов (100x40 можно сказать, таков у меня исходный размер окна по умолчанию), и у меня бок о бок открыто несколько окон терминала. Поэтому работаю с двумя мониторами по 4k.

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

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

ЛТ: О, я не читаю саму рассылку, посвященную разработке ядра, годами не читал. Там слишком много всего.

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

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

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

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

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

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


Дж.А.: Я пристально следил за разработкой ядра на протяжении примерно десяти лет,вел на эту тему блог в KernelTrap и писал о новых возможностях по мере их развития. Бросил заниматься этим примерно к моменту выхода версии ядра 3.0, выпущенной спустя 8 лет, когда выходили версии с номерами 2.6.x. Можете ли резюмировать, какие наиболее интересные события произошли с ядром после релиза версии 3.0?

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

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

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

Мы затеяли всю историю с привязкой релизов ко времени и с окном по сведению патчей (merge window) во времена 2.6.x, поэтому сама эта инициатива не нова. Но именно в 3.0 последние реликты у номера есть значение были выброшены на свалку.

У нас была и случайная схема нумерации (в основном до версии 1.0), у нас была целая модель нечетные минорные номера соответствует версии ядра, которая находится в разработке, четные означают стабильное ядро, готовое к продакшену, после чего в версиях 2.6.x мы перешли к модели с привязкой релизов по времени. Но у людей по-прежнему оставался вопрос Что должно произойти, чтобы увеличился мажорный номер. И в версии 3.0 было официально объявлено, что четный мажорный номер версии не несет никакой семантики, и что мы всего лишь стараемся придерживаться простой нумерации, с которой было бы удобно обращаться, и которая бы чрезмерно не разрасталась.

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

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

Дж.А.: На настоящий момент последний релиз 5.12-rc5. Как стандартизирован процесс релизов? Например, изменения какого рода попадают в -rc1, по сравнению с -rc2 и так далее? И в какой момент вы решаете, что очередной релиз готов к официальному выходу? Что происходит, если вы ошиблись, и после финального релиза приходится серьезно отойти назад, и как часто это случается? Как этот процесс развивался с годами?

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

Поэтому у нас была такая каденция с двухнедельным окном по сведению патчей, за которым следует примерно 6-8 недель, затрачиваемых на изучение кандидатов для релиза; думаю, такие циклы поддерживаются уже на протяжении примерно 15 лет.

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

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

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

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

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

Дж.А.: В прошлом ноябре, по вашим словам, вас впечатлили новые чипсеты ARM64 от Apple, поставленные в некоторых из их новых компьютеров. Вы следите за этими разработками, чтобы поддерживать их под Linux? Вижу, workбыладобавлена в for-next. Вероятно ли, что Linux будет грузиться на оборудовании Apple MacBook уже с появлением готовящегося ядра 5.13? Станете ли вы одним из ранних пользователей? Насколько велика для вас важность ARM64?

ЛТ: я очень эпизодически проверяю, как с этим дела, но пока там все на очень раннем этапе. Как вы правильно отметили, самый ранний вариант поддержки, вероятно, будет добавлен в ядро 5.13, но учитывайте пожалуйста, что мы в самом начале пути, и аппаратное обеспечение Apple пока еще не годится дляполезной работыпод Linux.

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

Но улучшилось не только аппаратное обеспечение Apple сама инфраструктура для arm64 значительно выросла, и ядра процессора изменились от ни о чем до вполне конкурентоспособной альтернативы для серверного пространства. Еще не так давно серверное пространство arm64 представляло собой весьма унылое зрелище, но процессоры Graviton2 от Amazon и Altra от Ampere оба основаны на значительно улучшенной версии ARM Neoverse IP гораздо лучше альтернатив, имевшихся на рынке несколько лет назад.

Я уже более десяти лет дожидался, пока появится удобная машина с ARM, и ее до сих пор нет, но сейчас до нее гораздо ближе, чем было когда-то.

На самом деле могу сказать, что хотел машину с ARM гораздо дольше, еще в подростковые годы, причем, по-настоящему желанна была Acorn Archimedes, но из соображений цены и доступности пришлось удовлетвориться Sinclair QL (процессор M68008), а затем, конечно же, через несколько лет я сменил ее на i386 PC.

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

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

ЛТ: на самом деле, намвесьмахорошо удавалось даже целиком переписывать некоторые вещи, если была такая необходимость, поэтому все детали, которые казались необезвреженными бомбами, давным-давно переписаны.

Естественно, у нас есть изрядное количество слоев, которые оставлены для обеспечения совместимости, но обычно там не ужас-ужас. Причем, неясно, а исчезнут ли эти слои для совместимости, если переписать все с нуля ведь они нужны для обратной совместимости со старыми бинарными файлами (а зачастую и для обратной совместимости со старыми архитектурами, например, для запуска 32-битных приложений для x86 на x86-64). Поскольку я считаю обратную совместимость очень важной, я хотел бы сохранить их даже в переписанной версии.

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

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

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

Дж.А.: Как насчет хотя бы частично переписать ядро на Rust, языке, который разрабатывался именно с прицелом на производительность и безопасность? Есть ли пространство для улучшения в таком ключе? Как вы считаете, возможно ли, чтобы другой язык, например, Rust, заменил C в ядре?


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

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

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

Дж.А.: Есть ли в ядре какие-либо конкретные элементы, которыми вы лично особенно гордитесь?

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

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

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

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

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

Итак, одна из основных задач, решаемых на уровне VFS это обработка всего кэширования и всех блокировок, связанных с компонентами имени пути, а также с обработкой всех операций, касающихся сериализации и обхода точек монтирования, причем, все это делается в основном при помощи неблокирующих алгоритмов (RCU), а также с применением весьма умных сущностей, напоминающих блокировки (блокировка lockref, предусмотренная в Linux это очень особенная спин-блокировка с подсчетом ссылок, буквально предназначенная для кэширования dcache, и, в принципе, это специализированный механизм подсчета ссылок, учитывающий блокировки, который в определенных типичных ситуациях может выполнять исключение блокировок).

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

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

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

Дж.А.: Прошлый год тяжело дался всему миру. Как пандемия COVID-19 повлияла на процесс разработки ядра Linux?

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

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

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



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Перевод 30 лет Линукса. Интервью с Линусом Торвальдсом. Часть 2

06.05.2021 14:05:14 | Автор: admin


Первая часть интервью.

Распределенная система контроля версий Git


Дж.А.: Linux только первая из ваших работ, глобально повлиявших на мир опенсорса. В 2005 году вы также создали Git, исключительно популярную распределенную систему контроля версий. Вы быстро перенесли дерево исходников ядра Linux из проприетарного хранилища Bitkeeper в новоиспеченный Git, который сделали опенсорсным, и в том же году передали поддержку Git Джунио Хамано. История этих событий увлекательна, расскажите, что побудило вас передать этот проект так быстро, и как вы нашли и выбрали Джунио?

ЛТ: Итак, ответ на этот вопрос состоит из двух частей.


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

Итог: я занимаюсь Linux более 30 лет (до годовщины первого релизаеще остается пара месяцев, но работать над тем, что впоследствии превратилось в Linux, я стал уже более 30 лет назад), и все это время занимаюсь его поддержкой. Но Git? Я даже не думал о том, чтобы поддерживать его в долгосрочной перспективе. Он мне определенно нравится, и я, конечно, считаю, что это наилучшая из имеющихся систем управления исходниками, но она не является моей большой любовью и увлечением, если вы понимаете, о чем я.

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

Таков контекст.

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

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

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

Вот у Джунио такой хороший вкус нашелся.

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

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

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

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

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

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

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

Поскольку во многих других проектах использовались такие инструменты как CVS или SVN фундаментально некоторые люди действительно становятся особенными и пользуются обладанием, которое приходит вместе с этим статусом. В мире BSD этот феномен называется бит подтверждения (commit bit): это разряд, обладатель которого имеет право фиксировать код в центральном репозитории (или, как минимум, некоторых его частях).

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

Опять же, в Git такой ситуации не возникает. Все равны. Каждый может клонировать ветку, начать собственную разработку, и, если они хорошо справятся с работой, то при объединении их ветка может вернуться в основную, а если очень хорошо то им поручается поддержка, и именно они начинают отвечать за слияние кода в тех деревьях, за которые отвечают ;).

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

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

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

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

Итак, для меня Git всегда был хорош, но со временем стал только лучше.

Значительныеулучшения связаны с тем, насколько удобнее стало регулярным пользователям работать с Git. Во многом благодаря тому, что люди разобрались, как в Git устроен поток задач, и просто привыкли к нему (оноченьотличается от CVS и других аналогов, к которым люди привыкли ранее), но и сам Git стал гораздо приятнее в использовании.



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Управляем контактами GPIO из C .NET 5 в Linux на одноплатном компьютере Banana Pi M64 (ARM64) и Cubietruck (ARM32)

10.05.2021 12:10:00 | Автор: admin
dotnet libgpiod

Когда заходит речь про программирование на C# .NET для одноплатных компьютеров, то разговоры крутятся только в основном вокруг Raspberry Pi на Windows IoT. А как же Banana/Orange/Rock/Nano Pi, Odroid, Pine64 и другие китайские одноплатные компьютеры работающие на Linux? Так давайте это исправим, установим .NET 5 на Banana Pi BPI-M64 (ARM64) и Cubietruck (ARM32), и будем управлять контактами GPIO из C# в Linux. В первой части серии постов, подключим светодиод и кнопку для отработки прерываний и рассмотрим библиотеку Libgpiod (спойлер, библиотеку так же можно использовать в C++, Python) для доступа к контактам GPIO.

Предисловие


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

Данный пост применим не только к платам Banana Pi BPI-M64 и Cubietruck, но и другим, основанных на процессоре ARM архитектуры armv71(32-bit) и aarch64 (64-bit). На Banana Pi BPI-M64 (ARM64) и Cubietruck (ARM32) установлена ОС Armbian версии 21.02.1, основанная на Ubuntu 18.04.5 LTS (Bionic Beaver), ядро Linux 5.10.12. uname: Linux bananapim64 5.10.12-sunxi64 #21.02.1 SMP Wed Feb 3 20:42:58 CET 2021 aarch64 aarch64 aarch64 GNU/Linux

Armbian это самый популярный дистрибутив Linux, предназначенный для одноплатных компьютеров построенных на ARM процессоре, список поддерживаемых плат огромен: Orange Pi, Banana Pi, Odroid, Olimex, Cubietruck, Roseapple Pi, Pine64, NanoPi и др. Дистрибутив Armbain основан на Debian и Ubuntu. Из большого перечня поддерживаемых одноплатных компьютеров можно выбрать то решение, которое лучше всего походит для вашего IoT проекта, от максимально энергоэффективных до высокопроизводительных плат с NPU. И на базе всех этих одноплатных компьютеров, вы сможете реализовать свое решения на платформе .NET и работать с периферийными устройствами из кода на C#.

Что такое GPIO


GPIO(general-purpose input/output) интерфейс ввода/вывода общего назначения. GPIOподключены напрямую к процессоруSoC (System-on-a-Chip Система на кристалле), и неправильное использование может вывести его из строя. Большинство одноплатных компьютеров, кроме обычных двунаправленных Input/Output портов, имеют один или более интерфейсов: UART,SPI,IC/TWI,PWM (ШИМ), но не имеютADC (АЦП). GPIO- порты обычно могут быть сконфигурированны на ввод или вывод (Input/Output), состояние по умолчанию обычноINPUT.

Некоторые GPIO-порты являются просто питающими портами 3.3V, 5V и GND, они не связаны сSoCи не могут использоваться как либо еще.

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

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

Работа с контактами GPIOосуществляется через виртуальную файловую систему sysfs. стандартный интерфейс для работы с контактами sysfs впервые появился с версии ядра 2.6.26, в Linux. Работа с GPIO проходит через каталог /sys/class/gpio путём обращения к файлам-устройствам.

К портам GPIO подключаются:

  • светодиоды;
  • кнопки;
  • реле;
  • температурные и другие датчики;
  • различные периферийные устройства.

Для программирования GPIO существует несколько способов обращения:

  • Посредством файл-устройства GPIO;
  • Используя языки программирования:
    • Через прямое обращение к регистрам чипа;
    • Используя уже готовые библиотеки (libgpiod).


Одноплатный компьютер Banana Pi BPI-M64


Banana Pi BPI-M64 это 64-битный четырехъядерный мини-одноплатный компьютер, поставляемый как решение с открытым исходном кодом. Ядром системы является процессор Allwinner A64 с 4-мя ядрами Cortex-A53 с частотой 1.2 ГГц. На плате размещено 2 ГБ DDR3 SDRAM 733МГц оперативной памяти и 8 ГБ eMMC.

На плате размещен 40-контактный совместимый с Raspberry Pi разъем, который содержит: GPIO (x28), Power (+5V, +3.3V and GND), UART, I2C, SPI. И 40-контактный интерфейс MIPI DSI.

dotnet libgpiod
Banana Pi BPI-M64 и 40-контактный разъем типа Raspberry Pi 3

Наличие 40-контактного разъема типа Raspberry Pi 3 GPIO, существенно облегчает подключение датчиков из-за совпадение назначение контактов с Raspberry Pi 3. Не приходится гадать к какому контакту подключать тот или иной датчик. Указанные в посте датчики (светодиод и кнопка) подключенные к Banana Pi BPI-M64, можно подключать на те же самые контакты другого одноплатного компьютера, на котором тоже есть 40-контактный разъем, типа Raspberry Pi 3 (или к самой Raspberry Pi 3, разницы нет никакой). Единственное, необходимо изменить номера контактов (линий, ножка процессора) в программном коде, т.к. они зависят от используемого процессора. Но легко определяются но названию контакта. Плата Cubietruck (ARM32) приведена для проверки совместимости и работы кода на 32-разрядных ARM процессорах.

Banana Pi BPI-M64 GPIO Header Position
Позиция [1] 3V3 power соответствует позиции на плате со стрелочкой

Формула для вычисления номера GPIOXX
Для обращение к контактам из C# кода необходимо знать порядковый номер (линия, порт) физической ножки процессора SoC(для Allwinner). Эти данные в спецификациях отсутствую, т.к. порядковый номер получаем путем простого расчета. Например, из схемы возьмем 32-контакт на разъеме типа Raspberry Pi. Название контакта PB7, для получения номера контакта на процессоре произведем расчет по формуле:
(позиция буквы в алфавите 1) * 32 + позиция вывода.Первая буква не учитывается т.к. P PORT, позиция буквы B в алфавите = 2, получаем (2-1) * 32 + 7 = 39. Физический номер контакта PB7является номер 39. У каждого разработчика SoC может быть свой алгоритм расчета номера контактов, должен быть описан в Datasheet к процессору.

Banana Pi BPI-M64 GPIOXX
Контакт PB7 на процессоре Allwiner A64, номер ножки 39

Библиотеки .NET IoT


До того как напишем первую программу на C# по управления GPIO, необходимо рассмотреть пространство имен входящих в dotnet/iot. Все используемые библиотеки добавляются через Nuget пакеты. Подробно рассмотрим драйвера для получения доступа к контактам GPIO одноплатного компьютера. Код на C# взаимодействует с GPIO через специальный драйвер, который является абстракцией доступа к GPIO и позволяет переносить исходный код от одного одноплатного компьютера к другому, без изменений.

Пространства имен .NET IoT:

  • System.Device.Gpio. Пакет System.Device.Gpio поддерживает множество протоколов для взаимодействия с низкоуровневыми аппаратными интерфейсами:
    • General-purpose I/O (GPIO);
    • Inter-Integrated Circuit (I2C);
    • Serial Peripheral Interface (SPI);
    • Pulse Width Modulation (PWM);
    • Serial port.


  • Iot.Device.Bindings. Пакет Iot.Device.Bindings содержит:
    • Драйвера и обертки над System.Device.Gpio для различных устройств которые упрощают разработку приложений;
    • Дополнительные драйвера поддерживаемые сообществом (community-supported).


dotnet IoT Library
Стек библиотек .NET IoT

Рассмотрим первую программу типа Hello World, мигание светодиода (Blink an LED):

using System;using System.Device.Gpio;using System.Threading;Console.WriteLine("Blinking LED. Press Ctrl+C to end.");int pin = 18;using var controller = new GpioController();controller.OpenPin(pin, PinMode.Output);bool ledOn = true;while (true){    controller.Write(pin, ((ledOn) ? PinValue.High : PinValue.Low));    Thread.Sleep(1000);    ledOn = !ledOn;}

Разбор кода:

  • using System.Device.Gpio пространство имен для использования контроллера GpioController доступа к аппаратным ресурсам;
  • using var controller = new GpioController() создает экземпляр контроллера для управления контактами GPIO;
  • controller.OpenPin(pin, PinMode.Output) инициализирует контакт pin = 18 на вывод, к 18 контакту подключен светодиод;
  • controller.Write(pin, ((ledOn)? PinValue.High: PinValue.Low)) если ledOn принимает значение True, то PinValue.High присваивает высокое значение 18 контакту и светодиод загорается. На 18 контакт подается напряжение в 3.3V. Если ledOn принимает значение False, то PinValue.Low присваивает низкое значение контакту 18 и светодиод гаснет. На 18 контакт подается напряжение в 0V (или минимальное пороговое для значения 0, может быть немного выше 0V).

Далее остается компиляция под ARM архитектуру: dotnet publish -r linux-arm или dotnet publish -r linux-arm64. Но так работает просто только для Raspberry Pi. При использование одноплатных компьютерах отличных от Raspberry Pi необходимо при инициализации GpioController выбирать драйвер доступа к GPIO.

Драйвера доступа к GPIO из .NET


Классы драйверов доступа к GPIO находятся в пространстве имен System.Device.Gpio.Drivers. Доступны следующие драйвера-классы:

  • HummingBoardDriver GPIO драйвер для платы HummingBoard на процессоре NXP i.MX 6 Arm Cortex A9;
  • LibGpiodDriver этот драйвер использует библиотеку Libgpiod для получения доступа к портам GPIO, заменяет драйвер SysFsDriver. Библиотека Libgpiod может быть установлена на Linux и Armbian, не является аппаратно-зависимой, что позволяет ее использовать для различных одноплатных компьютерах ARM32 и ARM64;
  • RaspberryPi3Driver GPIO драйвер для одноплатных компьютеров Raspberry Pi 3 или 4;
  • SysFsDriver GPIO драйвер работающий поверх интерфейса SysFs для Linux и Unux систем, предоставляет существенно меньше возможностей, чем драйвер LibGpiodDriver, но не требует установки библиотеки Libgpiod. Тот случай, когда хочется просто попробовать помигать светодиодом из C# без дополнительных действий;
  • UnixDriver базовый стандартный класс доступа к GPIO для Unix систем;
  • Windows10Driver GPIO драйвер для ОС Windows 10 IoT. Из поддерживаемых плат только Raspberry Pi, весьма ограниченное применение.

В данном посте будет рассматриваться доступ к GPIO через драйвер LibGpiodDriver. Драйвер SysFsDriver базируется на устаревшем методе работы с GPIO через виртуальную файловую систему SysFs. Для решений IoT, SysFs не подходит по трем серьезным причинам:

  • Низкая скорость работы I/O;
  • Есть проблемы с безопасной работой с GPIO при совместном доступе;
  • При контейнеризации приложения на C# в контейнер придется пробрасывать много путей из файловой системы Linux, что создается дополнительные сложности. При использование библиотеки Libgpiod этого не требуется.

Библиотека Libgpiod предназначена для работы с GPIO не только из .NET кода, но и из Python, C++, и т.д. Поэтому ниже изложенная инструкция по установке библиотеки Libgpiod позволит разработчикам на Python реализовывать подобную функциональность, как и на C#. В состав пакета Libgpiod входят утилиты для работы с GPIO. До создание программы на C#, поработаем с датчиками через эти утилиты.

Схема подключения светодиода (LED) и кнопки


Подключать светодиод и кнопку будем на 40-контактный разъем совместимый с Raspberry Pi 3. Светодиод будет подключен на 33 контакт разъема, название контакта PB4, номер линии 36. Кнопка будет подключен на 35 контакт разъема, название контакта PB6, номер линии 38. Необходимо обратить внимание на поддержку прерывания на контакте PB6 для кнопки. Поддержка прерывания необходима для исключения постоянного опроса линии с помощью CPU. На контакте PB6 доступно прерывание PB_EINT6, поэтому кнопку к этому контакту и подключим. Например, соседний контакт PL12 не имеет прерывание, поэтому подключать кнопку к нему кнопку не будем. Если вы подключаете кнопку и резистор напрямую, то не забывайте в цепь добавить резистор для сопротивления для избежания выгорания порта!

libgpiod Armbian
Схема подключения светодиода (LED) и кнопки к 40-контактному разъему совместимый с Raspberry Pi 3

libgpiod Armbian
Схема назначения контактов к которым подключается светодиод (LED) и кнопка

Интерфейс GPIO ядра Linux


GPIO (General-Purpose Input/Output) является одним из наиболее часто используемых периферийных устройств во встраиваемых системах (embedded system) Linux.

Во внутренней архитектуре ядро Linux реализует доступ к GPIO через модель производитель/потребитель. Существуют драйверы, которые предоставляют доступ к линиям GPIO (драйверы контроллеров GPIO) и драйверы, которые используют линии GPIO (клавиатура, сенсорный экран, датчики и т. д.).

В ядре Linux система gpiolib занимается регистрацией и распределением GPIO. Эта структура доступна через API как для драйверов устройств, работающих в пространстве ядра (kernel space), так и для приложений пользовательского пространства (user space).

libgpiod Armbian
Схема работы gpiolib

Старый путь: использование виртуальной файловой системы sysfs для доступа к GPIO


До версии ядра Linux 4.7 для управления GPIO в пользовательском пространстве использовался интерфейс sysfs. Линии GPIO были доступны при экспорте по пути /sys/class/gpio. Так, например, для подачи сигнала 0 или 1 на линию GPIO, необходимо:

  1. Определить номер линии (или номер ножки процессора) GPIO;
  2. Экспортировать номер GPIO, записав его номер в /sys/class/gpio/export;
  3. Конфигурировать линию GPIO как вывод, указав это в /sys/class/gpio/gpioX/direction;
  4. Установить значение 1 или 0 для линии GPIO /sys/class/gpio/gpioX/value;

Для наглядности установим для линии GPIO 36 (подключен светодиод) из пользовательского пространства, значение 1. Для этого необходимо выполнить команды:

# echo 36 > /sys/class/gpio/export# echo out > /sys/class/gpio/gpio36/direction# echo 1 > /sys/class/gpio/gpio36/value

Этот подход очень простой как и интерфейс sysfs, он неплохо работает, но имеет некоторые недостатки:

  1. Экспорт линии GPIO не связан с процессом, поэтому если процесс использующий линию GPIO аварийно завершит свою работу, то эта линия GPIO так и останется экспортированной;
  2. Учитываю первый пункт возможен совместный доступ к одной и той же линии GPIO, что приведет к проблеме совместного доступа. Процесс не может узнать у ОС используется ли та или иная линия GPIO в настоящий момент;
  3. Для каждой линии GPIO приходится выполнять множество операций open()/read()/write()/close(), а так же указывать параметры (export, direction, value, и т.д.) используя методы работы с файлами. Это усложняет программный код;
  4. Невозможно включить/выключить сразу несколько линий GPIO одним вызовом;
  5. Процесс опроса для перехвата событий (прерываний от линий GPIO) ненадежен;
  6. Нет единого интерфейса (API) для конфигурирования линий GPIO;
  7. Номера, присвоенные линиям GPIO непостоянны, их приходится каждый раз экспортировать;
  8. Низкая скорость работы с линиями GPIO;

Новый путь: интерфейс chardev


Начиная с ядра Linux версии 4.8 интерфейс GPIO sysfs объявлен как deprecated и не рекомендуется к использованию. На замену sysfs появился новый API, основанный на символьных устройствах для доступа к линиям GPIO из пользовательского пространства.

Каждый контроллер GPIO (gpiochip) будет иметь символьное устройство в разделе /dev, и мы можем использовать файловые операции (open(), read(), write(), ioctl(), poll(), close()) для управления и взаимодействия с линиями GPIO. контроллеры GPIO доступны по путям /dev/gpiochipN или /sys/bus/gpiochipN, где N порядковый номер чипа. Просмотр доступных контроллеров GPIO (gpiochip) на Banana Pi BPI-M64:

root@bananapim64:~# ls /dev/gpiochip*/dev/gpiochip0  /dev/gpiochip1  /dev/gpiochip2


libgpiod Armbian
Стек работы библиотеки libgpiod

Несмотря на то, что новый API предотвращает управление линиями GPIO с помощью стандартных инструментов командной строки, таких как echo и cat, он обладает весомыми преимуществами по сравнению с интерфейсом sysfs, а именно:

  • Выделение линий GPIO связано с процессом, который он его использует. При завершение процесса, так же в случае аварийного завершения, линии GPIO используемые процессом освобождаются автоматически;
  • Дополнительно, можно всегда определить какой процесс в данное время использует определенную линию GPIO;
  • Можно одновременно читать и писать в несколько линий GPIO одновременно;
  • Контроллеры GPIO и линии GPIO можно найти по названию;
  • Можно настроить состояние вывода контакта (open-source, open-drain и т. д.);
  • Процесс опроса для перехвата событий (прерывания от линий GPIO) надежен.

Библиотека libgpiod и инструменты управления GPIO


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

Libgpiod(LibraryGeneralPurposeInput/Outputdevice) предоставляет набор API для вызова из своих программ и несколько утилит для управления линиями GPIO из пользовательского режима.

В состав libgpiod входят следующие утилиты:

  • gpiodetect выведет список всех чипов GPIO, их метки и количество линий;
  • gpioinfo выведет информацию о линиях GPIO конкретного контроллера GPIO. В таблице вывода по колонкам будет указано: номер линии, название контакта, направление ввода/вывода, текущее состояние;
  • gpioget считает текущее состояние линии GPIO;
  • gpioset установит значение для линии GPIO;
  • gpiofind выполняет поиск контроллера GPIO и линии по имени;
  • gpiomon осуществляет мониторинг состояния линии GPIO и выводит значение при изменение состояния.

Например, следующая программа написанная на C использует libgpiod для чтения строки GPIO:

void main() {struct gpiod_chip *chip;struct gpiod_line *line;int req, value;chip = gpiod_chip_open("/dev/gpiochip0");if (!chip)return -1;line = gpiod_chip_get_line(chip, 3);if (!line) {gpiod_chip_close(chip);return -1;}req = gpiod_line_request_input(line, "gpio_state");if (req) {gpiod_chip_close(chip);return -1;}value = gpiod_line_get_value(line);printf("GPIO value is: %d\n", value);gpiod_chip_close(chip);}

Библиотеку можно вызывать так же и из кода на C++, Python, C#, и т.д.

Для управления линиями GPIO из терминала необходимо использовать инструменты командной строки, предоставляемые libgpiod. Библиотеку libgpiod и инструменты управления GPIO можно установить скомпилировать из исходного текста и установить.

Установка библиотеки libgpiod и инструментов управления GPIO


Репозитарий библиотеки libgpiod доступ по адресу libgpiod/libgpiod.git. В разделе Download опубликованы релизы библиотеки. На 28.04.2021 последний релиз: v1.6.3.

Библиотеку libgpiod можно установить из репозитария дистрибутива, но скорее всего будет доступна старая версия. Установка libgpiod:

$ sudo apt-get update$ sudo apt-get install -y libgpiod-dev gpiod

Для установки последней актуальной версии необходимо выполнить скрипт установки, который возьмет последнюю версию библиотеки из исходного репозитария. В строке вызова скрипта установки setup-libgpiod-arm64.sh, в качестве первого параметра указать номер версии библиотеки (например: 1.6.3), второй параметр (необязательный) папка установки скрипта. По умолчанию библиотека установится по пути: /usr/share/libgpiod.

Скрипт установки из исходного текста библиотеки libgpiod и утилит для ARM32/ARM64:

$ cd ~/$ sudo apt-get update$ sudo apt-get install -y curl $ curl -SL --output setup-libgpiod-armv7-and-arm64.sh https://raw.githubusercontent.com/devdotnetorg/dotnet-libgpiod-linux/master/setup-libgpiod-armv7-and-arm64.sh$ chmod +x setup-libgpiod-armv7-and-arm64.sh$ sudo ./setup-libgpiod-armv7-and-arm64.sh 1.6.3

Для удаления библиотеки выполнить скрипт: remove-libgpiod-armv7-and-arm64.sh

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

root@bananapim64:~# gpiodetect -vgpiodetect (libgpiod) v1.6.3Copyright (C) 2017-2018 Bartosz GolaszewskiLicense: LGPLv2.1This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law.

Инструменты библиотеки libgpiod


Команда gpiodetect выведет список всех чипов GPIO, их метки и количество линий. Результат выполнения команды:

root@bananapim64:~# gpiodetectgpiochip0 [1f02c00.pinctrl] (32 lines)gpiochip1 [1c20800.pinctrl] (256 lines)gpiochip2 [axp20x-gpio] (2 lines)

gpiochip0 и gpiochip1, это чипы входящие в состав SoC Allwinner A64. gpiochip1 имеет выход на 40-контактный разъем совместимый с Raspberry Pi. Чип gpiochip2 отдельная микросхема управления электропитанием axp209 подключенная по интерфейсу I2C.

Для вывод справки к вызываемой команде необходимо добавлять параметр "--help". Вызов справки для команды gpiodetect. Результат выполнения команды:

root@bananapim64:~# gpiodetect --helpUsage: gpiodetect [OPTIONS]List all GPIO chips, print their labels and number of GPIO lines.Options:  -h, --help:           display this message and exit  -v, --version:        display the version and exit

Команда gpioinfo выведет информацию о линиях GPIO конкретного контроллера GPIO (или всех контроллеров GPIO, если они не указаны).Результат выполнения команды:

root@bananapim64:~# gpioinfo 1gpiochip1 - 256 lines:        line   0:      unnamed       unused   input  active-high...        line  64:      unnamed         "dc"  output  active-high [used]...        line  68:      unnamed "backlightlcdtft" output active-high [used]...        line  96:      unnamed   "spi0 CS0"  output   active-low [used]        line  97:      unnamed       unused   input  active-high        line  98:      unnamed       unused   input  active-high        line  99:      unnamed       unused   input  active-high        line 100:      unnamed      "reset"  output   active-low [used]...        line 120:      unnamed "bananapi-m64:red:pwr" output active-high [used]...        line 254:      unnamed       unused   input  active-high        line 255:      unnamed       unused   input  active-high

В таблице по колонкам указано: номер линии, название контакта, направление ввода/вывода, текущее состояние. Сейчас к Banana Pi BPI-M64 подключен LCD экран ILI9341 на SPI интерфейсе, для подключения используется вариант с управляемой подсветкой, файл DTS sun50i-a64-spi-ili9341-backlight-on-off.dts. В DTS файле контакт PC4 GPIO68 обозначен для управления подсветкой, название backlightlcdtft. Соответственно в выводе команды, указан номер линии 68, название backlightlcdtft, направление вывод, текущее состояние active-high (включено).

Команда gpioset установит значение для линии GPIO. Например, следующая команда попытается выключить подсветку на LCD ILI9341. Команда: gpioset 1 68=0, где 1 gpiochip1, 68 номер линии(контакта), 0 логическое значение, может быть 0 или 1. Результат выполнения команды:

root@bananapim64:~# gpioset 1 68=0gpioset: error setting the GPIO line values: Device or resource busyroot@bananapim64:~#

В результате мы получим ошибку линия занята, т.к. данная линия занята драйвером gpio-backlight.

Попробуем включить светодиод на линии 36, название PB4, номер контакта на 40-контактном разъеме (совместимый с Raspberry Pi) 33. Результат выполнения команды:

root@bananapim64:~# gpioset 1 36=1

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

Команда gpioget считывает текущее состояние линии GPIO. Результат выполнения команды:

root@bananapim64:~# gpioget 1 361

Получили значение 1, т.к. до этого включили светодиод командой gpioset.

Команда gpiomon будет осуществлять мониторинг состояния линии GPIO и выводить значение при изменение состояния. Будем мониторить состояние кнопки, которая подключена на линию 38, название PB4, номер контакта на 40-контактном разъеме (совместимый с Raspberry Pi) 35. Команда: gpiomon 1 38, где 1 gpiochip1, 38 номер линии (контакта). Результат выполнения команды:

root@bananapim64:~# gpiomon 1 38event:  RISING EDGE offset: 38 timestamp: [     122.943878429]event: FALLING EDGE offset: 38 timestamp: [     132.286218099]event:  RISING EDGE offset: 38 timestamp: [     137.639045559]event: FALLING EDGE offset: 38 timestamp: [     138.917400584]

Кнопка несколько раз нажималась. RISING повышение, изменение напряжения с 0V до 3.3V, кнопка нажата и удерживается состояние. FALLING понижение, изменение напряжения с 3.3V до 0V, происходит отпускание кнопки, и кнопка переходит в состояние не нажата.

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

Установка .NET 5.0 для ARM


Одно из лучших нововведений в .NET 5.0 стало увеличение производительности для архитектуры ARM64. Поэтому переход на новую версию не только увеличит производительность решения на базе ARM64, но и увеличит время автономной работы в случае использования аккумуляторной батареи.

Определение архитектуры ARM32 и ARM64 для SoC


.NET 5 устанавливается на одноплатный компьютер в соответствие с архитектурой SoC:

  • ARM32, ARMv7, aarch32, armhf 32-разрядная архитектура ARM. Первые процессоры ARM для встраиваемых систем разрабатывались именно на этой архитектуре. По заявлению компании ARM Holding, в 2022 поддержка 32-битных платформ прекратится, и будет поддерживаться только 64-битная архитектура. Это означает, что компания не будет поддерживать разработку ПО для 32-битных систем. Если конечный производитель устройства пожелает установить 32-битную ОС, то ему придется самостоятельно заняться портированием драйверов с 64-битной архитектуры на 32-битную.
  • ARM64, ARMv8, aarch64 64-разрядная архитектура ARM. Ядра Cortex-A53 и Cortex-A57, поддерживающие ARMv8, были представлены компанией ARM Holding 30 октября 2012 года.

Плата Banana Pi BPI-M64 построена на основе процессора Allwinner A64, содержит в себе 64-битные ядра Cortex-A53, поэтому поддерживает 64-разрядные приложения. Для платы Banana Pi BPI-M64 используется 64-разрядный образ ОС Armbian, поэтому на плату будем устанавливать .NET для 64-разрядных систем ARM.

Плата Cubietruck построена на основе процессора Allwinner A20 содержит в себе 32-битные ядра Cortex-A7, поэтому поддерживает только 32-разрядные приложения. Соответственно на плату устанавливается .NET для 32-разрядных систем.

Если вы не знаете какую версию .NET установить на одноплатный компьютер, то необходимо выполнить команду для получения информации об архитектуре системы: uname -m.

Выполним команду на Banana Pi BPI-M64:

root@bananapim64:~# uname -maarch64

Строка aarch64 говорит о 64-разрядной архитектуре ARM64, ARMv8, aarch64, поэтому установка .NET для 64-х разрядных ARM систем.

Выполним команду на Cubietruck:

root@cubietruck:~# uname -marmv7l

Строка armv7l говорит о 32-разрядной архитектуре ARM32, ARMv7, aarch32, armhf, поэтому установка .NET для 32-разрядных ARM систем.

Редакции .NET 5.0 на ARM


.NET 5.0 можно устанавливать в трех редакциях:

  • .NET Runtime содержит только компоненты, необходимые для запуска консольного приложения.
  • ASP.NET Core Runtime предназначен для запуска ASP.NET Core приложений, так же включает в себя .NET Runtime для запуска консольных приложений.
  • SDK включает в себя .NET Runtime, ASP.NET Core Runtime и .NET Desktop Runtime. Позволяет кроме запуска приложений, компилировать исходный код на языках C# 9.0, F# 5.0, Visual Basic 15.9.

Для запуска .NET программ достаточно установки редакции .NET Runtime, т.к. компиляция проекта будет на компьютере x86.

Загрузить .NET с сайта Microsoft можно по ссылке Download .NET 5.0.

Установка .NET Runtime


На странице Download .NET 5.0. можно узнать текущую актуальную версию .NET. В первой колонке Release information будет указана версия: v5.0.5 Released 2021-04-06. Версия номер: 5.0.5. В случае выхода более новый версии .NET, ниже в скрипте в строке export DOTNET_VERSION=5.0.5, нужно будет заменить номер версии на последний. Выполним скрипт установки, в зависимости от разрядности системы ARM32 (Cubietruck) или ARM64(Banana Pi BPI-M64):

ARM64

$ cd ~/$ apt-get update && apt-get install -y curl$ export DOTNET_VERSION=5.0.5$ curl -SL --output dotnet.tar.gz https://dotnetcli.azureedge.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-runtime-$DOTNET_VERSION-linux-arm64.tar.gz \&& mkdir -p /usr/share/dotnet \&& tar -ozxf dotnet.tar.gz -C /usr/share/dotnet \&& rm dotnet.tar.gz$ ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet

ARM32
$ cd ~/$ apt-get update && apt-get install -y curl$ export DOTNET_VERSION=5.0.5$ curl -SL --output dotnet.tar.gz https://dotnetcli.azureedge.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-runtime-$DOTNET_VERSION-linux-arm.tar.gz \&& mkdir -p /usr/share/dotnet \&& tar -ozxf dotnet.tar.gz -C /usr/share/dotnet \&& rm dotnet.tar.gz$ ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet


Проверим запуск .NET, командой (результат одинаков для Banana Pi BPI-M64 и Cubietruck): dotnet --info

root@bananapim64:~# dotnet --infoHost (useful for support):  Version: 5.0.5  Commit:  2f740adc14.NET SDKs installed:  No SDKs were found..NET runtimes installed:  Microsoft.NETCore.App 5.0.5 [/usr/share/dotnet/shared/Microsoft.NETCore.App]To install additional .NET runtimes or SDKs:  https://aka.ms/dotnet-download

.NET установлен в системе, для запуска приложений в Linux необходимо воспользоваться командой: dotnet ConsoleApp1.dll

Обновление .NET 5.0


При выходе новых версий .NET необходимо сделать следующее:

  1. Удалить папку /usr/share/dotnet/
  2. Выполнить скрипт установки, указав новую версию .NET в строке export: DOTNET_VERSION=5.0.5. Номер последней версии .NET можно посмотреть на странице Download .NET 5.0. Строку скрипта создания символической ссылки выполнять не надо: ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet


Удаленная отладка приложения на .NET 5.0 в Visual Studio Code для ARM


Удаленная отладка в Visual Studio Code позволяет в интерактивном режиме видеть ошибки и просматривать состояние переменных, без необходимости постоянного ручного переноса приложения на одноплатный компьютер, что существенно облегчает разработку. Бинарные файлы копируются в автоматическом режиме с помощью утилиты Rsync. Для работы с GPIO, настройка удаленной отладки не является обязательной задачей. Более подробно можно почитать в публикации Удаленная отладка приложения на .NET 5.0 в Visual Studio Code для ARM на примере Banana Pi BPI-M64 и Cubietruck (Armbian, Linux).

Создание первого приложения для управления (вкл/выкл светодиода) GPIO на C#, аналог утилиты gpioset


Поздравляю тебя %habrauser%! Мы уже подходим к финалу, осталось буквально чуть-чуть. Разрабатывать и компилировать приложение будем на x86 компьютере в в Visual Studio Code. Находясь в этой точке, подразумевается, что на одноплатном компьютере уже установлена платформа .NET 5 и библиотека Libgpiod, а на компьютере x86 .NET 5 и Visual Studio Code. Итак приступаем:

Шаг 1 Создание приложения dotnet-gpioset


Действия выполняются на x86 компьютере. В командной строке создаем проект с названием dotnet-gpioset: dotnet new console -o dotnet-gpioset, где dotnet-gpioset название нового проекта. Результат выполнения команды:

D:\Anton\Projects>dotnet new console -o dotnet-gpiosetGetting ready...The template "Console Application" was created successfully.Processing post-creation actions...Running 'dotnet restore' on dotnet-gpioset\dotnet-gpioset.csproj...  Определение проектов для восстановления...  Восстановлен D:\Anton\Projects\dotnet-gpioset\dotnet-gpioset.csproj (за 68 ms).Restore succeeded.

После выполнения команды будет создана папка \Projects\dotnet-gpioset\, в этой папке будет расположен наш проект: папка obj, файл программы Program.cs и файл проекта dotnet-gpioset.csproj.

Шаг 2 Установка расширения C# for Visual Studio Code (powered by OmniSharp) для Visual Studio Code


Запустим Visual Studio Code и установим расширение C# for Visual Studio Code (powered by OmniSharp), для возможности работы с кодом на C#. Для этого нажмем на закладке: 1. Extensions, затем 2. в поле ввода напишем название расширения C# for Visual Studio Code, выберем пункт 3. C# for Visual Studio Code (powered by OmniSharp). 4. Перейдем на страницу описание расширения и нажмем на кнопку Install.

.NET Visual Studio Code ARM
C# for Visual Studio Code (powered by OmniSharp)

После установки можно выполнить настройку расширения.

.NET Visual Studio Code ARM
Настройка расширения C# for Visual Studio Code

После установки расширения, перезапустим Visual Studio Code.

Шаг 3 Открытие проекта в Visual Studio Code и добавление NuGet пакетов


Откроем проект в Visual Studio Code. Меню: File =>Open Folder, и выберем папку с проектом \Projects\dotnet-gpioset\

dotnet libgpiod
Проект в Visual Studio Code

Откроем файл dotnet-gpioset.csproj, убедимся что версия .NET выставлена верно, должно быть следующее содержание:

dotnet libgpiod
Содержание файла dotnet-gpioset.csproj

NuGet пакеты можно добавить через командную строку или расширение NuGet Package Manager. Установим данное расширение, и добавим пакеты: Iot.Device.Bindings и System.Device.Gpio. Для этого нажмем комбинацию Ctrl+Shift+P, затем в поле введем: Nuget, выберем Nuget Packet Managet: Add Package.

dotnet libgpiod
Запуск расширения NuGet Package Manager

В поле ввода укажем название пакета Iot.Device.Bindings, нажмем Enter, затем выберем версию 1.4.0 и нажмем Enter. Так же сделать и для пакета System.Device.Gpio. В результате добавление пакетов, содержимое файла dotnet-gpioset.csproj должно быть следующим:

dotnet libgpiod
Содержание файла dotnet-gpioset.csproj

Шаг 4 Добавление обработки аргументов в код


Утилита dotnet-gpioset как и оригинальная gpioset будет принимать на вход точно такие же аргументы. Вызов: dotnet-gpioset 1 36=1, включит светодиод на gpiochipX 1, номер линии 36, значение 1. В режиме отладки будут заданы значения по умолчанию int_gpiochip=1, int_pin=36, pin_value = PinValue.High. Подключим пространство имен System.Device.Gpio для использование структуры PinValue.

Обработка входящих аргументов:

static void Main(string[] args){  //run: dotnet-gpioset 1 36=1  //-----------------------------------------------                          int? int_gpiochip=null,int_pin=null;  PinValue? pin_value=null;    #if DEBUG    Console.WriteLine("Debug version");    int_gpiochip=1;    int_pin=36;    pin_value = PinValue.High;  #endif  if (args.Length==2)    {      //Read args      if (int.TryParse(args[0], out int output)) int_gpiochip = output;      Regex r = new Regex(@"\d+=\d+");//36=1      if (r.IsMatch(args[1])) //check: 36=1        {          var i = args[1].Split("=");          if (int.TryParse(i[0], out output)) int_pin = output;          if (int.TryParse(i[1], out output))            {              pin_value=(output != 0) ? PinValue.High : PinValue.Low;                                         }        }      }  Console.WriteLine($"Args gpiochip={int_gpiochip}, pin={int_pin}, value={pin_value}");  //next code  Console.WriteLine("Hello World!");}

Запускаем выполнение кода для проверки, меню Run => Start Debugging, все работает отлично!

Загружено "C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.5\System.Text.Encoding.Extensions.dll". Загрузка символов пропущена. Модуль оптимизирован, включен параметр отладчика "Только мой код".Debug versionArgs gpiochip=1, pin=36, value=HighHello World!Программа "[8528] dotnet-gpioset.dll" завершилась с кодом 0 (0x0).

Шаг 5 Добавление контроллера управления GPIO c драйвером LibGpiodDriver


Для управления GPIO необходимо создать объект GpioController и указать драйвер LibGpiodDriver, для этого добавим пространство имен System.Device.Gpio.Drivers.

Добавление контроллера:

//next codeGpioController controller;var drvGpio = new LibGpiodDriver(int_gpiochip.Value);            controller = new GpioController(PinNumberingScheme.Logical, drvGpio);

Описание кода:

  • GpioController класс контроллера для управления контактами GPIO;
  • LibGpiodDriver(int_gpiochip.Value) драйвер обертки библиотеки Libgpiod, в качестве аргумента указываем номер gpiochip;
  • GpioController(PinNumberingScheme.Logical, drvGpio) инициализация контроллера, PinNumberingScheme.Logical формат указания контактов. Есть два варианта, по названию контакта или по его номеру. Но т.к. названия контактов не заданы, то обращение будет только по номеру.

Шаг 6 Управление контактом GPIO


Добавление кода для задания значения контакту:

//set value            if(!controller.IsPinOpen(int_pin.Value))  {    controller.OpenPin(int_pin.Value,PinMode.Output);    controller.Write(int_pin.Value,pin_value.Value);                      } 

Описание кода:

  • controller.IsPinOpen проверка открытия контакта, может быть занят или недоступен;
  • controller.OpenPin открытие контакта и задание ему режима работы, PinMode.Output на вывод;
  • controller.Write(int_pin.Value,pin_value.Value) выставление контакту int_pin значение pin_value.

Шаг 7 Публикация для архитектуры ARM


Открыть командную строку, и перейти в папку \Projects\dotnet-gpioset\.

Для ARM32 выполнить команду:

  • параметр --runtime задает архитектуру выполнения программы (берется из списка Runtime Identifiers (RIDs));
  • параметр --self-contained указывает на необходимость добавление в каталог всех зависимых сборок .NET, при выставление значение в False, копируются только дополнительные сборки не входящие в .NET Runtime (в данном случае будут скопированы сборки из дополнительных NuGet пакетов).

dotnet publish dotnet-gpioset.csproj --configuration Release --runtime linux-arm --self-contained false

Файлы для переноса на одноплатный компьютер будут в папке: \Projects\dotnet-gpioset\bin\Release\net5.0\linux-arm\publish\.

Для ARM64 выполнить команду:

dotnet publish dotnet-gpioset.csproj --configuration Release --runtime linux-arm64 --self-contained false

Файлы для переноса на одноплатный компьютер будут в папке: \Projects\dotnet-gpioset\bin\Release\net5.0\linux-arm64\publish\.

Шаг 8 Перенос папки \publish\


Содержимое папки \publish\ необходимо перенести в домашний каталог Linux пользователя на одноплатном компьютере. Это можно сделать используя терминал MobaXterm.

Шаг 9 Запуск dotnet-gpioset на одноплатном компьютере


Содержимое папки \publish\ было скопировано в папку /root/publish-dotnet-gpioset. Исполняемым файлом будет файл с расширением *.dll. В самом начале, светодиод был подключен на контакт 33, 40-контактного разъема совместимого с Raspberry P, название контакта PB4, номер линии 36. Поэтому в качестве аргумента номера контакта указываем 36. Для запуска программы необходимо выполнить команду:

dotnet dotnet-gpioset.dll 1 36=1

Результат выполнения команды:

root@bananapim64:~# cd /root/publish-dotnet-gpiosetroot@bananapim64:~/publish-dotnet-gpioset# dotnet dotnet-gpioset.dll 1 36=1Args gpiochip=1, pin=36, value=HighOK

Светодиод включился!



Проект доступен на GitHub dotnet-gpioset.

Создание приложения обработки прерывания от кнопки


Теперь реализуем программу обработки прерываний от GPIO. Задача будет заключаться в переключение светодиода по нажатию кнопки. Первое нажатие кнопки включит светодиод, последующее, выключит светодиод, и так до бесконечности. Программа основана на примере Push button.

Светодиод подключен контакту с номером 36. Кнопка подключена на контакт с номером 38. Итак приступаем:

Шаг 1 Создание приложения dotnet-led-button


Действия выполняются на x86 компьютере. В командной строке создаем проект с названием dotnet-led-button: dotnet new console -o dotnet-led-button, где dotnet-led-button название нового проекта.

D:\Anton\Projects>dotnet new console -o dotnet-led-buttonGetting ready...The template "Console Application" was created successfully.Processing post-creation actions...Running 'dotnet restore' on dotnet-led-button\dotnet-led-button.csproj...  Определение проектов для восстановления...  Восстановлен D:\Anton\Projects\dotnet-led-button\dotnet-led-button.csproj (за76 ms).Restore succeeded.

После выполнения команды будет создана папка с файлами проекта \Projects\dotnet-led-button\.

Шаг 2 Открытие проекта в Visual Studio Code и добавление NuGet пакетов


Точно так же, как и в предыдущем проекте добавим Nuget пакеты: Iot.Device.Bindings и System.Device.Gpio.

Шаг 3 Добавление контроллера управления GPIO c драйвером LibGpiodDriver


Добавим контроллер для управления GPIO, и выставим режим работы контактов:

private const int GPIOCHIP = 1;private const int LED_PIN = 36;private const int BUTTON_PIN = 38;       private static PinValue ledPinValue = PinValue.Low;     static void Main(string[] args){                          GpioController controller;  var drvGpio = new LibGpiodDriver(GPIOCHIP);  controller = new GpioController(PinNumberingScheme.Logical, drvGpio);  //set value  if(!controller.IsPinOpen(LED_PIN)&&!controller.IsPinOpen(BUTTON_PIN))    {      controller.OpenPin(LED_PIN,PinMode.Output);      controller.OpenPin(BUTTON_PIN,PinMode.Input);    }  controller.Write(LED_PIN,ledPinValue); //LED OFF

Описание кода:

  • controller.OpenPin(LED_PIN,PinMode.Output) - открывает контакт светодиода, и выставляет режим работы на вывод;
  • controller.OpenPin(BUTTON_PIN,PinMode.Input) - открывает контакт кнопки, и выставляет режим работы на ввод (сигнал поступает от кнопки.

Шаг 4 Добавление обработки прерывания кнопки


Обработка прерывания реализуется путем добавление Callback на изменение состояние контакта. Callback регистрируется в контроллере GPIO:

controller.RegisterCallbackForPinValueChangedEvent(BUTTON_PIN,PinEventTypes.Rising,(o, e) =>  {    ledPinValue=!ledPinValue;    controller.Write(LED_PIN,ledPinValue);    Console.WriteLine($"Press button, LED={ledPinValue}");          });

Описание кода:

  • RegisterCallbackForPinValueChangedEvent регистрация Callback на контакт BUTTON_PIN, будет срабатывать при нажатие на кнопку Rising. Так же доступно срабатывание на событие отпускание кнопки.

Шаг 5 Публикация для архитектуры ARM


Открыть командную строку, и перейти в папку \Projects\dotnet-led-button\.

Для ARM32 выполнить команду:

dotnet publish dotnet-led-button.csproj --configuration Release --runtime linux-arm --self-contained false

Файлы для переноса на одноплатный компьютер будут в папке: \Projects\dotnet-led-button\bin\Release\net5.0\linux-arm\publish\.

Для ARM64 выполнить команду:

dotnet publish dotnet-led-button.csproj --configuration Release --runtime linux-arm64 --self-contained false

Файлы для переноса на одноплатный компьютер будут в папке: \Projects\dotnet-led-button\bin\Release\net5.0\linux-arm64\publish\.

Шаг 6 Перенос папки \publish\


Содержимое папки \publish\ необходимо перенести в домашний каталог Linux пользователя на одноплатном компьютере.

Шаг 7 Запуск dotnet-led-button на одноплатном компьютере


Содержимое папки \publish\ было скопировано в папку /root/publish-dotnet-led-button. Для запуска программы необходимо выполнить команду:

dotnet dotnet-led-button.dll

Результат выполнения команды:

root@bananapim64:~/publish-dotnet-led-button# dotnet dotnet-led-button.dllCTRL+C to interrupt the read operation:Press any key, or 'X' to quit, or CTRL+C to interrupt the read operation:Press button, LED=LowPress button, LED=HighPress button, LED=LowPress button, LED=HighPress button, LED=Low

Кнопка работает!

Проект доступен на GitHub dotnet-led-button.

Теперь поговорим о скорости


Замеры скорости управления GPIO на Banana Pi BPI-M64 не проводились из-за отсутствия осциллографа. Но не так давно, пользователь ZhangGaoxing опубликовал результаты замеров скорости на Orange Pi Zero: ОС Armbian buster, ядро Linux 5.10.16, .NET 5.0.3. Тест заключался в быстром переключение контакта GPIO с 0 на 1 и наоборот, по сути осуществлялась генерация сигнала ШИМ (в Arduino аналог SoftPWM). Чем больше частота, тем быстрее переключатся контакт. Для замера был разработан проект SunxiGpioDriver.GpioSpeed. ZhangGaoxing для доступа к контактам разработал драйвер SunxiDriver, который напрямую обращается к регистрам памяти для управления GPIO. Код этого драйвера так же можно адаптировать к любой плате, путем изменения адресов регистров памяти из datasheet к процессору. Минус такого подхода заключается в отсутствие контроля к GPIO со стороны ОС, можно влезть в контакт используемой ОС и вызвать сбой работы.

Таблица замеров:
Драйвер Язык Версия библиотеки Средняя частота
SunxiDriver C# - 185 KHz
SysFsDriver C# System.Device.Gpio 1.3.0 692 Hz
LibGpiodDriver C# System.Device.Gpio 1.3.0
libgpiod 1.2-3
81 KHz
wiringOP C 35de015 1.10 MHz

Результаты подтвердили, что самым медленным интерфейсом является SysFs, и его не стоит использовать для серьезных проектов. wiringOP является С оберткой доступа к GPIO. Непосредственно управление GPIO из C кода существенно быстрее, чем из приложения на .NET, разница скорости в ~13 раз. Это и есть плата за Runtime.

Итог


Управлять контактами GPIO в C# оказалось не сложнее чем на Arduino. В отличие от Arduino в нашем распоряжение Linux с поддержкой полноценной графики, звуком, и большими возможностями подключения различной периферии. В далеком 2014 году с хабровчанином prostosergik был спор о целесообразности использовании Raspberry Pi в качестве школьного звонка. Мною был реализован подобный функционал на C# .NET Micro Framework, отладочная плата FEZ Domino. С того времени многое что изменилось. Сейчас вариант использования для подобных индивидуальных задач, одноплатных компьютеров на Linux более оправдан, чем использование микроконтроллера. Первое существенное изменение это .NET теперь работает на Linux нативно. Второе появились библиотеки которые упрощают и скрывают под капотом все сложную работу. Третье цена, сейчас одноплатный компьютер с 256 Мб ОЗУ, Ethernet и Wi-Fi в известном китайском магазине можно приобрести за 18$. За такие деньги МК, с поддержкой полноценного Web-интерфейса и шифрования сетевого трафика, вряд ли найдешь. Платформа .NET IoT позволяет работать с GPIO на достаточно высоком уровне абстракции, что существенно снижает порог вхождения. В результате любой разработчик .NET платформы, может с легкостью реализовать свое решение для IoT не вдаваясь в детали как это работает внутри. Установка платформы .NET и библиотеки Libgpiod было приведено для понимания, как это работает, но такой подход не является самым удобным. Гораздо удобнее все разворачивать в Docker контейнере, тем более это mainstream для Linux. В продолжении посмотрим как упаковывать приложение на C# вместе с .NET 5 и Libgpiod в один контейнер, для дальнейшей удобной дистрибьюции нашего решения потенциальному клиенту, задействуем LCD для вывода информации из .NET кода.



На правах рекламы


Прямо сейчас вы можете заказать мощные серверы, которые используют новейшие процессоры AMD Epyc. Гибкие тарифы от 1 ядра CPU до безумных 128 ядер CPU, 512 ГБ RAM, 4000 ГБ NVMe.

Подписывайтесь на наш чат в Telegram.

Подробнее..

Nix воспроизводимая сборка

11.05.2021 20:09:12 | Автор: admin


Привет, Хаброюзеры!


Сегодня мы продолжим наш цикл статей о Nix и как мы в Typeable его используем.


Первый пост из серии, рассказывающий об основах языка Nix, можно прочитать здесь.


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


Весь код для этой статьи можно найти в репозитарии на Github.


Проблема


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


Наше приложение


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


Наш Main.hs:


module Main wheremain :: IO ()main = putStrLn "Hello, World!"

Для сборки проекта без Nix мы используем утилиту stack (подробнее с ней можно ознакомиться здесь). В качестве описания проекта для stack требуется файл stack.yaml, содержащий список наших пакетов и resolver. Последнее это стабильный срез Hackage, базы пакетов для языка Haskell, в котором гарантируется, что все пакеты собираются и дружат друг с другом (NB подобных срезов крайне не хватает в других языках ): ).


stack.yaml:


resolver: lts-17.11packages:- hello-world

Рецепт сборки конкретного пакета находится в hello-world.cabal:


cabal-version:      2.4name:               hello-worldversion:            1.0synopsis:           Hello Worldlicense:            MITlicense-file:       LICENSEauthor:             Nickexecutable hello-world    main-is:          Main.hs    build-depends:    base >= 4 && < 5    hs-source-dirs:   src    default-language: Haskell2010    ghc-options:      -Wall -O2

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


$ stack run hello-worldHello, World!

Come to the dar^Wnix side, we have cookies!


Сам по себе stack отличное средство для сборки проектов на Haskell, но в нём не хватает многих возможностей. Для сборки программ на Haskell для Nix есть библиотека haskell.nix, разработанная компанией IOHK. Её-то мы и будем здесь использовать. Для начала, сделаем так, чтобы наш проект собирался с помощью Nix.


Haskell.nix позволяет нам в несколько строчек преобразовать всю информацию о сборке нашего проекта из .cabal-файлов и stack.yaml в derivation для Nix.


nix/stackyaml.nix:


{  # Импортируем последнюю версию haskell.nix с GitHub и инициализируем Nixpkgs с её использованием.  haskellNix ? import (builtins.fetchTarball "https://github.com/input-output-hk/haskell.nix/archive/b0d03596f974131ab64d718b98e16f0be052d852.tar.gz") {}  # Здесь мы используем последнюю стабильную версию Nixpkgs. Версия 21.05 скоро выйдет :), nixpkgsSrc ? haskellNix.sources.nixpkgs-2009, nixpkgsArgs ? haskellNix.nixpkgsArgs, pkgs ? import nixpkgsSrc nixpkgsArgs}:let  # Создаём проект на базе stack. Для проектов Cabal есть функция cabalProject.  project = pkgs.haskell-nix.stackProject {    name = "hello-world";    # Derivation с исходным кодом проекта.    # Функция cleanGit копирует для сборки проекта только файлы, присутствующие в нашем git-репозитарии.    src = pkgs.haskell-nix.haskellLib.cleanGit {      name = "hello-world";      # Параметр src должен указывать на корневую директорию, содержащую stack.yaml.      src = ../.;      # keepGitDir оставляет директорию .git при сборке.      # Это может быть полезно, например, чтобы вставить хэш коммита в код.      keepGitDir = true;    };    # В параметре modules можно указать параметры сборки как для всех модулей сразу, так и для каждого в отдельности.    modules = [{      # doCheck отвечает за запуск юнит-тестов при сборке проекта, в том числе содержащихся во всех зависимостях.      # Здесь мы этого хотим избежать, поэтому этот параметр лучше всего ставить false и включить только для нужных      # пакетов.      doCheck = false;      # Добавим для нашего Hello World флаг -Werror.      packages.hello-world.components.exes.hello-world.ghcOptions = [ "-Werror" ];    }];  };# Наружу из этого файла мы выставляем project -- наш проект, а также pkgs -- срез nixpkgs, который мы будем использовать дальше.in { inherit project; inherit pkgs; }

Давайте проверим, что наш проект теперь можно собрать через Nix. Для этого достаточно команды nix build. Как и всегда, в текущей директории будет создана символическая ссылка result, содержащая результаты сборки.


$ nix build project.hello-world.components.exes$ ./result/bin/hello-worldHello, World!

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


Dockerfile? Какой Dockerfile?


Сейчас 2021 год, и очень многие компании используют Docker для деплоя и запуска сервисов. Typeable здесь не будет исключением. В составе nixpkgs есть весьма удобный инструментарий для сборки контейнеров под названием dockerTools. Более подробно с его возможностями можно ознакомиться по ссылке, я лишь покажу, как мы с его помощью упаковываем наш код в контейнеры. Полностью код можно посмотреть в файле nix/docker.nix.


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


sourceImage = dockerTools.pullImage {  imageName = "centos";  imageDigest = "sha256:e4ca2ed0202e76be184e75fb26d14bf974193579039d5573fb2348664deef76e";  sha256 = "1j6nplfs6999qmbhjkaxwjgdij7yf31y991sna7x4cxzf77k74v3";  finalImageTag = "7";  finalImageName = "centos";};

Здесь всё очевидно для всех, кто когда-либо работал с Docker. Мы говорим Nix, какой образ из публичного Docker Registry мы хотим использовать и что дальше мы будем на него ссылаться как на sourceImage.


Для сборки самого образа в dockerTools есть функция buildImage. У неё довольно много параметров, и часто проще написать свою обёртку над ней, что мы и сделаем:


makeDockerImage = name: revision: packages: entryPoint:  dockerTools.buildImage {    name = name;    tag = revision;    fromImage = sourceImage;    contents = (with pkgs; [ bashInteractive coreutils htop strace vim ]) ++ packages;    config.Cmd = entryPoint;  };

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


И, наконец, создадим образ с нашим великолепным приложением.


hello-world = project.hello-world.components.exes.hello-world;helloImage = makeDockerImage "hello"   (if imageTag == null then "undefined" else imageTag)  [ hello-world ]  [ "${hello-world}/bin/hello-world"  ];

Для начала мы создадим алиас для нужного нам пакета, чтобы не писать project.hello-world... повсюду. Дальше, вызвав написанную ранее функцию makeDockerImage, мы создаём образ контейнера с пакетом hello-world. В качестве тэга будет указан параметр imageTag, передаваемый снаружи, либо "undefined" если ничего не передано.


Проверим сборку:


$ nix build --argstr imageTag 1.0 helloImage[4 built, 0.0 MiB DL] $ ls -l resultlrwxrwxrwx 1 user users 69 May 11 13:12 result -> /nix/store/56qqhiwahyi46g6mf355fjr1g6mcab0b-docker-image-hello.tar.gz

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


$ docker load < result 76241b8b0c76: Loading layer [==================================================>]  285.9MB/285.9MBLoaded image: hello:1.0$ docker run hello:1.0Hello, World!

Заключение


В итоге, с помощью сравнительно небольшого количества кода, у нас получилось сделать воспроизводимую сборку нашего проекта на Haskell. Точно так же, заменив haskell.nix на что-то другое, можно поступить с проектами на других языках: в nixpkgs есть встроенные средства для C/C++, Python, Node и других популярных языков.


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

Подробнее..

Перевод Thunderbird, RNP и важность хорошего API

12.05.2021 14:11:23 | Автор: admin


Недавно мне довелось побеседовать с разработчикомThunderbirdо проектировании API. В ходе этой беседы я поделился соображениями о RNP,новой реализации OpenPGP, которую Thunderbird недавно стал использовать вместо GnuPG.

Собеседник скептически отнесся к моему тезису о том, что API RNP нуждается в улучшении, и спросил, разве это не субъективно какие API лучше, а какие хуже?. Согласен, у нас нет хороших метрик для оценки API. Но не соглашусь, что мы в принципе не в силах судить об API.

На самом деле, подозреваю, что большинство опытных программистов узнают плохой API, если увидят его. Думаю, далее в этой статье получится разработать хорошую эвристику, которую я попытаюсь выстроить на моем собственном опыте работы с (и над) GnuPG,Sequoia и RNP. Затем я рассмотрю API RNP. К сожалению, этот API не только можно запросто использовать неправильно он к тому же обманчив, поэтому пока его не следует использовать в контекстах, где принципиальная роль отводится соблюдению безопасности. Но целевая аудитория Thunderbird это люди, известные своей уязвимостью, в частности, журналисты, активисты, юристы и их партнеры, отвечающие за коммуникацию; все эти люди нуждаются в защите. На мой взгляд, это означает, что в Thunderbird должны лишний раз подумать, стоит ли использовать RNP.

Примечание: также предлагаю ознакомиться с этим электронным письмом:Lets Use GPL Libraries in Thunderbird!, которое я отправилв постовую рассылку по планированию развития Thunderbird.

Каковы черты плохого API?


Прежде, чем мы вместе с Юстусом и Каем приступили к проектуSequoia, мы все втроем работали над GnuPG. Мы не только сами копались в gpg, но и беседовали, и сотрудничали со многими последующими пользователями gpg. Люди смогли сказатьмного хорошего по поводу GnuPG.


Что касается критикиgpg, наиболее значительными нам показались два вида замечаний по поводу API. Первое сводится к следующему: API gpg слишком догматичен. Например, вgpgприменяется подход, основанный на использовании личной базы ключей (keyring). Таким образом, просмотреть или использовать сертификат OpenPGP можно лишь в том случае, если он импортирован в личную базу ключей. Но некоторые разработчики желают сначала просмотреть сертификат, и лишь потом импортировать его. Например, при поиске сертификата на сервере ключей по его отпечатку, можно проверить и убедиться, что возвращенный сертификат действительно тот, что нужен, поскольку его URL является самоаутентифицируемым. Это можно сделать при помощи gpg, но только обходным путем, огибая принципы той модели программирования, которая в него заложена. Базовая идея такова: создать временный каталог, добавить в него конфигурационный файл, приказать gpgиспользовать альтернативный каталог, импортировать туда сертификат, проверить сертификат, после чего очистить временный каталог. Это официальная рекомендация, добавленнаяЮстусомна основе наших бесед с последующими пользователями gpg. Да, этот метод работает. Но для него требуется писать код, специфичный для операционной системы, этот код медленный, и в нем часто заводятся ошибки.

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

Чтобы лучше понять второй повод для беспокойства, рассмотрим уязвимости EFAIL. Основная проблема, связанная с API дешифрования gpg: при дешифровании сообщенияgpgвыдает обычный текст, даже если ввод был поврежден.gpgв таком случае действительно возвращает ошибку, но некоторые программы все равно выводят обычный текст в поврежденном виде. Так как, почему нет? Определенно, лучше показать хотя бы часть сообщения, чем не показать ничего, верно? Так вот, уязвимости EFAIL демонстрируют, как злоумышленник может этим воспользоваться, чтобы внедрить веб-багв зашифрованное сообщение. Когда пользователь просматривает это сообщение, веб-баг просачивается из сообщения. Уф.

Итак, на чьей совести этот баг? РазработчикиGnuPGнастаивали, чтопроблема на уровне приложений, в том, что они используют gpgнеправильно:

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

gpgпросигнализировала об ошибке; приложения не соблюдают контракт API. Должен согласиться с разработчиками GnuPG и добавить: интерфейсgpg был (и остается) бомбой замедленного действия, поскольку не подсказывает пользователю, как действовать правильно. Напротив, легкое и, казалось бы, полезное действие является неверным. ИAPI такоготипа, к сожалению, обычны в GnuPG.

Из чего слагается хороший API?


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

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

Типы


Покажу на примере, как мы используем типы в Sequoia, и как они помогают нам сделать хороший API. Чтобы пример был понятнее, полезно будет напомнить некоторый контекст, касающийся OpenPGP.


Простой сертификат OpenPGP

В OpenPGP существует несколько фундаментальных типов данных, а именно: сертификаты, компоненты (например, ключи и пользовательские ID), а также подписи привязки. Корень сертификата это первичный ключ, полностью определяющий отпечаток сертификата (fingerprint = Hash(primary key)). В состав сертификата обычно входят такие компоненты как подключи и пользовательские ID. OpenPGP привязывает компонент к сертификату при помощи так называемой подписи привязки. Когда мы используем в качестве отпечатка обычный хеш первичного ключа и используем подписи для привязки компонентов к первичному ключу, создаются условия, чтобы впоследствии можно было добавить и дополнительные компоненты. В состав подписей привязки также входят свойства. Поэтому есть возможность изменить компонент, например, продлить срок действия подключа. Вследствие этого с конкретным компонентом может быть ассоциировано несколько действительных подписей. Подписи привязки являются не только фундаментальной, но и неотъемлемой частью механизма безопасности OpenPGP.

Поскольку может существовать множество действительных подписей привязки, требуется способ выбирать из них нужную. В качестве первого приближения предположим, что нужная нам подпись самая недавняя, неистекшая, неотозванная действительная подпись, создание которой не отложено на будущее. Но что такое действительная подпись? В Sequoia подпись должна не только пройти математическую проверку, но и согласовываться с политикой. Например, в силу противодействия скомпрометированным коллизиям, мыдопускаем SHA-1 только в очень небольшом количестве ситуаций. (Пол Шауб, работающий надPGPainless, недавноподробно написал об этих сложностях.) Вынуждая пользователя API держать в уме все эти соображения, мы создаем почву для уязвимостей. В Sequoia легкий способ получить время истечения это безопасный способ. Рассмотрим следующий код, который работает как надо:

let p = &StandardPolicy::new(); let cert = Cert::from_str(CERT)?;for k in cert.with_policy(p, None)?.keys().subkeys() {    println!("Key {}: expiry: {}",             k.fingerprint(),             if let Some(t) = k.key_expiration_time() {                 DateTime::<Utc>::from(t).to_rfc3339()             } else {                 "never".into()             });}


cert это сертификат. Начинаем с применения политики к нему. (Политики определяются пользователем, но, как правило,StandardPolicyне только достаточна, но и наиболее уместна). Фактически здесь создается представление сертификата, в котором видны только компоненты с действительной подписью привязки. Важно, что она также изменяет и предоставляет ряд новых методов. Метод keys, к примеру, изменен так, что возвращаетValidKeyAmalgamationвместоKeyAmalgamation. (Это слияние, так как результат включает не только Key, но и все подписи, связанные с ним; некоторые считают, что этот процесс было бы удачнее назвать Катамари.\_()_/) УValidKeyAmalgamationесть действительная подпись привязки, согласно вышеприведенным критериям. А также предоставляет такие методы как key_expiration_time, что имеет смысл только с действительным ключом! Также отметим: возвращаемый тип, используемый сkey_expiration_time, эргономичен. Вместо того, чтобы возвращать необработанное значение, key_expiration_timeвозвращает SystemTime, безопасный и удобный в работе.

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

Примеры


Релиз 1.0библиотеки Sequoia состоялся в декабре 2020 года. За девять месяцев до того мы вышли на ситуацию полной работоспособности (feature complete) и были готовы к релизу. Но выжидали. Следующие девять месяцев ушли у нас на добавление документации и примеров к публичному API. Посмотрите документацию к структуре данныхCertв качестве примера, посмотрите, что у нас получилось. Как указано в нашем посте, нам не удалось предоставить примеры для всех функций до одной, но мы успели довольно много. В качестве бонуса к написанию примеров мы также успели найти несколько шероховатостей, которые при этом отполировали.

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

API RNP


RNP это свежая реализация OpenPGP, разработанная преимущественно Ribose. Примернодва года назад в Thunderbird решили интегрироватьEnigmailв Thunderbird и одновременно с этимзаменить GnuPG на RNP. Тот факт, что в Thunderbird выбрали RNP не только лестен для RNP; он также означает, что RNP стал, пожалуй, самой востребованной реализацией OpenPGP для шифрования почты.

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

Инфраструктура с критически важными требованиями к безопасности


К сожалению, RNP пока не дошла до состояния в котором, на мой взгляд, ее можно безопасно развертывать. Enigmail пользовались не только люди, озабоченные приватностью своих данных, но и журналисты, активисты и адвокаты, которым важна собственная безопасность и безопасность их собеседников. В интервью, данном в 2017 году, Бенджамин Исмаил, глава Азиатско-Тихоокеанского отделения организации Репортеры без границ, сказал:

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

Интервью с Бенджамином Исмаиломиз организацииРепортеры без границ

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

RNP и подписи привязки подключа


Говоря о том, как мы используем типы в Sequoia для того, чтобы усложнить неправильное использование API, я показал, как узнать срок истечения действия ключа, написав всего несколько строк кода. Хотел начать с примера, демонстрирующего человеку, неискушенному в OpenPGP или RNP, как тот же самый функционал можно реализовать при помощи RNP. Следующий код перебирает подключи сертификата (key) и выводит на экран срок истечения действия каждого подключа. Напоминаю: время истечения действия хранится в подписи привязки подключа, а значение 0свидетельствует, что срок действия ключа не истечет никогда.

int i;for (i = 0; i < sk_count; i ++) {  rnp_key_handle_t sk;  err = rnp_key_get_subkey_at(key, i, &sk);  if (err) {    printf("rnp_key_get_subkey_at(%d): %x\n", i, err);    return 1;  }   uint32_t expiration_time;  err = rnp_key_get_expiration(sk, &expiration_time);  if (err) {    printf("#%d (%s). rnp_key_get_expiration: %x\n",           i + 1, desc[i], err);  } else {    printf("#%d (%s) expires %"PRIu32" seconds after key's creation time.\n",           i + 1, desc[i],           expiration_time);  }}

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

#1 (doesn't expire) expires 0 seconds after key's creation time.#2 (expires) expires 94670781 seconds after key's creation time.#3 (expired) expires 86400 seconds after key's creation time.#4 (invalid sig) expires 0 seconds after key's creation time.#5 (no sig) expires 0 seconds after key's creation time.


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

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

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

Чтобы улучшить этот код, сначала необходимо проверить, есть ли у ключа действительная подпись привязки. Некоторые функции, делающие именно это, недавно были добавлены в RNP для решения проблемыCVE-2021-23991. В частности, разработчики RNP добавили функциюrnp_key_is_valid, чтобы возвращать информацию о том, действителен ли ключ. Это дополнение улучшает ситуацию, но требует от разработчика явно выбирать, должны ли проводиться эти критичные для безопасности проверки (а не явно отказываться от уже заданных проверок как было бы в случае работы с Sequoia). Поскольку проверки безопасности не относятся к выполнению полезной работы, о них легко забыть: код работает, даже если проверка безопасности проведена не была. А поскольку чтобы правильно выбрать, что проверять, нужны экспертные знания, о проверках забывают.

Следующий код предусматривает проверку безопасности и пропускает любые ключи, которые rnp_key_is_validсочтет недействительными:

int i;for (i = 0; i < sk_count; i ++) {  rnp_key_handle_t sk;  err = rnp_key_get_subkey_at(key, i, &sk);  if (err) {    printf("rnp_key_get_subkey_at(%d): %x\n", i, err);    return 1;  }   bool is_valid = false;  err = rnp_key_is_valid(sk, &is_valid);  if (err) {    printf("rnp_key_is_valid: %x\n", err);    return 1;  }   if (! is_valid) {    printf("#%d (%s) is invalid, skipping.\n",           i + 1, desc[i]);    continue;  }   uint32_t expiration_time;  err = rnp_key_get_expiration(sk, &expiration_time);  if (err) {    printf("#%d (%s). rnp_key_get_expiration: %x\n",           i + 1, desc[i], err);  } else {    printf("#%d (%s) expires %"PRIu32" seconds after key's creation time.\n",           i + 1, desc[i],           expiration_time);  }}


Вывод:

#1 (doesn't expire) expires 0 seconds after key's creation time.#2 (expires) expires 94670781 seconds after key's creation time.#3 (expired) is invalid, skipping.#4 (invalid sig) is invalid, skipping.#5 (no sig) is invalid, skipping.


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

Хотя бывает и так, что мы не хотим использовать тот ключ или сертификат, срок действия которого истек, иногда мы к ним прибегаем. Например, если пользователь забудет продлить срок действия ключа, то у него должна быть возможность увидеть, что ключ истек, и тогда проверить сертификат, а также продлить в таком случае срок действия ключа. Хотя gpg --list-keysне показывает истекшие ключи, при редактировании сертификата все-таки видны истекшие подключи, так, чтобы пользователь мог продлить срок их действия:

$ gpg --edit-key 93D3A2B8DF67CE4B674999B807A5D8589F2492F9Secret key is available.sec ed25519/07A5D8589F2492F9created: 2021-04-26 expires: 2024-04-26 usage: Ctrust: unknown    validity: unknownssb ed25519/1E2F512A0FE99515created: 2021-04-27 expires: never    usage: Sssb cv25519/8CDDC2BC5EEB61A3created: 2021-04-26 expires: 2024-04-26 usage: Essb ed25519/142D550E6E6DF02Ecreated: 2021-04-26 expired: 2021-04-27 usage: S[ unknown] (1). Alice <alice@example.org>


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

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

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

int i;for (i = 0; i < sk_count; i ++) {  rnp_key_handle_t sk;  err = rnp_key_get_subkey_at(key, i, &sk);  if (err) {    printf("rnp_key_get_subkey_at(%d): %x\n", i, err);    return 1;  }   uint32_t valid_till;  err = rnp_key_valid_till(sk, &valid_till);  if (err) {    printf("rnp_key_valid_till: %x\n", err);    return 1;  }   printf("#%d (%s) valid till %"PRIu32" seconds after epoch; ",         i + 1, desc[i], valid_till);   if (valid_till == 0) {    printf("invalid, skipping.\n");    continue;  }   uint32_t expiration_time;  err = rnp_key_get_expiration(sk, &expiration_time);  if (err) {    printf("rnp_key_get_expiration: %x\n", err);  } else {    printf("expires %"PRIu32" seconds after key's creation time.\n",           expiration_time);  }}


Результаты:

#1 (doesn't expire) valid till 1714111110 seconds after epoch; expires 0 seconds after key's creation time.#2 (expires) valid till 1714111110 seconds after epoch; expires 94670781 seconds after key's creation time.#3 (expired) valid till 1619527593 seconds after epoch; expires 86400 seconds after key's creation time.#4 (invalid sig) valid till 0 seconds after epoch; invalid, skipping.#5 (no sig) valid till 0 seconds after epoch; invalid, skipping.


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

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

Но, даже если игнорировать этот косяк, функция все равно странная. В OpenPGP ключ может быть действителен в течение нескольких периодов времени. Допустим, срок действия ключа истекает 1 июля, а пользователь продлевает его только с 10 июля. В период с 1 по 10 июля ключ был недействителен, а подписи, сгенерированные в это время, также должны считаться недействительными. Итак, что же должна возвращать рассматриваемая функция для такого ключа? Гораздо важнее, как пользователь такого API должен интерпретировать результат? Уместно ли вообще использовать такой API? (Да, я спрашивал.)

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

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

Заключение


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

Тем не менее, API RNP опасен. А Thunderbirdиспользуетсяв контекстах с критическими требованиями к безопасности. В интервью от 2017 годаМихал Рысьек Вознякиз Центра по исследованию коррупции и организованной преступности (OCCRP) четко сообщил, что на кону чьи-то жизни:

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

ИнтервьюсМихалом Рысьеком Вознякомиз Центра по исследованию коррупции и организованной преступности

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

Во-вторых, Thunderbird могла бы переключиться на иную реализацию OpenPGP. В наше время ихцелая кучана выбор. Лично я считаю, что Thunderbird следовало бы переключиться на Sequoia. Конечно же, я разработчик Sequoia, поэтому необъективен. Но дело здесь не в деньгах: мне платит фонд, а на свободном рынке мне предложили бы, пожалуй, вдвое больше, чем я зарабатываю сейчас. Я работаю ради того, чтобы защитить пользователей. Но, даже кроме API Sequoia и преимуществ реализации, Thunderbird в данном случае выигрывает и еще в одном отношении: мы уже заставили эту реализацию работать. Несколько недель назад мы выпустили Octopus, альтернативный бекенд OpenPGP для Thunderbird. У него не только функциональный паритет с RNP, но и есть ряд ранее недостававших фич, например, интеграция с gpg, а также залатаны некоторые бреши в безопасности и выполнено несколько нефункциональных требований.

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



VPS от Маклауд идеально подходят для разработки API.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Перевод Внутренности Linux как procselfmem пишет в недоступную для записи память

26.05.2021 12:10:08 | Автор: admin

Странная причудливость псевдофайла /proc/*/mem заключается в его пробивной семантике. Операции записи через этот файл будут успешными даже если целевая виртуальная память помечена как недоступная для записи. Это сделано намеренно, и такое поведение активно используется проектами вроде компилятора Julia JIT или отладчика rr.

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

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

Патчим libc с помощью /proc/self/mem


Как выглядит эта пробивная семантика? Рассмотрим код:

#include <fstream>#include <iostream>#include <sys/mman.h>/* Write @len bytes at @ptr to @addr in this address space using * /proc/self/mem. */void memwrite(void *addr, char *ptr, size_t len) {  std::ofstream ff("/proc/self/mem");  ff.seekp(reinterpret_cast<size_t>(addr));  ff.write(ptr, len);  ff.flush();}int main(int argc, char **argv) {  // Map an unwritable page. (read-only)  auto mymap =      (int *)mmap(NULL, 0x9000,                  PROT_READ, // <<<<<<<<<<<<<<<<<<<<< READ ONLY <<<<<<<<                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);  if (mymap == MAP_FAILED) {    std::cout << "FAILED\n";    return 1;  }  std::cout << "Allocated PROT_READ only memory: " << mymap << "\n";  getchar();  // Try to write to the unwritable page.  memwrite(mymap, "\x40\x41\x41\x41", 4);  std::cout << "did mymap[0] = 0x41414140 via proc self mem..";  getchar();  std::cout << "mymap[0] = 0x" << std::hex << mymap[0] << "\n";  getchar();  // Try to writ to the text segment (executable code) of libc.  auto getchar_ptr = (char *)getchar;  memwrite(getchar_ptr, "\xcc", 1);  // Run the libc function whose code we modified. If the write worked,  // we will get a SIGTRAP when the 0xcc executes.  getchar();}

Здесь /proc/self/mem используется для записи в две недоступные для записи страницы памяти. Первая содержит сам код, а вторая принадлежит libc (функции getchar). Последняя часть вызывает больший интерес: код записывает байт 0xcc (точка прерывания в приложениях под x86-64), который в случае своего исполнения заставит ядро предоставить нашему процессу SIGTRAP. Это буквально меняет исполняемый код libc. И если при следующем вызове getchar мы получим SIGTRAP, то будем знать, что запись была успешной.

Вот как это выглядит при запуске программы:


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

На видео:


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

Оборудование


На платформе x86-64 есть две настройки процессора, которые управляют возможностью ядра обращаться к памяти. Они применяются модулем управления памятью (MMU).

Первая настройка бит защиты от записи, Write Protect bit (CR0.WP). Из руководства Intel (том 3, раздел 2.5) мы знаем:

Защита от записи (16-й бит CR0). Если задана, он не даёт процедурам уровня супервизора записывать в защищённые от записи страницы. Если бит пуст, то процедуры уровня супервизора могут записывать в защищённые от записи страницы (вне зависимости от настроек бита U/S; см. раздел 4.1.3 и 4.6).

Это не позволяет ядру записывать в защищённые от записи страницы, что, естественно, по умолчанию разрешено.

Вторая настройка предотвращение доступа в режиме супервизора, Supervisor Mode Access Prevention (SMAP) (CR4.SMAP). Полное описание, приведённое в томе 3, в разделе 4.6, многословное. Если вкратце, то SMAP полностью лишает ядро способности записывать или читать из памяти пользовательского пространства. Это предотвращает эксплойты, которые наполняют пользовательское пространство зловредными данными, которые ядро должно прочитать в ходе исполнения.

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

Если CR0.WP не задан, то реализация /proc/*/mem в ядре действительно сможет бесцеремонно записывать в защищённую от записи память пользовательского пространства.

Однако CR0.WP задаётся при загрузке и обычно живёт в течение всего времени работы систем. В этом случае при попытке записи будет выдаваться сбой страницы. Это, скорее, инструмент для копирования при записи (Copy-on-Write), чем средство защиты, поэтому не накладывает на ядро никаких реальных ограничений. Иными словами, требуется неудобная обработка сбоев, которая не обязательна при заданном бите.

Давайте теперь разберёмся с реализацией.

Как работает /proc/*/mem


/proc/*/mem реализован в fs/proc/base.c.

Структура file_operations содержит функции обработчика, а функция mem_rw() полностью поддерживает обработчик записи. mem_rw() использует для операций записи access_remote_vm(). А access_remote_vm() делает вот что:

  • Вызывает get_user_pages_remote(), чтобы найти физический фрейм, соответствующий целевому виртуальному адресу.
  • Вызывает kmap(), чтобы пометить этот фрейм доступный для записи в виртуальном адресном пространстве ядра.
  • Вызывает copy_to_user_page() для финального выполнения операций записи.

Эта реализация полностью обходит вопрос о способности ядра записывать в незаписываемую память пользовательского пространства! Контроль ядра над подсистемой виртуальной памяти позволяет полностью обойти MMU, позволяя ядру просто записывать в свое собственное адресное пространство, доступное для записи. Так что обсуждения CR0.WP становится неактуальным.

Рассмотрим каждый из этапов:

get_user_pages_remote()

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

Вызывающий предоставляет контекст и с помощью флагов меняет поведение get_user_pages(). Особенно интересен флаг FOLL_FORCE, который передаётся mem_rw(). Флаг запускает check_vma_flags (логику проверки доступа в get_user_pages()), чтобы игнорировать запись в незаписываемые страницы и продолжить поиск. Пробивная семантика полностью относится к FOLL_FORCE (комментарии мои):

static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags){        [...]        if (write) { // If performing a write..                if (!(vm_flags & VM_WRITE)) { // And the page is unwritable..                        if (!(gup_flags & FOLL_FORCE)) // *Unless* FOLL_FORCE..                                return -EFAULT; // Return an error        [...]        return 0; // Otherwise, proceed with lookup}

get_user_pages() стоже соблюдает семантику копирования при записи (CoW). Если определяется запись в таблицу незаписываемой страницы, то эмулируется сбой страницы с помощью вызова handle_mm_fault, основного обработчика страничных ошибок. Это запускает соответствующую подпрограмму обработки копирования при записи посредством do_wp_page, которая при необходимости копирует страницу. Так что если записи через /proc/*/mem выполняются приватным разделяемым маппингом, например, libc, то они видимы только в рамках процесса.

kmap()

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

На 64-битной платформе x86 вся физическая память отображается через область линейного отображения виртуального адресного пространства ядра. В этом случае kmap() работает очень просто: ему лишь нужно добавить начальный адрес линейного отображения в физический адрес фрейма, чтобы вычислить виртуальный адрес, в который отображён этот фрейм.

На 32-битной платформе x86 линейное отображение содержит подмножество физической памяти, так что функции kmap() может потребоваться отобразить фрейм с помощью выделения highmem-памяти и манипулирования таблицами страницы.

В обоих случая линейное отображение и highmem-отображение выполняются с защитой PAGE_KERNEL, которая позволяет запись.

copy_to_user_page()

Последний этап выполнение записи. Это делается с помощью copy_to_user_page(), по сути memcpy. Это работает, поскольку целью является записываемое отображение из kmap().

Обсуждение


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

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

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

Заключение


Изучив подробности пробивной семантики в реализации /proc/*/mem мы можем отразить взаимосвязи между ядром и процессором. На первый взгляд, способность ядра писать в недоступную для записи память вызывает вопрос: до какой степени процессор может влиять на доступ ядра к памяти? В руководстве описаны механизмы управления, которые могут ограничивать действия ядра. Но при внимательном рассмотрении оказывается, что ограничения в лучшем случае поверхностны. Это простые препятствия, которые можно обойти.
Подробнее..

Перевод Смартфон на Linux как обстоят дела

02.05.2021 22:08:33 | Автор: admin
image


Прошлое


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

  • Openmoko: Финансовые вопросы,
  • Nokia (Maemo/Meego): Изменение корпоративной стратегии (новый генеральный директор),
  • HP (WebOS): Изменение корпоративной стратегии (новый генеральный директор),
  • Mozilla (Firefox OS): Изменение Корпоративной стратегии (смещение фокуса на IOT),
  • Canonical (Ubuntu Touch): Изменение корпоративной стратегии (смещение фокуса на облако и прибыльность).


Это, конечно, сильное упрощение и я признаю, что реальная история в каждом случае намного сложнее. Кроме того, трудно сравнивать нынешних игроков, PINE64 и Purism (и большое разнообразие комьюнити-проектов), с компаниями размером с Nokia, HP или даже Mozilla, или Canonical. Сравнение с Openmoko, скорее всего, работает лучше всего.

Openmoko

image

Openmoko стартовал в 2006 году как проект в более крупной компании, а затем был свернут. Их аппаратное обеспечение было немного устаревшим даже для того времени и продолжало устаревать (ARMv4, GPRS; Linux также работал на многих устройствах, первоначально работающих под управлением Windows Mobile в то время), и слабее, чем они надеялись. Они разработали свою ОС в открытом режиме, но самостоятельно (разработчики были наняты), и им пришлось переделывать ее три раза. Они не могли позволить себе сделать свой третий телефон, который исправил бы многие проблемы (3G, конкурентоспособный SoC, ...) по финансовым причинам, и решили вместо этого сделать WikiReader, а затем медленно испарились как компания.

Период с 2006 по 2009 год был трудным временем для начала работы на рынке смартфонов: и Apple, и Google (в рамках Open Handset Alliance) вышли на очень динамичный рынок в то время. Технология 3G сделала мобильные данные действительно стоящими, архитектура ARMv7 добавила вычислительную мощность, а емкостные сенсорные экраны навсегда изменили пользовательский интерфейс.

Сегодня


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

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

Purism

image

Purism разрабатывает свое собственное внутреннее программное обеспечение (software in house), они используют умный, минималистский, комьюнити-дружелюбный подход, который работает довольно хорошо. Беря как можно больше из вышестоящих проектов и работая с ними, а также разрабатывая мелочи, которые им нужно сделать:

  • libhandy (как библиотека, позволяющая сделать приложения GTK мобильными и конвергентными, теперь GNOME project),
  • mobile shell,, [виртуальная клавиатура(http://personeltest.ru/aways/source.puri.sm/Librem5/squeek доска)] и приложения для звонков и чатов,
  • аппаратное обеспечение их устройства Librem 5.


К сожалению, они не так хороши в доставке телефонов: недавно они объявили о дальнейших задержках и повышении цен.

PINE64

image

К счастью, PINE64 лучше справляется с доставкой своего телефона Pine. Предположительно, в 2009 году Openmoko было отгружено 10 000 телефонов, и уже более чем в 3 раза больше. Это важно, поскольку это означает, что существует относительно большое сообщество как разработчиков, так и пользователей, которые на самом деле имеют физическое оборудование для разработки и использования приложений для телефонов Linux.

Сообщество также является местом, где происходит разработка программного обеспечения с помощью PINE64, они производят устройства для софта от комьюнити. Это привело к увеличению активности в проектах сообщества, таких как Plasma Mobile (ранее Plasma Active) и многих новых небольших проектах, таких как Sxmo и бесчисленное множество других.

Означает ли это, что мы в безопасности?


Мы никогда не можем быть уверены, но ситуация намного лучше. Есть импульс! У нас есть аппаратное обеспечение, и я слышал слухи о нескольких предстоящих телефонах Linux на 2021 год, хотя я не уверен, что усилия не напрасны, учитывая текущую нехватку компонентов.

Даже если бы мы оставались ограниченными в аппаратном обеспечении PinePhone и Librem 5, postmarketOS и другие подвижки в этом направлении, такие как Droidian, способствуют внедрению новейшего линуксово-телефонного ПО на большее количество устройств.

Давайте также не будем забывать, что такие проекты, как Ubuntu Touch (подхваченный Ubport, когда Canonical бросил его) и Sailfish OS от Jolla продолжают работать.

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

Мы можем быть в безопасности, только если будем строить и взращивать это вместе!

Дополнительные материалы






Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Свой лунапарк TFTP с блэкджеком и С17

02.06.2021 12:04:43 | Автор: admin

image


Преамбула


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


Прошивка, в данном случае, это ...

набор бинарных файлов, размером до 70Мб, представляющих собой:


  • доработанный Das u-boot;
  • доработанное ядро GNU Linux;
  • файловая система, в которой содержится множество скомпилированных исполняемых файлов, конфигураций и пр., например, корневая файловая система GNU Linux;
  • кортеж комбинаций всего перечисленного;

Для идентификации файлов прошивки в системе документооборота и трекере используются MD5 хеши в файлах *.md5. В итоге имеем несколько деревьев в файловой системе для хранения прошивок.


Обновление ПО, а именно передача прошивки в изделие, происходит через протокол TFTP.


Постановка задачи


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


Результат


В результате, получился проект TFTP Firmware Server или просто server-fw.


Поддержка рекомендаций:


  • RFC1350 "THE TFTP PROTOCOL (REVISION 2)"
  • RFC2347 "TFTP Option Extension"
  • RFC2348 "TFTP Blocksize Option"
  • RFC7440 "TFTP Windowsize Option" (экспериментально)

Текущая версия сервера v0.2.0


Список опций сервера
$ bin/server-fw -hSimple tftp firmware server 'server-fw' v0.2 licensed GPL-3.0(c) 2019-2021 Vitaliy.V.Shirinkin, e-mail: vitaliy.shirinkin@gmail.comSome features:  - Recursive search requested files by md5 sum in search directory  - Use Firebird SQL server as file storage (optional requirement)Usage: bin/server-fw [<option1> [<option1 argument>]] [<option2> [<option2 argument>]] ... Possible options:  {-h|-H|-?|--help} Show help message  {-l|-L|--ip|--listen} {<IPv4>|[<IPv6>]}[:port] Listening address and port    (default 0.0.0.0:69)    Sample IPv4: 192.168.0.1:69    Sample IPv6: [::1]:69  {-s|-S|--syslog} <0...7> SYSLOG level flooding (default 6)  --lib-dir <directory> System library directory  --lib-name <name> Firebird library filename (default libfbclient.so)  --root-dir <directory> Server root directory  --search <directory> Directory for recursive search by md5 sum (may be much)  --fb-db <database> Firebird access database name  --fb-user <username> Firebird access user name  --fb-pass <password> Firebird access password  --fb-role <role> Firebird access role  --fb-dialect <1...3> Firebird server dialect (default 3)  --daemon Run as daemon  --retransmit <N> Maximum retransmit count if fail  --file-chuser <user name> Set user owner for created files (default root)  --file-chgrp <group name> Set group owner for created files (default root)    Warning: if user/group not exist then use root  --file-chmod <permissions> Set permissions for created files (default 0664)    Warning: can set only r/w bits - maximum 0666; can't set x-bits and superbits

Сервер позволяет настроить один основной каталог (опция --root-dir) и любое количество каталогов для поиска (опция --search). Каталоги для поиска работают только для GET запросов.
Для принимаемых файлов сервер позволяет настроить права (опция --file-chmod) и владение (опция --file-chuser и опция --file-chgrp).


Для запуска в качестве демона настройки располагаются в файле /etc/default/server-fw.


Соответствие настроек файла конфигурации и аргументов командной строки сервера
Настройки демона в файле /etc/default/server-fw Аргументы командной строки
IP --listen
SYSLOG --syslog
ROOT_DIR --root-dir
SERACH --search
FILE_CHUSER --file-chuser
FILE_CHGRP --file-chgrp
FILE_CHMOD --file-chmod

Обработка запросов GET осуществляется в следующем порядке:


  • если сервер распознает имя как md5-хеш, то ищет по хешу:
    в основном каталоге сервера
    в дополнительных каталогах поиска в порядке их задания в опциях
  • сервер ищет по имени файла:
    в основном каталоге сервера
    в дополнительных каталогах поиска в порядке их задания в опциях

При обработке запросов PUT, сервер складывает файл в основной каталог.


Пакет для установки для OC Ubuntu 18.04 (другие не проверялись) лежит здесь


Для самостоятельной сборки (GNU Linux):


$ mkdir server-fw$ git clone https://github.com/shvit/server-fw.git server-fw...$ cd server-fw$ make deb...$

Рефлексия


Сервер не поддерживает режим mail и netascii, вернее он должен их обрабатывать аналогично режиму octet (интересно узнать, а кто-то их испрользует? см. опрос).


Проверить опцию "windowsize" пока не получилось, но вроде должно работать.


Послесловие


Задачи на будущее:
  • использовать для хранения файлов реляционную СУБД
  • расширить поиск файлов по дополнительным метаданным
Подробнее..

Перевод Rust в ядре Linux

13.06.2021 16:20:24 | Автор: admin


Вболее ранней публикации компания Google объявила, что в Android теперь поддерживается язык программирования Rust, применяемый в разработке этой ОС как таковой. В связи с этим авторы данной публикации также решили оценить, насколько язык Rust востребован в разработке ядра Linux. В этом посте на нескольких простых примерах рассмотрены технические аспекты этой работы.

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

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

Поддержка Rust


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

Мы присоединились к организацииRust для Linux, где сообщество уже много сделало и продолжает делать для добавления поддержки Rust в систему сборки ядра Linux. Также нам нужно проектировать системы таким образом, чтобы фрагменты кода, написанные на двух языках, могли взаимодействовать друг с другом: нас особенно интересуют безопасные абстракции без издержек, которые позволяют коду Rust использовать функционал ядра, написанный на C, а также возможность реализовывать функционал на идиоматическом Rust, который можно гладко вызывать из тех частей ядра, что написаны на C.

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

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

Пример драйвера


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

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

> cat semaphore

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

> echo -n a > semaphore

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

> echo -n abc > semaphore

увеличивает счетчик на 3, поэтому следующие 3 считывания не приведут к блокированию.

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

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

Символьные устройства


Разработчику необходимо сделать следующее, чтобы реализовать на Rust драйвер для нового символьного устройства:

  1. Реализовать типажFileOperations: все связанные с ним функции опциональны, поэтому разработчику требуется реализовать лишь те, что релевантны для данного сценария. Они соотносятся с полями в структуре Cstruct file_operations.
  2. Реализовать типажFileOpenerэто типобезопасный эквивалент применяемого в C поляopenиз структурыstruct file_operations.
  3. Зарегистрировать для ядра новый тип устройства: так ядру будет рассказано, какие функции нужно будет вызывать в ответ на операции над файлами нового типа.

Далее показано сравнение двух первых этапов нашего первого примера на Rust и C:



Символьные устройства в Rust отличаются целым рядом достоинств по части безопасности:

  • Пофайловое управление состоянием жизненного цикла:FileOpener::openвозвращает объект, чьим временем жизни с того момента владеет вызывающая сторона. Может быть возвращен любой объект, реализующий типаж PointerWrapper, и мы предоставляем реализации дляBoxиArc, так что разработчики, реализующие идиоматические указатели Rust, выделяемые из кучи или предоставляемые путем подсчета ссылок, обеспечены всем необходимым.

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

    То есть, здесь мы опираемся на принятую в Rust систему владения, взаимодействуя с кодом на C. Мы обрабатываем написанную на C часть кода, которым владеет объект Rust, разрешая ему вызывать функции, реализованные на Rust, а затем, в конце концов, возвращаем владение обратно. Таким образом, коль скоро код на C, время жизни файловых объектов Rust также обрабатывается гладко, и компилятор обязывает поддерживать правильное управление временем жизни объекта на стороне Rust. Например, open не может возвращать в стек указатели, выделенные в стеке, или объекты, выделенные в куче, ioctl/read/writeне может высвобождать (или изменять без синхронизации) содержимое объекта, сохраненное вfilp->private_data, т.д.


    Неизменяемые ссылки: все ассоциированные функции, вызываемые между openиreleaseполучают неизменяемые ссылки наself, так как они могут конкурентно вызываться сразу множеством потоков, а действующие в Rust правила псевдонимов не допускают, чтобы в любой момент времени на объект указывало более одной изменяемой ссылки.

    Если разработчику требуется изменить некоторое состояние (а это в порядке вещей), то это можно сделать при помощивнутренней изменяемости : изменяемое состояние можно обернуть в MutexилиSpinLock(илиatomics) и безопасно через них изменить.

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


    Состояние для каждого устройства отдельно: когда экземпляры файлов должны совместно использовать состояние конкретного устройства, что при работе с драйверами бывает очень часто, в Rust это можно делать полностью безопасно. Когда устройство зарегистрировано, может быть предоставлен типизированный объект, для которого затем выдается неизменяемая ссылка, когда вызываетсяFileOperation::open. В нашем примере разделяемый объект обертывается вArc, поэтому объекты могут безопасно клонировать и удерживать ссылки на такие объекты.

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

    Таким образом исключается, что разработчик может получить неверные данные, попытавшись извлечь разделяемое состояние. Например, когда в C зарегистрировано miscdevice, указатель на него доступен вfilp->private_data; когда зарегистрированоcdev, указатель на него доступен в inode->i_cdev. Обычно эти структуры встраиваются в объемлющую структуру, которая содержит разделяемое состояние, поэтому разработчики обычно прибегают к макросуcontainer_of, чтобы восстановить разделяемое состояние. Rust инкапсулирует все это и потенциально проблемные приведения указателей в безопасную абстракцию.


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


    Операции над файлами: как упоминалось выше, разработчику требуется реализовать типажFileOperations, чтобы настраивать поведение устройства на свое усмотрение. Это делается при помощи блока, начинающегося с impl FileOperations for Device, гдеDevice это тип, реализующий поведение файла (в нашем случае это FileState). Оказавшись внутри блока, инструменты уловят, что здесь может быть определено лишь ограниченное количество функций, поэтому смогут автоматически вставить прототипы. (лично я используюneovimи LSP-серверrust-analyzer.)

    При использовании этого типажа в Rust, та часть ядра, что написана на C, все равно требует экземпляр struct file_operations. Контейнер ядра автоматически генерирует такой экземпляр из реализации типажа (а опционально также макросdeclare_file_operations): хотя, в нем и есть код, чтобы сгенерировать корректную структуру, здесь все равно всеconst, поэтому интерпретируется во время компиляции, и во время выполнения не дает никаких издержек.

    Обработка Ioctl

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



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

    Но от драйвера нетребуетсяэтим пользоваться. Если, например, он не использует стандартную кодировку ioctl, то Rust подходит к этому гибко: просто вызывает cmd.rawдля извлечения сырых аргументов и использует их для обработки ioctl (потенциально с небезопасным кодом, что требуется обосновать).

    Но, если в реализации драйвера действительно используется стандартный диспетчер, то ситуация значительно улучшается, поскольку ему вообще не придется реализовывать какой-либо небезопасный код, и:

    • Указатель на пользовательскую память никогда не является нативным, поэтому пользователь не может случайно разыменовать его.
    • Типы, позволяющие драйверу считывать из пользовательского пространства, допускают лишь однократное считывание, поэтому мы не рискуем получить баги TOCTOU (время проверки до времени использования). Ведь когда драйверу требуется обратиться к данным дважды, он должен скопировать их в память ядра, где злоумышленник не в силах ее изменить. Если исключить небезопасные блоки, то допустить баги такого класса в Rust попросту невозможно.
    • Не будет случайного переполнения пользовательского буфера: считывание или запись ни в коем случае не выйдут за пределы пользовательского буфера, которые задаются автоматически на основе размера, записанного в команде ioctl. В вышеприведенном примере реализацияIOCTL_GET_READ_COUNTобладает доступом лишь к экземпляру UserSlicePtrWriter, что ограничивает количество доступных для записи байт величиной sizeof(u64), как закодировано в команде ioctl.
    • Не смешиваются операции считывания и записи: в ioctl мы никогда не записываем буферы, предназначенные для считывания, и наоборот. Это контролируется га уровне обработчиков чтения и записи, которые получают только экземплярыUserSlicePtrWriterиUserSlicePtrReaderсоответственно.

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

    • Типы, применяемые для чтения и записи пользовательской памяти, не реализуют типажиSendиSync, и поэтому они (и указатели на них) небезопасны при использовании в другом контексте. В Rust, если бы разработчик попытался написать код, который передавал бы один из этих объектов в другой поток (где использовать их было бы небезопасно, поскольку контекст менеджера памяти там мог быть неподходящим), то получил бы ошибку компиляции.
    • ВызываяIoctlCommand::dispatch, логично предположить, что нам потребуется динамическая диспетчеризация, чтобы добраться до фактической реализации обработчика (и это повлечет дополнительные издержки, которых не было бы в C), но это не так. Благодаря использованию дженериков, компилятор сделает функцию мономорфной, что обеспечит нам статические вызовы функции. Функцию можно будет даже оформить внутристрочно, если оптимизатор сочтет это целесообразным.

    Блокировка и условные переменные

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



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

    А вот какими достоинствами обладает соответствующая реализация на Rust:

    • ПолеSemaphore::innerдоступно только во время удержания блокировки, при помощи ограничителя, возвращаемого функциейlock. Поэтому разработчики не могут случайно прочитать или записать защищенные данные, предварительно их не заблокировав. В примере на C, приведенном выше,countиmax_seenвsemaphore_stateзащищены мьютексом, но программа не обязывает держать блокировку во время доступа к ним. there is no enforcement that the lock is held while they're accessed.
    • Получение ресурса есть инициализация (RAII): блокировка снимается автоматически, когда ограничитель (innerв данном случае) выходит из области видимости. Таким образом, все блокировки всегда снимаются: если разработчику нужно, чтобы блокировка оставалась на месте, он может продлевать существование ограничителя, например, возвращая его; верно и обратное: если необходимо снять блокировку ранее, чем ограничитель выйдет из области видимости, это можно сделать явно, вызвав функцию drop.
    • Разработчики могут использовать любую блокировку, использующую типажLock, в состав которого, кстати, входят MutexиSpinLock, и, в отличие от реализации на C, это не повлечет никаких дополнительных издержек во время выполнения. Другие конструкции для синхронизации, в том числе, условные переменные, также работают прозрачно и без дополнительных издержек времени выполнения.
    • Rust реализует условные переменные при помощи очередей ожидания, предусмотренных в ядре. Благодаря этому разработчики могут пользоваться атомарным снятием блокировки и погружать поток в сон, не задумываясь о том, как это отразится на низкоуровневом планировщике функций ядра. В вышеприведенном примере на C вsemaphore_consumeвидим смесь семафорной логики и тонкого планирования в стиле Linux: например, код получится неправильным, еслиmutex_unlockбудет вызван доprepare_to_wait, поскольку таким образом можно забыть об операции пробуждения.
    • Никакого несинхронизированного доступа: как упоминалось выше, переменные, совместно используемые несколькими потоками или процессорами, должны предоставляться только для чтения, и внутренняя изменяемость пригодится для тех случаев, когда изменяемость действительно нужна. Кроме вышеприведенного примера с блокировками, есть еще пример с ioctl из предыдущего раздела, где также демонстрируется, как использовать атомарную переменную. В Rust от разработчиков также требуется указывать, как память должна синхронизироватьсяпри атомарных обращениях. В той части примера, что написана на C, нам довелось использовать atomic64_t, но компилятор не предупредит разработчика о том, что это нужно сделать.

    Обработка ошибок и поток выполнения

    В следующих примерах показано, как в нашем драйвере реализованы операцииopen,read иwrite:







    Здесь видны и еще некоторые достоинства Rust:

    • Оператор?operator: используется реализациями openиreadв Rust для неявного выполнения обработки ошибок; разработчик может сосредоточиться на семафорной логике, и код, который у него получится, будет весьма компактным и удобочитаемым. В версиях на C необходимая обработка ошибок немного замусоривает код, из-за чего читать его сложнее.
    • Обязательная инициализация: Rust требует, чтобы все поля структуры были инициализированы при ее создании, чтобы разработчик не мог где-нибудь нечаянно забыть об инициализации поля. В C такая возможность не предоставляется. В нашем примере с open, показанном выше, разработчик версии на C мог легко забыть вызвать kref_get(пусть даже все поля и были инициализированы); в Rust же пользователь обязан вызватьclone(повышающую счет ссылок на единицу), в противном случае он получит ошибку компиляции.
    • Область видимости при RAII: при реализации записи в Rust используется блок выражений, контролирующий, когда innerвыходит из области видимости и, следовательно, когда снимается блокировка.
    • Поведение при целочисленном переполнении: Rust стимулирует разработчиков всегда продумывать, как должны обрабатываться переполнения. В нашем примере с write, мы хотим обеспечить насыщение, чтобы не пришлось плюсовать к нашему семафору нулевое значение. В C приходится вручную проверять, нет ли переполнений, компилятор не оказывает дополнительной поддержки при такой операции.




    Облачные серверы от Маклауд быстрые и безопасные.

    Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

GentooArchLFS как путь в мир Linux

20.05.2021 10:09:10 | Автор: admin

Долго держался, не публиковал этот материал. Последний каплей стало вот это: Как Unix-way убивает десктопный Linux


Попалось мне обсуждение на habr Q&A habr Q&A
Очень понравился вот этот ответ от пользователя xolst9.


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


О себе работаю с linux более 10 лет, имеют опыт эксплуатации различных серверов(почта, файловые, DNS, маршрутизаторы, радиотрансляции, кластеры виртуализации и тд) на уровне провайдеров услуг(это несколько отличается от варианта "Фирма в 100 человек"), различных дистрибутивов( включая debian,ubuntu,gentoo, centos/oracle, arch), разные сферы деятельность (включая радиовещание и телеком), есть опыт работы как простым ведущим инженером, так и на руководящей должности. Есть что вспомнить, есть каким опытом поделиться.


Замечу, что каждый дистриб хорош на своем месте. Если мне потребуется создать сервер для длительной эксплуатации, то в 99% случаев я возьму Centos/RedHat. Нужно сделать VPN-сервер Zentyal. Настроить себе десктоп для работы mint или fedora, manjaro. Собрать небольшой дистрибутив/live flash с узкими задачами gentoo/arch. Создать кроссплатформенную прошивку начну собирать свой дистриб в buildroot.


Да, все это можно делать на ЛЮБОМ дистрибутиве, на самом деле. Будут вопросы в версиях пакетов, но в том или ином дистрибе есть свои плюсы и минусы.

PS Прекрасно знаю о сборках вроде zentyal/proxmox/esxi/прочие. Так или иначе они мне попадали в руки. Да, они позволяют упростить многие вещи, сэкономить время, дать возможность работать с ними людям, далёким от мира Linux. Но всё это до тех пор, пока вас устраивают их возможности и ограничения. Или пока что-то не сломалось.


PS Ещё я знаю про LFS/YOCTO/buildroot. Последний меня особенно интересует.


PSS И для любителей ткнуть в комментах да, я знаю, что такое проект GNU, что Linux это только ядро. Для желающих рекомендую прочитить вот это


Кто же такой Linux-профи


Меня давно удивляет ситуация, когда работодатель удивляется:"Вы специалист по Linux, а ни разу не настраивали EXIM4/NGINX/МОЙ_ОГРОМНЙ_СОФТ_НАПИСАННЙ_СТУДЕНТОМ_ЗА_ЕДУ?".


Так вот, следуют все таки отличать навыки работы в самой ОС и настройку ПРИКЛАДНОГО программного обеспечения на этой ОС. Да, linux популярен в хостингах. Но мне чаще приходилось делать почтовики и шлюзы с прокси, чем LAMP(или эквивалент). Отсюда вывод умение настраивать конкретный прикладной софт может быть преимуществом при выборе кандидата на вакансию.


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


Во-вторых, этот мир развивается. Кто-то вспомнит времена, когда web-сервер это однозначно Apache. Сейчас большинству (из личного опроса) в голову приходит NGINX. Всё больше вокруг меня postgresql, все меньше mysql.


Личный пример. Работал в одном интернет-провайдере, не маленьком. Потребовалось переделать DNS-систему. Не внутреннюю, а для предоставления услуг. Первое что приходит в голову bind! И тут же понимаю, что не всё так просто. Например удобный интерфейс управления зонами с возможностью разделения прав доступа.


Решил всё же посмотреть аналоги. И наткнулся на powerDNS. И сделал всё на нём. Почему потому что удобно:


  • Относительно простая настройка (справедливо и для bind)
  • Наличие "из коробки" веб-интерфейса для редактирования зон, с разделением прав доступа
  • Разделение на recursor и Authoritative серверы
  • Хранение зон в базе данных

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


Второй пример. Коллеги, имеющие опыт работы с Proxmox, смогли создать удивительно тормозящий кластер. У меня такого опыта не было, я работал только с KVM + qemu + libvirt. Замечу, что внутри proxmox находится правильно, kvm + qemu + libvirt. Тем не менее, проблему нашел и решал именно я. Оказалось, что никто не прочитал отличие паравиртуализированного драйвера от эмуляции IDE. А для дальнейшего перевода виртуалок на паравиртуализированные драйверы потребовалось уметь работать с fstab/grub, тк после смены драйвера виртуалки перестали загружаться(тот случай, когда в fstab и в загрузчике использовались привязки не по UUID/LABEL, а по именам устройств. И когда быстрее было загрузиться с live-диска и быстро всё исправить).


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


Вывод 2. Не так важен дистрибутив, как голова и прямые руки. Умеешь работать в командной строке, понимаешь устройство ОС семейства linux, и адаптация к дистрибутиву перестаёт быть для тебя проблемой. Мне самому приходилось переходить из мира gentoo в мир redhat, что было не сложно. Переход в мир debian/ubuntu также не занял вопросов. Найти почему есть специфические проблемы с прошивке российской разработки тоже не проблема.


Вывод 3. Уметь работать в консоли всё таки надо. Не попадалось мне ещё панели управления или иного gui, имеющих мощность и гибкость командной строки. Да, можно здорово упростить себе жизнь, используя gui (мне нравится, как в zentyal построено управление openvpn). Но консоль никто не отменял.


Gentoo как источник технических навыков


Начну с самого простого чему учится новичек, решивший отвоить Linux с нуля. Как гласит старая шутка:"Инструкцию по gentoo надо ещё склеить".
Большинство дистрибутивов всё же имеют инсталлятор, утрированно: "Да да далее установить". Так вот, Gentoo не имеет инсталлятора. Уже с моменты начала установки есть шанс получить новые знания. Причем базовые, без которых сложно (а может и невозможно) двигаться дальше. Не могу представить себе linux-профи, не умеющего форматировать разделы в командной строке. Или не освоившего chroot.


Итак, вот краткий список навыков, получаемых при установке Gentoo:


  • Общие навыки работы в командной строке копирование, скачивание файлов, навигация по дискам и тд
  • Проверка контрольных сумм
  • Понимание параметров загрузки ядра
  • управление пользователями/группам, специальные группы
  • понимание модулей ядра, их загрузки
  • настройка сети(статика/dhcp/wifi)
  • маршрутизация в сетях
  • работа с разделами дисков
  • создание различных файловых систем(в тч LVM/SWAP/BTRFS)
  • дата/время (вручную и ntp, часовые пояса)
  • работа с переменными (объявление, использование, export)
  • понимание основ сборки пакетов флаги сборки, настройка параметров компилятора
  • монтирование на лету (в тч особых систем вроде /dev /sys /proc)
  • настройка fstab
  • chroot
  • настройка локали
  • конфигурирование и сборка ядра
  • настройка логирования
  • cron
  • настройка удаленного доступа
  • установка/настройка загрузчика( в тч EFI)

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


Maquefel:


"Исключения конечно есть, люди которые дальше Ctrl+C, Ctrl+V не пошли и находятся в состоянии "ждем ebuild'ов", но с ростом количества "простых" дистрибутивом, такие уже в большинстве своём исчезли с нашего горизонта."

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


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


Результат на выходе получается человек, который попробовал много чего. А ведь он ещё не начал устанавливать прикладной софт и графику. Сделает и это весьма не плохо освоит новые подсистемы ОС.


Замечу, что установка Arch в целом похожа (на мой взгляд), только без компиляции. И даёт примерно такой же набор навыков.


У обоих дистрибутивов отличная документация. Особенно у Arch, субъективно она мне больше нравится.


Психологический аспект


Невероятно, на факт мне часто встречались/встречаются так называемые "linux-специалисты", неуверенно работающие в командной строке. Или вовсе её боящиеся.


Боязнь эта вполне объяснима и понятно нет устойчивых навыков работы в командной строке. Что называется не набита рука! Представьте себе лётчика, который привык летать только на автопилоте. Вы предпочтете лететь с ним или с тем, кто в добавок умеет уверенно управлять вручную? В результате типичные ошибки:


  • Отрицание возможности выполнения задачи, потому что не знают как
  • Ошибки при выполнении команд (использование неверных ключей, неверный порядок операндов)
  • Использование избыточных средств (например, искать/писать скрипт на питоне там, где достаточно grep)

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


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


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


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


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


Gentoo vs Enterprise


Снова вернусь к истоку, к Q&A. Прозвучали фразы вроде "не корпоративный дистриб". В целом согласен, сам предпочитал использовать Centos.
Но сейчас будет подрыв.
Так вот, есть ситуации, когда gentoo используется в суровом Enterprise. Пример разработка. Gentoo отличный инструмент для разработчика:


  • Свежие версии пакетов
  • Всегда свежий компилятор (пользователи RH/Centos/Oracle поймут)
  • Возможность собрать свой дистрибутив/прошивку с минимальным и достаточным функционалом
  • Восстановление работы конкретного сервера с live-диска.

Как видно, gentoo может пригодиться в больших компаниях. Скажите, что специфичным образом? А что не специфичный? Почтовик или паблик-шара на samba? :-D


И снова напомню про нештатные/аварийные ситуации. Или про "черные ящики"('мы не знаем что там и как, но без него падает что-то важное'), которыми грешат крупные фирмы с историей.


Почему я предпочту гентушника в коллегах


Исходя из выше описанного, при прочих равных гентушник будет иметь преимущество в моих глазах.
Как минимум он не испугался и освоил не слабый handbook.
Он умеет работать в консоли, что критически важно для linux-администратора/инженера/архитектора.
Он понимает основы компиляции и сборки пакетов, что иногда крайне важно (почитайте про тесты SPEC CPU, например spec 2006).


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


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


"Поколение Ubuntu"


Впервые этот термин был мной услышан в 2012 году в ходе собеседования. Сейчас я им активно пользуюсь.
Maquefel:


Вообще за все надо платить. Линуксоиды, со стажем 15+ лет, а помните, как мы занимались популяризацией GNU/Linux? Вот мы и добились своего, только почему то мы думали, что адепты проникнуться духом и путем Linux/UNIX и все станет лучше, богаче, проще. А нет, адепты просто притащили к нам философию Microsoft, ничего не делать, ждать готового, изобретать громадные комбайны, не читать документацию."

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


- Вот выдержка из лога. Выведите список неуспешных попыток входа по SSH

И вот лучшие из ответов(кроме попыток считать вручную):


  • Я бы написал на питоне, там что-то было для работы с SSH (ну да, с текстовым файлом)
  • Может это можно сделать через awk? но я не уверен (тепло, но написать не смог)
  • Есть grep, но я не помню как им пользоваться (оставлю без комментариев)

Ещё пример:


- с Systemd сталкивались?- да,  редкостное...(в общем не нравится)- а что именно не так, что мешает?- ну...так пишут

В данном случае я не агитирую за systemd. У него есть и плюсы, и минусы.


- Вам говорят, что linux сервер тормозит. Что будете делать?- Гляну загрузку проца, оперативки.- Как?- эээ...не знаю/не помню- ок, оперативки много, процессор показывает высокую загрузку (load averange)- Значит процессоров не хватает!- А может тормозят диски?(намёк на  CPU utilization и iowait)- Нет, едва ли. - И всё же, если диски. Как посмотреть?- не знаю.

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


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


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


Для написания статьи использовался следующий инструментарий:


  • Visual studio code (да, в нем достаточно удобна работать с markdown)
  • vim
  • git

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

Подробнее..

Есть ли жизнь после CentOS?

28.04.2021 10:12:44 | Автор: admin

Под конец и без того нелегкого 2020 года Red Hat преподнесла всем поклонникам CentOS весьма неожиданный подарок, объявив о радикальном сокращении EOL восьмой версии дистрибутива и последующем отказе от дальнейшего развития проекта. Пользователи операционной системы, на протяжении многих лет занимавшей третье место по популярности в мире, оказались на распутье. Что выбрать в такой ситуации? Стать вечным бета-тестером, перейдя на CentOS Stream? Выделить бюджет на покупку лицензии Red Hat Enterprise Linux? Или быть может попробовать одно из конкурирующих решений?

Хроника пикирующего дистрибутива: зачем убили CentOS?


В июле 2019 года руководство Red Hat объявило об официальном урегулировании всех формальностей, связанных с продажей бизнеса корпорации IBM. Финальная сумма сделки составила около 34 миллиардов долларов США, или $190 за каждую проданную акцию (при том, что на момент объявления о сделке бумаги торговались по $116). Как обычно и бывает в таких случаях, Red Hat клятвенно заверили сообщество, что присоединение к знаменитому IT-гиганту поможет привлечь дополнительные инвестиции и ресурсы, что даст возможность компании достичь новых вершин, и донести технологии Red Hat до еще более широкой аудитории, чем прежде. В свою очередь, лидеры разработки Fedora и CentOS поспешили заявить, что цели и модель управления проектами останутся неизменными, а Red Hat, как и ранее, продолжит активно участвовать в их развитии. И компания действительно сдержала свое слово, но лишь наполовину.


Кто-то всерьез полагал, что после поглощения Red Hat ничего не изменится?

В начале декабря 2020 года компания объявила о прекращении поддержки CentOS операционной системы, фактически являющейся бесплатной альтернативой Red Hat Enterprise Linux, великодушно предложив всем пользователям данного дистрибутива перейти на CentOS Stream. Больше всего эта новость ошарашила тех, кто уже успел мигрировать на CentOS 8: хотя поддержка восьмой версии операционной системы должна была завершиться лишь 31 мая 2029 года, первоначальный EOL ограничили 31 декабря 2021 года (то есть, обещанные 10 лет волшебным образом превратились в 2 года). Тем, кто использует CentOS 7, повезло значительно больше: сроки поддержки семерки оставили неизменными, так что операционная система продолжит получать критически важные апдейты до 2024 года. Впрочем, кто знает, что придет в голову Red Hat в ближайшем будущем?

Согласно официальной позиции компании, модель взаимодействия между пользователями и разработчиками программного обеспечения, которую олицетворяет собой CentOS, в современных реалиях уже не актуальна. И CentOS Stream является на сегодняшний день наиболее оптимальным и сбалансированным дистрибутивом, сочетая в себе инновации Fedora и стабильность Red Hat Enterprise Linux. Именно стремясь удовлетворить потребности сообщества, Red Hat приняли решение всецело сосредоточиться на поддержке данной версии дистрибутива, сделав CentOS Stream основным центром инноваций экосистемы RHEL.

Однако сообщество подобную заботу не оценило: на фоне новостей о прекращении дальнейшего развития оригинального дистрибутива CentOS стал стремительно терять позиции, модераторы тематического сабреддита добавили к прежнему названию приписку Corporate-driven (вместо community-driven), Not suitable for Enterprise, закрепив тред RIP CentOS, 20042020, журнал ZDNet, принадлежащий CBS Interactive, открыто высказал мнение, что отказ от поддержки CentOS является ничем иным, как частью продвижения RHEL, в сети появился лэндинг довольно ехидного содержания centos.rip (создание которого, кстати, приписывают Oracle главному конкуренту Red Hat, разрабатывающему собственный RH-based дистрибутив), а на change.org была опубликована петиция, авторы которой обратились к Red Hat с просьбой продолжить разработку CentOS в прежнем формате.


Кем бы ни были авторы centos.rip, ехидства им не занимать

Реакция Red Hat не заставила себя долго ждать: компания прислушалась к мнению сообщества, изменив правила использования Red Hat Enterprise Linux для разработчиков. Ранее в рамках программы Red Hat Developer действовало правило один разработчик одна лицензия, а сам дистрибутив можно было разворачивать только в локальном окружении. С 1 февраля 2021 года в программе могут участвовать целые команды, количество лицензий увеличилось с 1 до 16, к тому же новые условия EULA допускают установку ОС в инстансах публичных cloud-сервисов. Но, разумеется, только в целях разработки программного обеспечения: использование дистрибутива в продакшене обновленная лицензия по-прежнему не предусматривает.

Таким образом Red Hat прозрачно намекнула, что не намерена отклоняться от ранее избранного курса, чего и следовало ожидать. Вполне очевидно, что несмотря на первоначальное обещание сохранить созданную компанией модель разработки и поддерживать сложившуюся вокруг ее проектов экосистему, IBM увидела в CentOS прямую угрозу продажам RHEL. Отказ же от старой доброй CentOS позволяет убить не то что двух, а сразу трех зайцев:

  1. Часть пользователей из числа представителей крупного бизнеса наверняка предпочтет перейти на RHEL, что обеспечит дополнительную прибыль;
  2. Те, кто рискнет мигрировать на CentOS Stream, пополнят ряды бета-тестеров, а это поможет эффективнее обкатывать новые технологии и быстрее выявлять уязвимости в новых версиях пакетов;
  3. Освободившиеся ресурсы можно использовать для дальнейшего развития Red Hat Enterprise Linux, оставаясь в рамках прежнего бюджета на разработку.

Мотивы IBM понятны: даже для такой крупной корпорации 34 миллиарда долларов очень серьезная сумма, а предпринятые шаги помогут частично компенсировать затраты на покупку Red Hat уже в обозримом будущем за счет крупных корпоративных заказчиков. Но что же делать небольшим компаниям, которые попросту не могут себе позволить приобретение коммерческой лицензии RHEL?

Куда пойти, куда податься?


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

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

1. CentOS Stream



Пользователям CentOS 8 Red Hat предлагает мигрировать на CentOS Stream. Всерьез рассматривать перспективы использования данного дистрибутива в продакшене не получается, ведь если даже в релизные версии операционных систем периодически просачиваются баги и уязвимости, зачастую носящие критический характер, то что говорить о непрерывно обновляемой бете? Хотя чисто технически влиться в Поток предельно просто. Для этого достаточно выполнить в терминале всего три команды:

  1. Подключаем репозиторий CentOS Stream

# dnf install centos-release-stream

  1. Указываем новый репозиторий в качестве дефолтного

# dnf swap centos-{linux,stream}-repos

  1. Синхронизируем установленные пакеты

# dnf distro-sync

Поскольку отличия пакетной базы CentOS 8 и CentOS Stream на сегодняшний день минимальны, процедура миграции с вероятностью 99% пройдет безболезненно. Тем не менее, если речь идет не о нуждах разработки, а о комплексной IT-инфраструктуре, на которой завязана львиная доля бизнес-процессов предприятия, стоит присмотреться к чему-то более надежному. Например, к тому же Oracle Linux.

2. Oracle Linux



Фактически Oracle Linux представляет собой клон RHEL. Данная операционная система полностью совместима с CentOS на уровне двоичного кода. К тому же в декабре 2020 года компания Oracle представила удобный скрипт для миграции продакшн-систем, автоматически заменяющий специфичные для CentOS пакеты на эквивалентные из поставки Oracle Linux, и поддерживающий 6-ю, 7-ю и 8-ю версии ОС. Интересной особенностью данного скрипта является функция резервного копирования затронутых файлов, так что при возникновении любых проблем вы сможете откатить все внесенные изменения.

Разумеется, не могло обойтись и без некоторых ограничений:

  1. Скрипт обрабатывает только основные репозитории операционной системы. Подключение внешних репозиториев вроде EPEL для получения обновлений ранее установленных пакетов придется производить вручную;
  2. Совместимость с пакетами, полученными из сторонних репозиториев, не гарантируется. В частности, Oracle указывает на возможные конфликты, вызванные наличием файла /etc/oracle-release;
  3. После миграции могут перестать работать пакеты, использующие сторонние модули ядра и/или модули ядра с закрытым исходным кодом (к таковым относятся, например, коммерческие антивирусные приложения);
  4. Скрипт не поддерживает системы, в которых используются сторонние инструменты централизованного управления наподобие Foreman, Spacewalk или Uyni.

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

  1. Дистрибутив полностью бесплатен и может использоваться в коммерческих проектах без каких-либо ограничений или дополнительного лицензирования;
  2. Бесплатная и коммерческая версии Oracle Linux отличаются друг от друга только наличием технической поддержки от специалистов корпорации, сами же дистрибутивы полностью идентичны и используют единый репозиторий, одновременно получая все выходящие обновления;
  3. Изменения в ядре Unbreakable Enterprise Kernel публикуются в Git-репозитории с разделением на отдельные патчи и детализацией внесенных изменений, что повышает прозрачность и предсказуемость поведения системы при ее обновлении;
  4. Oracle Linux поддерживает высокопроизводительную сетевую файловую систему Oracle Cluster File System 2 (OCFS2), позволяющую создавать разделяемые хранилища, используемые одновременно несколькими Linux-системами, что делает Oracle Linux весьма удобной для построения масштабируемых веб-серверов, кластерных баз данных, виртуализации и других аналогичных сценариев.

В свете всего перечисленного переход на Oracle Linux кажется наиболее правильным и логичным шагом. Однако если рассматривать перспективы миграции на данный дистрибутив в историческом контексте, все становится уже совсем не так однозначно. Дело в том, что и Oracle Linux, и CentOS в их современном виде являются плодом ожесточенной конкуренции между Red Hat и Oracle.

Еще в октябре 2006 года, на фоне анонса инициативы Unbreakable Linux, в рамках которой Oracle фактически предложила пользователям копию RHEL за вдвое более низкую цену, акции Red Hat упали на 28%, что вынудило компанию перейти к активным действиям. Ответным шагом стал перевод RHEL 6 на монолитное ядро, что фактически блокировало возможность использования наработок компании в сторонних дистрибутивах, сделав анализ примененных патчей излишне трудоемким. Технический директор Red Hat, Брайан Стивенс, тогда заявил:

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

Реакция Oracle не заставила себя долго ждать: компания открыла неограниченный доступ ко всем обновлениям Oracle Linux, включая errata и оперативные патчи безопасности, сформировав Git-репозиторий для отслеживания всех изменений, и начав весьма успешно переманивать пользователей CentOS, агитируя их переходить на бесплатный продукт enterprise-класса.

После этого Red Hat не оставалось ничего, кроме как возглавить разработку CentOS: в январе 2014 года компания объявила о начале прямого финансирования проекта, получив права на владение всеми товарными знаками.


Данный шаг позволил устранить основной недостаток операционной системы непредсказуемость процесса разработки. Инциденты вроде неожиданной пропажи Лэнса Дэвиса, одного из основателей проекта, и многомесячные перерывы в выпуске обновлений не добавляли CentOS популярности, заставляя сообщество склоняться в сторону более надежной альтернативы. Устранив перечисленные риски, Red Hat поставила под сомнение целесообразность использования Oracle Linux: и правда, в чем смысл работать с клоном, если можно получить качественный, полностью бесплатный продукт от создателей оригинальной RHEL, что называется, из первых рук?

На протяжении последующих 6 лет в противостоянии Red Hat и Oracle сохранялся паритет. Теперь же, когда столь сильный конкурент сошел с дистанции, дальнейший вектор развития Oracle Linux становится непредсказуем. Можно с уверенностью утверждать, что ближайшие 46 лет политика компании в отношении лицензирования операционной системы останется неизменной: сейчас для Oracle куда важнее расширить инсталляционную базу за счет бывших пользователей CentOS, нежели получить сиюминутную прибыль. В отсутствие же сильного конкурента корпорация может начать, по примеру Red Hat, обкатывать обновления на пользователях бесплатной версии ОС. Подобный сценарий вполне реален, если только в ближайшие годы на рынке не появится достойная альтернатива.

3. AlmaLinux



И таковой вполне может оказаться AlmaLinux. Появление данного проекта стало ответной реакцией CloudLinux на преждевременное прекращение поддержки CentOS 8. Компания планирует выделять на разработку операционной системы по 1 миллиону долларов в год из собственных доходов.

AlmaLinux следует базовым принципам CentOS: дистрибутив формируется путем пересборки пакетной базы RHEL, сохраняя бинарную совместимость с оригинальной операционной системой, что позволяет пользователям CentOS 8 легко и безболезненно перевести IT-инфраструктуру на новые рельсы. В дальнейшем развивать проект планируется с привлечением сообщества, при этом будет использоваться модель управления, аналогичная той, что применяется при разработке Fedora. Для этих целей создана отдельная некоммерческая организация AlmaLinux OS Foundation.

Первый стабильный выпуск AlmaLinux увидел свет 30 марта этого года. Дистрибутив бесплатен для всех категорий пользователей, включая корпоративных. В настоящее время для загрузки доступны сборки для архитектуры x86_64, однако в скором времени разработчики обещают подготовить ARM-версии операционной системы. Дистрибутив полностью идентичен по функциональности с RHEL 8.3, которая лежит в его основе, за исключением отсутствия ряда специфических для Red Hat Enterprise Linux пакетов (например, redhat-*, insights-client и subscription-manager-migration*). CloudLinux обещает поддерживать текущую версию AlmaLinux до 2029 года. Как и в случае с Oracle Linux, для простой и прозрачной миграции с CentOS 8 разработчики операционной системы подготовили удобный автоматизированный скрипт.

Если рассуждать о перспективах данного проектах, то AlmaLinux имеет неплохие шансы занять вакантное место CentOS, благо команда CloudLinux, насчитывающая на сегодняшний день более сотни IT-специалистов, обладает вполне достаточным опытом разработки и сопровождения RH-based проектов, ведь флагманский продукт компании основан на пакетной базе RHEL. Ребята уже наглядно продемонстрировали, на что способны, представив первый стабильный билд операционной системы спустя всего 4 месяца после анонса, и если так пойдет и дальше, они вполне смогут составить достойную конкуренцию Oracle.

4. Rocky Linux



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

  • Разработку Rocky Linux возглавляет компания Ctrl IQ стартап основателя CentOS Грегори Курцера;
  • Компания заручилась поддержкой инвесторов в лице венчурного фонда IAG Capital Partners и одного из крупнейших поставщиков гипермасштабируемых систем хранения данных OpenDrives, по итогам переговоров с которыми на разработку операционной системы удалось привлечь $4 млн;
  • В число ключевых спонсоров проекта входят корпорация Amazon, предоставившая команде Ctrl IQ необходимые для разработки и сборки дистрибутива вычислительные мощности в облаке AWS, и MontaVista Software, имеющая более, чем 20-летний опыт разработки программного обеспечения с открытым исходным кодом, ориентированного на нужды корпоративных клиентов.

Rocky Linux позиционируется, как полностью бесплатная RH-based операционная система, демонстрирующая уровень стабильности enterprise-класса и пригодная для использования в рабочих проектах. Согласно заявлению Курцера, новый дистрибутив, основанный на исходном коде RHEL, продолжит традиции оригинального CentOS, а его разработка останется полностью прозрачна для сообщества, которое и будет определять генеральный вектор развития проекта. При этом Ctrl IQ не рассматривает Rocky Linux в качестве источника дохода. Компания планирует зарабатывать на предоставлении корпоративным заказчикам инфраструктуры для высокопроизводительных вычислений под ключ, направляя часть вырученных средств на базовые расходы по разработке и юридической поддержке проекта.

Rocky Linux находится лишь в начале своего пути, поэтому говорить о надежности новой операционной системы пока еще рано. Как рано рассуждать и о том, насколько конкурентоспособным окажется бизнес Ctrl IQ, ведь из четырех компонентов технологического стека HPC в настоящее время можно пощупать лишь Warewulf набор инструментов для управления высокопроизводительными кластерами Linux. Впрочем, ждать осталось совсем недолго: выход первого релиз-кандидата Rocky Linux запланирован на 30 апреля 2021 года.


Время для принятия взвешенного решения все еще есть. Даже если вы используете CentOS 8, оставшиеся месяцы вполне достаточный срок для того, чтобы определиться с выбором дистрибутива и спланировать стратегию миграции IT-инфраструктуры на новую платформу. Те же, кто остался на CentOS 7, имеют отличную возможность оценить, на что в действительности способны команды разработчиков AlmaLinux и Rocky Linux и сделать осознанный выбор в пользу той или иной операционной системы, опираясь не на обещания, а на реальные кейсы.

Если же перечень используемого вами программного обеспечения ограничивается стандартными пакетами, входящими в состав практически каждого дистрибутива Linux, возможно есть смысл рассмотреть и иные альтернативы, например разрабатываемую сообществом Debian или Ubuntu, успешно развиваемую частной британской компанией Canonical на протяжении вот уже 16 лет и, на сегодняшний день являющуюся самой популярной UNIX-подобной операционной системой.



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

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Вкусовщина Я не могу работать на MacBook Pro 16

05.06.2021 00:21:43 | Автор: admin

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

Я - простой разработчик, пишу на Java/Kotlin, бэкэнд. Ранее работал на Windows (было давно и не продолжительное время), потом пересел на Linux по ряду причин. Безвылазно на Linux 7 лет, вообщем с системой работал около 16. В один прекрасный день мне довелось устроиться в компанию, где Linux для пользователей не то чтобы под запретом, скорее просто нежелательно (читать с приставкой - крайне). Что из этого вышло....

Долгое время я был свободным человеком, почти без обязательств, и выбор на чем работать был только за мной, ну или за моим карманом и жадностью. Последний сетап был уж достаточно странным: Dell Precision 7540 (Xeon E-2286M, 48GB RAM, 2x512GB ssd m.2, 256GB SSD m.2, GF T2000), 2 монитора Dell p2418d, док станция Dell TB16, клавиатура Apple Magic Keyboard with numeric, мышь Apple Magic Mouse 2. Вообщем этакие набор кулл хацкера с комплексом ущербного мозга (ну может хоть сетап будет помогать писать гомнокод получше).

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

Мне очень нравилась по качеству клавиатура от Apple, пальцы летали на ней, когда все продавал даже думал оставить ее. Зато вот убунту меня иногда раздражала какими-либо проблемами: то блютуз глючит, то апдейт прилетит после которого кучка ошибок, ну а про переустановки я уже упоминал, благо заготовки были на такие случаи со временем. В трудные моменты я подумывал о смене своего Dell на MacBook. Мотивация была такая: этож тот же *nix, только стабильней, комфортабельней и вообще этожэппл.

В один прекрасный день я решил, что хватит работать удаленно и нужно уезжать из своего городка в цивилизацию. Распродал все, приехал в другой город, устроился в компанию и мне выдали MacBook 16 Pro. Думаю: вот оно походу пришло, что хотел, теперь-то я буду работать на нормальной системе и клава вроде такая же..... но не тут-то было.

Первые дни знакомства с маком меня уже огорчили, я понял что нужно привыкать к новой реалии, к новым хоткеям и новому софту (ну конечно oh-my-zsh там есть, но не им единым).

Оценил: Экран, тачпад, колонки. Эти компоненты просто вышка без всяких. Лучше пока еще не видел в ноутбуках.

Через пару дней уже завыл, так как хоткеи ну совсем не понимал, а вот эти Fn и ~..... ну ладно Fn, но ~ засунуть в такое интересное место + Enter: мизинец постоянно нажимал что угодно, но не Enter. Ну ладно думаю, привыкну... Прошла неделя, вроде даже научился печатать на этой клаве, но не Intelleji Idea. Да, я понимаю, что там другая раскладка, но вот я постоянно нажимал первое время Command + Q.... ну вы поняли что происходило и как печально было мое лицо.

Еще через время начал замечать, что чего-то не хватает и понял: открыть меню File я не могу, отобразить ветки Git тоже... Нужно значит что-то настраивать под себя, т.е. добавлять хоткеи. Ну ладно может быть и добавил бы, но вот беда, чтобы нажать условный F10, нужно нажать Fn, а делит... ну вы знаете как он устроен. Т.е. вместо 3 клавиш нужно нажимать 4, некоторые сочетания не просто выполнить даже если посещал пальцевую гимнастику (если такая есть). Молчу уже про конфликты некоторых хоткеев с системными. Но главный поинт такой, что вместо того, чтобы эффективно работать я должен воевать со своими пальцами и чудесными сочетаниями клавиш в виду отсутствия F, del, PgDown, PgUp и т.д.

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

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

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

Но как разработчик я себя чувствую лучше на Linux, правда вот клавиатуры (Apple Magic Keyboard with num pad) не хватает (хотя на моем текущем Honor-е клава по нажатию хороша), ну и тач бы еще маковский.

Подробнее..

Баги ради науки как Университет Миннесоты внедрял баги в код Linux

16.06.2021 10:13:29 | Автор: admin


Грег Кроа-Хартман, ответственный за сопровождение стабильных релизов ядра, в начале апреля запретил Университету Миннесоты (УМ) вносить изменения в код Linux. Университет Миннесоты по-видимому, всё это время сознательно вносил вредоносные изменения в код проекта. Развязка наступила после патча к механизму авторизации NFSv4, который не прошел проверку двух профильных разработчиков. Так выглядел тот самый патч.

diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.cindex 5f42aa5fc612..eb52eebb3923 100644--- a/net/sunrpc/auth_gss/auth_gss.c+++ b/net/sunrpc/auth_gss/auth_gss.c@@ -848,7 +848,8 @@ gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)warn_gssd();gss_release_msg(gss_msg);}-gss_release_msg(gss_msg);+if (gss_msg)+gss_release_msg(gss_msg);}static void gss_pipe_dentry_destroy(struct dentry *dir,--2.25.1

Ирония однако заключается в том, что спорный проект, который привел к потере доверия к Университету Миннесоты, изначально был направлен на повышение безопасности Linux. В университете с августа 2020 г ассистент профессора Kangije Lu и аспирант Qjushi Wu работали над статьёй под названием On the Feasibility of Stealthily Introducing Vulnerabilities in Open-Source Software via Hypocrite Commits. Под термином Open-Source Software имеется в виду ядро Linux.

Авторам Hypocrite Commits удалось внести в код ядра изменения, содержащие в скрытом виде уязвимость вида обращение после освобождения памяти. Статья была принята на 42-м симпозиуме IEEE по безопасности и конфиденциальности, но уже 26-го апреля Qjushi Wu и Kangije Lu отозвали своё участие в мероприятии. В письме они выражают своё сожаление по поводу случившегося и просят прощения комитету за доставленные неудобства.

We now understand that it was inappropriate and hurtful to the community to make it a subject of our research, and to waste its effort reviewing these patches without its knowledge or permission.

Исследование было поддержано со стороны National Science Foundation и содержало гарантии того, что ни один баг не попадет в исходный код Linux. Тем не менее, именно это и произошло в итоге. Однако миннесотовцы не остановились на достигнутом и решили пойти дальше, стремясь в этот раз протащить новую серию уязвимых патчей под видом нового статического анализатора кода. Развязка наступила 6-го апреля, когда Грег Кроа-Хартман назвал вещи своими именами.

Please stop submitting known-invalid patches. Your professor is playing around with the review process in order to achieve a paper in some strange and bizarre way.

This is not ok, it is wasting our time, and we will have to report this, AGAIN, to your university...

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

Излишне напоминать о том, что у разработчиков ядра и так достаточно проблем со спонтанными ошибками, следовательно поэтому патчи с намеренными ошибками явно нежелательны. Кроа-Хартман сказал, что все патчи, поступающие от @umn.edu отныне подлежат отмене, так как то, что они делают, является преднамеренным вредоносным поведением, неприемлемо и совершенно неэтично.

Надо отдать должное сдержанности Грега и Линуса Торвальдса. Лидер проекта не стал доставать боевой топор, высказавшись в том духе, что технический ущерб невелик, но имеет значение подрыв доверия со стороны товарищей. Можно было высказаться намного резче, особенно после такого пассажа.

On Wed, Apr 21, 2021 at 02:56:27AM -0500, Aditya Pakki wrote:

Greg, I respectfully ask you to cease and desist from making wild accusations that are bordering on slander.

These patches were sent as part of a new static analyzer that I wrote and it's sensitivity is obviously not great. I sent patches on the hopes to get feedback. We are not experts in the linux kernel and repeatedly making these statements is disgusting to hear.

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

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

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

Our community does not appreciate being experimented on, and being tested by submitting known patches that are either do nothing on purpose, or introduce bugs on purpose. If you wish to do work like this, I suggest you find a different community to run your experiments on, you are not welcome here.

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

Технический Консультационный Совет Linux Foundation направил сообщение в электронной почте факультету Computer Science с полным списком патчей Университета Миннесоты, начиная с 2018 г., связанных с исследованиями по безопасности в ядре Linux. Авторы сообщения стремились исчерпать конфликтную ситуацию и восстановить доверие между обеими сторонами.

Университет Миннесоты ответил 9 мая, признав предумышленно неисправными лишь четыре патча. Принося извинения за случившееся, представители УМ берут на себя обязательство соблюдать не только высокие академические, но и этические стандарты в своих исследованиях.



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Как теперь процессить kernel crash и bug report? Или несколько слов о разнице между интересным и удивительным

30.04.2021 22:19:48 | Автор: admin

Широко известен исторический анекдот о том, что царица Екатерина II писала простое русское слово из 3 букв с 4 ошибками. Куда менее известно, что эта ошибка вовсе не уникальна. Дети европейских экспатов, изучающие русский язык, запросто могут в диктанте слово ёжик написать как Й-О-Ш-Е-Г.

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

Автор этих строк много лет пилит гирю OpenVz linux kernel maintenance. OpenVz сейчас потихоньку переезжает с ядра RHEL7 на RHEL8. Ядро в Red Hat за 5 лет сильно изменилось, поэтому мы очередной раз пересматриваем наши старые патчи и думаем, что с ними делать: перетащить как есть, переделать получше или дропнуть за неактуальностью.

В рамках этой большой задачи я разбирался с memcg accounting. OpenVZ аккаунтит различные kernel objects практически с самого рождения с v2.2.x ядер далекого 2001 года. Зачем и почему мы решили аккаунтить тот или иной объект, сейчас сходу понять довольно затруднительно, приходится поднимать историю по старым багам и коммитам.

В стародавние времена мы использовали свою собственную систему accounting, так называемые user beancounters. Там была пара десятков разных параметров, и правильно их настраивать было затруднительно для админов. В upstream эту подсистему тоже принимать не особо хотели, для них приходилось изобретать namespaces и cgroups, а для своих ядер скрещивать ежа, ужа и трепетную лань.

Пересмотрев наш memcg accounting из Virtuozzo Hybrid Server 7.5, я большую их часть перетащил в Virtuozzo Hybrid Server 8, посмотрел, что из этого до сих пор отсутствует в последнем upstream и подготовил соответствующий патчсет. Если кому интересно, кстати говоря, вот он: https://lkml.org/lkml/2021/4/28/70

Перед отсылкой в upstream изменения полагается хотя бы минимально проверить. Я скомпилил свое ядро, взгромоздил его на свою тестовую VM с Fedora Rawhide и давай его по-всякому тестить.

Чтобы разобраться в деталях memcg аккаунтинга в ядро в свое время засунули per-memcg sysfs файлик memory.kmem.slabinfo. В нем показывается количество тех или иных SLAB объектов, которые нашлись в соответствующей memory cgroup, нечто типа обычного /proc/slabinfo. В новых upstream ядрах из соответствующие файлики тоже были, однако из них почему то вообще ничего не вычитывалось. Я посмотрел на своем ядре, посмотрел на оригинальном ядре Fedora то же самое: файл есть, контента нет.

Стал разбираться. Выяснилось, что с полгода назад memcg подсистему очередной раз переделали, но с выводом контента в memory.kmem.slabinfo возникли сложности. Поэтому вывод решили обнулить, а для тех, кому он все таки интересен, в ядро закоммитили drgn скрипт tools/cgroup/memcg_slabinfo.py.

Обычно с ядром разбираются посредством crash но на живом ядре это довольно тяжелый способ. сrash долго стартует, потребляет кучу ресурсов, и на сильно загруженной production node такое делать стремно можно запросто разбудить OOM-killerа. Можно пытаться заюзать ftrace, perf или systemtap но у каждого из них свои недостатки и неудобства.

drgn их легковесная альтернатива. Позволяет добраться до внутренностей ядра, и удобным образом переходить по ссылкам структур ядра. Много говорить про него не буду, кому интересно -- посмотрите-покрутите самостоятельно. В целом, по-моему, удобно, впечатления от использования положительные, рекомендую. Исходник здесь: https://github.com/osandov/drgn.

Закоммиченный полгода назад скрипт на новом ядре уже не работал, структуры ядра за это время уже успели несколько раз поменяться. Неудивительно, на это обречен любой внешний скрипт или out-ouf-tree модуль. Однако благодаря простоте drgn исправить скрипт оказалось несложным.

Я проверил исправленным скриптом свое ядро со своими фиксами accounting все отработало нормально. Потом решил, что стоит посмотреть на то, как вело себя непропатченное ядро. Откатывать свои патчи и перекомпилировать свое ядро заново мне было лениво. Тем более, что у меня уже было другое upstream ядро от Fedora Rawhide aka fc35. Я специально проапдейтился, загрузил самое последнее ядро, запускаю скрипт а он не работает. И проблема похоже даже не в самом скрипте: drgn сам по себе не запускается.

[root@localhost test]# rpm -q drgn

drgn-0.0.11-2.fc35.x86_64

[root@localhost test]# drgn -s /usr/lib/debug/lib/modules/5.12.0-0.rc8.191.fc35.x86_64/vmlinux

Traceback (most recent call last):

File "/usr/bin/drgn", line 33, in

sys.exit(load_entry_point('drgn==0.0.11', 'console_scripts', 'drgn')())

File "/usr/lib64/python3.9/site-packages/drgn/internal/cli.py", line 119, in main

prog.load_debug_info(args.symbols, **args.default_symbols)

Exception: /usr/lib/debug/usr/lib/modules/5.12.0-0.rc8.191.fc35.x86_64/vmlinux: .debug_info+0x7704ab: unknown DWARF CU version 5

Ставлю ядро от fc34, чуть более раннее не помогает.

Ну хорошо, думаю, раз не получается залезть в эти ядра drgn, давай попробую crash.

А он тоже не запускается!

[root@localhost ~]# crash -d 1 (без дебага dwarf error не виден)

...

Dwarf Error: wrong version in compilation unit header (is 5, should be 2, 3, or 4) [in module /usr/lib/debug/usr/lib/modules/5.11.12-300.fc34.x86_64/vmlinux]

crash: /usr/lib/debug/lib/modules/5.11.12-300.fc34.x86_64/vmlinux: no debugging data available

Рассылаю багрепорты во все стороны, выясняю:

Fedora 34 переехала на новый gcc 11, который для всего подряд генерит debuginfo в новом формате DWARF version 5. Обычный userspace с ним вполне нормально работает, gdb этому формату уже давно обучили.

Однако для ядра это оказалось поистине катастрофично, потому что:

  • crash использует внутри старую версию gdb, которая DWARF 5 еще не понимает

  • drgn поддержку DWARF 5 еще не прикрутил,

  • и у systemtap, похоже, аналогичные проблемы.

И пока я рассылал во все стороны багрепорты, Fedorа 34 благополучно зарелизилась.

М-да! Как они собираются процессить kernel crashes и bug reports? Возможно, у них есть хитрый план?

Связался с девелоперами crash и drgn - ни там, ни там быстро прикрутить поддержку DWARF 5 не обещают. Так что похоже, хитрого плана все-таки нет. Возможно, недосмотрели. Возможно, даже, специально закрыли на это глаза, чтобы посмотреть что именно развалится и собрать багрепорты. В конце концов, Fedora не Red Hat и не CentOS. Она как раз и предназначена для обкатки новых технологий. Удивительно, как оно так получилось. Однако при этом совершенно неинтересно, почему.

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

Во-первых, можно перекомпилировать ядро с DWARF 4, в ядре для этого есть соответствующий CONFIG_DEBUG_INFO_DWARF4. Полагаю, Fedora именно так и поступит в ближайшем будущем.

Во-вторых, можно установить ядро от предыдущей Fedora 33. Брать, например, здесь: https://koji.fedoraproject.org/koji/buildinfo?buildID=1738749. Проверил, вроде как то работает.

В-третьих, вспомнить, что Fedora для production не предназначена, и смириться с тем, что в ней багов больше обычного.

И, в любом случае, не поленитесь забить им багрепорты, это очень важно и для них, и для любого другого Open Source.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru