LINUX.ORG.RU

Posix-style regeх now in kernel

 , , tre,


0

2

Коллеги! Не бейте больно! :)

Мне для моего основного проекта понадобились в ядре регулярные выражения. Я понял, что писать автомат ala apparmor — дело долгое, а у меня времени не очень много. Я попробовал портировать в ядро хоть какую-то библиотеку для сопоставления строки регексу. И у меня получилось.

После недолгого копания в альтернативах, выбор пал на старую библиотеку TRE, которую использует библиотека musl и какой-то из *BSD (Net- или Open-, кто-то из них) в качестве реализации того, что написано в regex(3) и далее по тексту.

Портануть это дело получилось буквально за несколько часов и оно заработало с первой загрузки. Я фшоке и Респект автору!

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

Лежит здесь — https://github.com/gns48/tre/tree/linux-kernel

Как этим пользоваться:

1. Сливаем репу с гитхаба
2. cd tre; git checkout linux-kernel
3. cd linux_kernel; make
4. В Makefile своего модуля прописываем 
    KBUILD_EXTRA_SYMBOLS = <tre module source>/Module.symvers
5. Добавляем в свои исходники #include <tre.h> из дерева исходников
    <tre module source>
6. Пользуем в своем модуле tre_regcomp/rtre_regexec/tre_regfree, как    
    описано в regex(3). Да, для пользовательского пространства 
    библиотека объявляет имена regcomp, regexec, regfree для 
    совместимости, но я не стал этого делать.

Ограничения: 

1. нет поддержки "широких символов" и прочего юникода.
2. нет поддержки "приблизительного" сравнения (Approximate pattern matching), уж больно много соответствующие функции отжирают стека статическими массивами. Да и незачем оно в ядре. Основное назнчение -- это сравниватиь пути с "точным" образцом.

Что дальше в планах:

1. Сопроводить каждый регекс некоторым «контекстом использования» для того, что бы кучу регексов можно было объединить в один по '|' и скомпилировать их вместе. Далее regexec кроме «да/нет» должен возвратить, допустим, «да, и вот контекст того индивидуального регекса, которому соответствует строка».

2. Поправить распределение памяти при компиляции регекса с тем, что бы, во-первых, был известен объем выделенной памяти функцией regcomp и, во-вторых, выделенный объём памяти был скомпонован в единый непрерывный бандл. Сейчас regex_t внутри — это развесистая структура, из которой во все стороны торчат указатели, и память для них выделяется не единовременно.

★★★★★

Ответ на: комментарий от annulen

re2c

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

anonymous
()

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

hibou ★★★★★
()
Ответ на: комментарий от firkax

Да, wildcards, globs итд — это тоже паттерны, другое их название. Задача же не просто написать простой разборщик шелловских глобов (они и так в ядре есть, кстати). Задача более общая. См «планы развития».

gns ★★★★★
() автор топика
Ответ на: комментарий от firkax

Если я правильно понял что это значит, то wildcard-паттерны уже тут нарушают и это определение регэкспа.

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

Тут вот есть некий барьер между теоретическими построениями из теории вычислимости и реальными алгоритмами (от себя замечу — тезис Черча, называется).

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

gns ★★★★★
() автор топика
Ответ на: комментарий от firkax

Спасибо, но это, конечно, доделывать надо.

if(strstr(pat,«***»)) return -1; — вот это минимизировать надо, а не ошибку возвращать. Есть правила поглощения одной маски другой, если они подряд написаны.

gns ★★★★★
() автор топика
Ответ на: комментарий от gns

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

static int xx_match(char const *pat, char const *s) {
  while(pat[1]=='*') pat++;
firkax ★★★★★
()

Я в марте 2020 какую-то библиотеку libre в ядро втянул. Там всего 2 файла regexp.c и regexp.h

https://github.com/vel21ripn/nDPI/tree/flow_info-4/ndpi-netfilter/libre

/*
 * regcomp and regexec -- regsub and regerror are elsewhere
 * @(#)regexp.c 1.3 of 18 April 87
 *
 *      Copyright (c) 1986 by University of Toronto.
 *      Written by Henry Spencer.  Not derived from licensed software.
vel ★★★★★
()
Последнее исправление: vel (всего исправлений: 1)
Ответ на: комментарий от firkax

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

Регексы придумал Клини в 50х годах для нужд теории вычислимости и прочих оснований математики. Регулярным выражением над языком L c конечным алфавитом Σ по Клини является:

1. Пустая строка. 
2. Любой символ языка (один)
3. Два символа подряд (конкатенация строк из одного символа)
4. (Замыкание Клини) - унарная операция над множеством строк либо
     символов. Замыкание Клини в алфавите Σ: 
     Если Σ = {0,1} , то Σ∗ ={ε,0,1,00,01,10,11,000,001,010,011,…}
5 Применение правил 1-4 в произвольном порядке и количестве.

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

Далее было доказано существование частично-рекурсивной функции, распознающей или «перечисляющей» данный язык. (* Доказательство: алфавит конечен, полный перебор всегда существует. QED)

Далее, нам надо перешагнуть через тезис Черча из мира частично-рекурсивных функций в мир аффективных вычислительных процедур, сиречь алгоритмов. И здесь у нас, у приземленных программистов, сразу появляется необходимость задать множество допустимых слов. Я тут не буду определять, что такое «вычислить», «распознать» и т.п. Список литературы будет приложен ниже.

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

Этот переход между теоретическим понятием регекса и практическим можно пояснить следующмим. Теоретический регекс — это множество слов языка (возможно счетное). А практический регекс — это Гёделев номер множества тех слов языка, которые распознает или «допускает» этот регекс.

Таким образом, shell glob, паттерн, маска, шаблон — это все названия одного и того же.

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

gns ★★★★★
() автор топика

нет поддержки «широких символов» и прочего юникода

Это не совсем так. Я не помню конкретных деталей, но туда можно передавать пользовательский callback, уже в котором можно написать свою логику с поддержкой юникода. Интерфейс там мерзкий, но лет 5 назад у меня завелось.

Сопроводить каждый регекс некоторым «контекстом использования» для того, что бы кучу регексов можно было объединить в один по ‘|’ и скомпилировать их вместе. Далее regexec кроме «да/нет» должен возвратить, допустим, «да, и вот контекст того индивидуального регекса, которому соответствует строка».

…в том числе для этого.

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

P.S. рекомендую посмотреть на sregex. Поделка примерно того же уровня, но к допиливанию куда более дружелюбная.

Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 1)
Ответ на: комментарий от Siborgium

нет поддержки «широких символов» и прочего юникода

Это не совсем так. Я не помню конкретных деталей, но туда можно передавать пользовательский callback, уже в котором можно написать свою логику с поддержкой юникода. Интерфейс там мерзкий, но лет 5 назад у меня завелось.

Тут какая-то аберрация сознания. В библиотеке все есть, и под винду, с ее utf-16 собирается, и wchar есть, Конфигурится э то все прекрасно автотулзами. Я это все обрезал при компиляции. Поэтому и нет.

Насчет коллбека не знаю, в стандартных regcomp/regexec никаких коллбеков нет, все как в regex(3) описано. Может и есть какие-то коллбеки в функциях нечеткого поиска, го я туда не смотрел от слова совсем.

gns ★★★★★
() автор топика