Конфлікт в Linux продовжується: Крістоф Хелвіг залишає посаду мейнтейнера
Крістоф Хелвіг відмовився від ролі мейнтейнера двох ключових підсистем ядра Linux — dma-mapping і configfs. Його рішення супроводжувалося поданням офіційного запиту на виключення зі списку мейнтейнерів і передачею відповідальності іншим спеціалістам. Відтепер супровід підсистеми dma-mapping здійснюватиме Марек Шипровскі з Samsung, а підсистема configfs перейде під контроль Джоела Беккера з Oracle.
«Марек люб’язно погодився займатися супроводом дерева dma-mapping, а Джоел знову займатиметься супроводом configfs, якщо йому дозволятиме час». — зазначив Крістоф у своєму листі.
Основним фактором, що спричинив таке рішення, стала політика Лінуса Торвальдса щодо впровадження мови Rust у ядро Linux. Торвальдс наполягає, що Rust-обгортки повинні додаватися до ядра незалежно від думки мейнтейнерів відповідних підсистем.
На початку року Хелвіг принципово відмовився включати до ядра Rust-обгортку для роботи з DMA, що спричинило конфлікт у спільноті. Унаслідок цього ядро залишили мейнтейнери підсистем Nouveau і ARM/Apple. 24 лютого розробники Rust for Linux запропонували патч із шаром абстракції для файлової системи configfs, яку підтримував Хелвіг. Він не долучився до обговорення цієї Rust-обгортки, а через кілька днів видалив себе зі списку мейнтейнерів цих підсистем.
Лінус Торвальдс, зі свого боку, стверджує, що Хелвіг не має повноважень блокувати прийняття Rust-обгорток у ядро для підсистеми DMA, оскільки вони не зачіпають безпосередньо її код і розміщуються в окремому підкаталозі, що має свого мейнтейнера. Торвальдс порівняв позицію Хелвіга зі спробою обмежити використання підсистеми DMA, хоча мейнтейнери відповідають виключно за код своєї підсистеми, а не за його подальше застосування. Наприклад, зміни в базових підсистемах можуть вплинути на роботу драйвера, проте це не є підставою для мейнтейнерів забороняти додавання нових драйверів.
Читайте також: «Не змушуйте нас працювати з вашою модною мовою!» або чому Linux-розробники воюють проти Rust, «Зелене світло» для Rust: Лінус Торвальдс підтримуює інтеграцію в ядро
70 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарівThe New Rust-Written NVIDIA “NOVA” Driver Submitted Ahead Of Linux 6.15
Відкрийте лист мейнтейнерів, там 26к рядків, тому ну пішов-то хай йде.
Його не тому не шкода, що там 26к рядків мейнтейнерів, а тому, що ну що це за мудацька така позиція — «моя підсистема, ідіть до біса, юзати її з расту не дам». Ну камон, чоловіче, тобі що люди, мають в ноги кланятися, щоб ти дозволив?
А у чому проблема юзати її з Rust або Ada або C++?
Тоді Rust не дуже підходить для цієї задачі, раз на інших мовах програмування можна використовувати підсистему як є, а у разі Rust треба спеціально для нього треба змінювати ядро. Бо це збільшення складності. А з урахуванням того, що Rust опціональний компонент, то у більшості випадків це буде або зайвий оверхід для більшості, або #ifdef та необхідність протестувати усі комбінації.
На справді мова йде про те, що розробники Rust не хочуть робити Copy+Paste. Насправді я не здивуюся, коли через п’ять років Linux for Rust вони вирішили написати перший простий драйвер на Rust з використаннаям DMA, побачили, що у рожевих мріях, коли усі драйвери будуть на Rust, буде копіпаста коду, та вирішили це передбачити та додати підтримку в ядро.
Справа в тому що ця підсистема ядра — Device Memory Allocation (DMA), це розподілювач пам’яті kmalloc для драйверів пристроїв, фактично ключовий компонент API ядра для драйверів. Через призив чи тиск Пентагону, великі корпорації стали писати драйвери пристроїв на Rust, зокрема на нього переїхав вільний драйвер NVidea — Nouveau, які ще і в знак протесту покидали проект. Це насправді дуже вагома проблема, оскільки величезна кількість серверів зі штучним інтелектом вимагають саме NVidea, через історично перший продукт GPGPU — CUDA.
Це проблема для усіх хто надає послуги із хмарних сервісів із GPU POD-ами, і ще дуже багато кого, а ці компанії зокрема є донорами які фінансують The Linux Foundataion. Зокрема топ донорами є компанії : Google, Microsoft, Red Hat and IBM. Усі продають рішення в яких використовується Linux як критична технологія.
Хто коли небудь намагався написати власний розподілювач пам’яті, з іншого боку розуміє, що там усе що вище С — вже оферхед, дуже незручно навіть на С++.
По суті черговий скандал який по факту виник на пустому ніби то місті, через потічні чвари, коли треба було шукати організаційні і технічні засоби вирішення проблеми.
Той же Лінус насправді, просто прогнув одну зі сторін конфлікту просто наїхавши. Це в принципі вважається поганим соф скілом з одного боку, з іншого боку — це Лінус Торвальдс за ним останнє слово. Але так результат — втрачено мейнтенера ядра, людину що робила роботу та мала певну експертизу. А проект тримається на тому що люди туди приносять патчі, часто зовсім безкоштовно, тоді як програмісти того рівня в штаті часто коштують по пів мільйона доларів на рік та більше. Також репутаційна втрата, не вдалось вирішити черговий конфлікт без скандалу.
Rust в Linux Kernel буде однозначно і не дивлячись на супутні складнощі!
Ядро вже давно складний продукт який активно розвивається та еволюціонує. Використання С ускладнює підтримку через слабку експресивність. Насправді С в ядрі має додаткові абстракції (допоміжні типи, макроси та ідіоми їх застосування):
— рівня ядра;
— рівня підсистеми або фреймворку (рідше);
— рівня драйвера (інколи).
Тобто відповідно до задач які вирішують на С він еволюціонував у такий собі макро-С.
Підтримку ядра могло б спростити використання мови більш високого рівня. Відповідні питання пропозиції піднімались багато років назад. Але г-н Торвальдс відкинув ті пропозиції у властивій йому манері і тепер йому повертатись до цього питання не пасує (а може і справді щиро не хочеться).
І тут виходить Rust! Єдиний (наразі) спосіб і злізти з С і не повертатись до тих мов які вже бути відхилені це перейти саме на Rust. Я впевнений (і насправді це вже видно), що г-н Торвальдс не пропустить такої нагоди.
Два питання: (1) що це означає; (2) коли?
Він таким був від народження. Скільки я не пам’ятаю, завжди були макроси, ...
Модулі ядра можна писати на будь-якій мові програмування, це не проблема взагалі. ko-файли це аналоги so-файлів, немає ніякої проблеми їх зробити хоч на Ada, хоч на Pascal.
Ну... по-перше, Rust мова, яка погано міксується з Сі. Так, вона прекрасно може використовувати сішні ліби, даючи інтерфейс більш високого рівня. Але... використання саме Rust коду у Сі досить проблематичне. Взагалі, якщо треба «особлива підтримка» Rust в ядрі, а іншим мовам програмування це не треба, то це обмеження пов’язане саме з Rust. По-друге, а що означає «злізти з Сі»? Переписати ядро повністю на Rust? То краще з нуля.
Мені важко відповісти на усі ваші запитання. Стосовно макро-С мій поінт був в тому що програмісту з досвідом в С але без досвіду в ядрі буде важко зрозуміти код ядра.
Мій досвід скоріше свідчить про зворотне. Проблема, якщо і є, то зовсім не в мові, а в архітектурі. Макроси і там і там однакові, це не питання. А от розібратися, як працює PCI шина, наприклад... в юзермоді такого досвіду немає.
Наприклад, cpython ceval_gil.h, а взагалі
#defineу хідерах зустрічається більше 5000 разів.Взагалі, якщо почитати Страуструпа, то ниска його нововведень (шаблони, інлайни) були виключно для того, щоб зменшити кількість макросів.
Мала експресивність різко перетворюється на перевагу, коли доводиться співставляти сурси з різного роду крашдампами, розставляти брейкпоїнти дебаггерами і все таке.
Коли стикаєшся з серйозною проблемою (типу race conditions) коли додавання логів чи робота під дебагером впливає на occurrence rate то ніяких переваг це не надає.
А я гадаю що саме на вирішення таких проблем (а не null pointer dereference) спрямовані основні ресурси підтримки.
Мені доводилося займатися чисто підтримкою з портуванням CVE для ядра. З того, що обіцяє раст, максимум 5% проблем була пов’язана з неініцілізованими ресурсами, і нуль разів з use-after-free, double free чи подібним в чистому вигляді. В «кращому» випадку, проблемний ресурс загорнутий в надто багато шарів абстракції, щоб вдалось прослідкувати весь лайфтайм (класика жанру — різного роду буферизації і прийоми-передачі дескрипторів сопроцесорам), в гіршому — його лайфтайм взагалі задається позапрограмними сутностями, типу висмикування на ходу USB-пристрою, і вже подібна подія призводить до race conditions або меморі ліків під час примусової деініціалізації. І для дебагу таких станів якраз і потрібна мінімальна експресивність і поменше магічних гарантій компілятора.
Стосовно того які проблеми потенційно покриває Rust сперечатись не стану і довіряю вашому досвіду.
Стосовно деініціалізації зауважу що є мови в яких звільнення ресурсів чітко визначено і підтримується компілятором на рівні мови. З мого досвіду ніколи не було проблем з тим що звільнення ресурсів відбувається не так як це визначено в коді. Особисто я вважаю ручну перевірку raw pointers прикрим марнуванням часу.
На жаль, це не дуже працює, коли ми власне працюємо над нутрощами того, що ці ресурси нарізає. Наприклад, в одних випадках важливо гарантовано отримати пам’ять, і ми можемо чекати доки вона з’явиться, в іншому затримки недопустимі, потрібно негайне повернення помилки, в третіх випадках потрібна гарантія, що об’єкти буде лежати на одній фізичній сторінці рам для правильної передачі наприклад від ядра до гіпервізора,... І це не рідкі виключення, а типова ситуація, тому одним дефолтним варіантом компілятора не обійтися.
З іншого боку, для чисто логічного коду добре працює просто виділити об’єкт по devm_kzalloc() і далі не думати, буде підчищено автоматично при деініціалізації інстансу драйвера. Відносно расту проблема хіба що в можливості накосячити з типами і виділити неправильну кількість пам’яті, але зазвичай такі проблеми проявляються одразу.
На жаль, це все ще самий дієвий спосіб відлову вищезгаданих проблем з логікою роботи при race conditions, спричинених позакодовими факторами. Хоча і неідеальний, зокрема, є відома проблема з тим, що перевірки мають тенденцію до дублювання, від чого ядро в середньому на1-2% в рік стає повільнішим.
Я погоджусь з тим що в реальних кейсах взаємодії з залізом можуть бути випадки коти стандартні підходи не працюють.
І так, погоджусь з тим що було б добре щоб звичайна (віртуальна) памʼять звільнялась автоматично.
Особливо незрозуміло виглядають випадки коли в С роблять підрахунок посилань (для управління часом життя ресурсу), але задача підрахунку при цьому покладається на програміста.
Мені це виглядає як аргумент про те що С не достатньо ефективний для вирішення задач які на нього покладаються!
В тому контексті завжди відмічаю: в нормальній сучасній мові «хоч якесь» RAII — musthave, або хоча б якийсь defer, як в go, хоча б using як в C#, хоча б with як в Пайтоні (а оті всі goto Cleanup; то особливий клінічний затятий мазохізм, втім, як й набивання табличок VMT ручками, коли люди роблять «ООП на Сшці» й всі танці з бубном довкола ініціалізації (та клінапу в правильний момент) таких «класів»...)
Ну і так, референс каунтінг або передача овнершипа — мастхев, об’єкт не повинен зникати «сам собою» через то, що його прикантошили в іншому місці бо хтось висмикнув дріт...
Все оте «перевір чи ще підключено а тоді звертайся» — не катить, бо це завжди рейс кондішен!
Все ж таки, типовий сішний код часто виглядає так:
struct data * create_data(void) { struct data * result = malloc(sizeof(struct data)); if (result == NULL) goto bad; data->some_resource = alloc_resource(); if (!data->some_resource) goto bad1; data->another_resource = alloc_resource(); if (!data->anotherresource) goto bad2; return result; bad2: free_resource(data->some_resource); bad1: free(result); bad: return NULL; }І тут виникає питання, що треба додати в мову, щоб це робилося без оверхіду? Якщо
defer, то буде купа перевірок, чи ми ОК чи не ОК:struct data * result = NULL; struct data * ptr = malloc(sizeof(struct data)); if (!ptr) return NULL; defer { if (result == NULL) free(ptr); } ... result = ptr; return result; <pre> using не дуже підходить, бо нам не обов'язково треба звільняти ресурс. C++ треба на останньому кроці забрати володіння, зайве копіювання. А якщо оверхід припустимий, то код стає ± норм <pre> void free_data(struct data * me) { if (me == NULL) return; if (data->some_resource) free_resource(data->some_resource); if (data->another_resource) free_resource(data->anotherresource); free(me); } struct data * create_data(void) { struct data * data = malloc(sizeof(struct data)); if (result == NULL) return NULL; do { data->some_resource = alloc_resource(); if (!data->some_resource) break; data->another_resource = alloc_resource(); if (!data->another_resource) break; return data; } while(0); free_data(data); return NULL; }vs язык здорового человека
fn create_data() -> Option<Box<Data>> { Box::new(Data { some_resource: alloc_resource()?, another_resource: alloc_resource()?, }); }На C це робиться макросами. Виглядає і працює не гірше. Хоча як на мене С це сто кроків назад від таких мов, як Lisp і Forth
Саме так, але лаконічність тут не головне!
Принципова різниця полягає в тому що в С деініціалізацію треба явно викликати руками. Хоч то оператор, макрос чи функція, але все одно виклик має зробити програміст.
А дуже хочеться (бо це зручно) щоб звільнення ресурсів відбувалось автоматично!
Тому мені дуже подобається RAII в C++.
На ньому ж можна писати в стилі «C з RAII»
Виглядає багато зайвого оверхіду
use std::alloc::{self, Layout}; pub struct Resource { ptr: *mut u8, } impl Resource { fn new() -> Option<Self> { let layout = Layout::new::<u8>(); let ptr = unsafe { alloc::alloc(layout) }; if ptr.is_null() { None } else { Some(Resource { ptr }) } } fn dealloc(&self) { let layout = Layout::new::<u8>(); unsafe { alloc::dealloc(self.ptr, layout); } } } impl Drop for Resource { fn drop(&mut self) { self.dealloc(); // Теперь мы вызываем dealloc с ссылкой, а не перемещаем объект } } pub struct Data { some_resource: Resource, another_resource: Resource, } pub fn create_data() -> Option<Box<Data>> { Some(Box::new(Data { some_resource: Resource::new()?, another_resource: Resource::new()?, })) } pub fn main() { if let Some(data) = create_data() { println!( "Data allocated at: {:?}, {:?}", data.some_resource.ptr, data.another_resource.ptr ); } else { println!("Allocation failed"); } }Дивимося:
Сішний варіант компілиться один в один майже без зайвого:
так нечесно (воно не розгорнуте)
й це замість повністю розгорнутого вище
я б атакував в першу чергу сам «глобальний»
якщо такі виклики будуть по всьому ядру умовної операційки,
це печалька (що, втім, не скасовує можливості завести свої альтернативні способи алокації, й все буде тайпсейф та «чікі-пікі»)
Я не великий знавець Rust, а в питаннях коли треба економити на інструкціях взагалі не хочу таким займатися. Якийсь usermod так, я готовий писати лаконічно. А он на те, що Rust нагенерував, у кордампі дивитися мені не дуже захочется.
То я сам так написав, там міг бути будь-який зовнішній виклик.
Мене зачепило більше
я його робити це не просив.
Взагалі, є чуйка, що треба кожен раз компілювати Rust код щоб зрозуміти, чи не вставив він щось зайвого.
инлайн функций это примитивная оптимизация, которую делают и GCC и clang.
Это сайд эфект использования Box::new, который делает проверку на ошибки аллокации. Я ниже привел пример с try_new, который убирает эту проверку и переносит ее на пользователя через Option.
я «трохи про інше»: в «няшно Сшній частині» там «багато цікавого» заховано в готовенькому
а в Rust
порозгорталися/заінлайнилися, порівняння некоректне.
Але найбільші граблі: насправді в Сшному
create_data якщо то робити без готовенького free_data взагалі незрозуміло, що з тим data робити потім (в загальному випадку незрозуміло нaвіть чи воно thread safe, або чи можна елементи data звільняти по окремості...)
тобто create_data й free_data завжди мають йти «парою», й «оптимізація» всередині create_data не має смислу, також, зокрема й з огляду на те, що «зазвичай» все алокується, тобто оптимізувати треба лише «позитивний» шлях а не корнер кейс коли «щось навернулося»
Я не зрозумів, що там може бути незрозумілого... Дивишся код та усе ж видно! Там же немає нічого прихованого.
ні ні, не дивишся (зі мною не погодяться, але якщо для юзу не достатньо побачити *.h, а треба дивитися реалізацію в *.c то це велика печаль!)
... але в *.c коді реалізаці жодного коментаря про те, чи порядок суттєвий (тому «на всякий випадок його не змінюй, бо що буде, якщо зміниш — а чорт його знає»)
(я той пост якраз поедітав і дописав ще й про те, що free_data мастхев і викликати його для рідкісного кейсу «навернулася алокація одного ресурсу» — цілком норм!)
Няшна Сшка стає прекрасною тоді, коли
і тоді АПІшка стає абсолютно «герметична» йнавікидоки ми не змінимо сигнатури бінарно сумісна! (то відповідник C++ ної pimpl ідіоми)А в ідеалі дрова живуть кожен в своєму «процесі» й спілкуються через якийто IPC «великими кусками», й ми «забиваємо» на «оптимізції бліх», й отримуємо «якето
MinixQNX» що цілком прекрасно живе в світі «швидкодіючих девайсів з прогнозованим часом реакції на подію»))Звісно для часів появи Лінухів мікроядро й дрова в окремих процесах, було «дуже дорого», але ота «інша крайність», коли всі частини до всіх частин «кишками наружу» і «ковиряй що хочеш», то це якась зовсім печалька...
Золоті слова:
Попри всі «мікрооптимізації» Лінух не є QNX!
Навпаки, ріст частоти і зниження латентності давно зупинилось, і оффлоад всього що тільки можна в залізо і сопроцесори стає все більш значимим. Найгірше що можна придумати, це наплодити спільних боттлнеків зі стандартизованим IPC.
саме так! саме так!
тому чим далі, тим «передаються дані», а не якіто структурки з алокуємими в тому ж адресспейсі поінтерами/ресурсами, які потім треба звільняти/деалоковувати, «десь в іншому тому процесорі» (ваш «співпроцесор» не може «бачити» адресспейс викликаючого процесу й «звільняти» в ньому якіто «ресурсти»!)...
іншими словами «struct data» з якимито заалокованими даними в такій новій парадигмі не повинен існувати взагалі!
Вірніше повинен бути просто «структуркою без посилань», що сама містить всі дані, які треба!
Й тоді його можна буде пересилати «як є» і між процесами і між (спів)процесорами, і через всякі шини, і по мережі (вважаємо що ендіаннес співпадає)...
Й тоді «стандартизована» АПІшка для передачі даних «куди треба» вона «всюди однакова» і посилає «просто байтики»))
І всі учасники процесу будуть просто знати всі структури даних синхронізованим чином, в системі буде насипано достатньо рам, щоб вони були аллоковані узгодженим чином в компайл таймі на всіх сопроцесорах, і протоколи з якими це все працює ніколи не будуть змінюватися. Wifi 802.11b і відео в 1024×768 вистачить кожному і назавжди, наприклад...
ем... так це ж і є як сьогодні працюють Лінухи, от прямо зараз — то «моноліт» де «всі бачать все», і повинні «ковирятися» в розшарених на весь світ лінкедлістах де всі поля «кишками назовні» (і всі повинні знати правила ковиряння в тих кишках)...
А от щоб послати на «сопроцесор», то вже має бути теж відомий формат для обох сторін, який «просто дані»)) А «друга сторона», то може бути взагалі не Лінукс, а «електроніка» без ніякої OS...
яких «сопроцесорах»? Якщо мова про фізичний девайс який «щось вміє» й куди ми посилаємо «дані на обробку» щоб «розпакуй/декодуй мені ось це», то він «там в себе» не може звільнити те, що ми зробили (k)malloc (чи вицепили з лінкед ліста) «тут»! Тобто «той» девайс він алокує «в себе» дані «як хоче», але викликати деалокацію в «нашому» процесорі він вже не може ніяк!
Чого ж не будуть? будуть!... Але наш наявний допоміжний фізичний девайс з своїм іншим процесором, він змінює свій протокол доволі рідко!
А от ядро змінюється щоразу, і щоразу при автоматичному апдейті ядра dkms (що теж запускається автоматично) мучає процессор, хоча мій хардвар ніразу не змінився, все то само, як і було, але треба перекомпілити існуючий драйвер під нове ядро, бо ядро «щось змінило» в своїх «кишках назовні»))
якщо хардвар на моєму старенькому ноуті фізично вміє тільки в MP2, і перепрошивки не передбачено і там вже «хардваєред» електричних колах, то в MP4 його вже ніяк не заставиш))
І ніякий (k)malloc чи elem = free_blocks->next; тут вже ніяк не допоможе))
#![feature(allocator_api)] pub struct Resource { inner: Box<u8>, } impl Resource { fn new() -> Option<Self> { Some(Self { inner: Box::try_new(0).ok()? }) } pub fn ptr(&self) -> *const u8 { self.inner.as_ref() as *const _ } } pub struct Data { some_resource: Resource, another_resource: Resource, } pub fn create_data() -> Option<Box<Data>> { Box::try_new(Data { some_resource: Resource::new()?, another_resource: Resource::new()?, }).ok() } pub fn main() { if let Some(data) = create_data() { println!( "Data allocated at: {:?}, {:?}", data.some_resource.ptr(), data.another_resource.ptr() ); } else { println!("Allocation failed"); } }Box<> в ядрі?
github.com/.../kernel/alloc/kbox.rs#L76
Цей варіант краще, але... по-перше, він використовує unstable feature, тому можна білдити лише nightly компілятором. Але там ще поміняно порядок викликів, спочатку виділяється some_resource, потім another_resource, а потім Data. Далі, він намагається зберегти порядок ініціалізації, тому спробує звільнити some_resource перед another_resource, що теж далеко не факт, що треба робити саме так.
Тому замість сішного коду, де ми самі кажемо що за чим треба робити і як, ми отримує код на Rust, де він сам вирішує в якій послідовності будуть відбуватися дії. Або це дуже неочевидні питання по коду.
В Rust for Linux unstable features используются: github.com/...-for-Linux/linux/issues/2
Он ничего не менял, он следует вполне простым правилам.
Какой именно из вариантов делает «правильно» — немного спорный вопрос в контексте этого примитивного примера без дополнительных деталей.
Це просто свідчення того, що Rust в Linux це дуже експерементальна річ. Припустимо, я хочу поставити драйвер з частинами на Rust на моє кастомне ядро Funtoo Linux. Я запускаю сприпт інсталяції, йому треба як завжди щось докомпіляти, бо умовна компіляція впливає на зміщення полів структур. Для цього йому треба Rust, але не простий, а nightly build. І не факт, що останній підійде.
Тут скоріше питання у тому, що в разі Сі ти бачиш який з двох варіантів реалізований, та можеш обрати поведінку. У разі Rust ти цього не бачиш, а щоб досягнути бажаного треба займатися магією.
А якщо брати приклад, то звісно хочеться перекладу послідовності дій один в один, бо інакше це некоректний переклад коду. Тобто, написати один в один як в Сі лаконічно не вийде, але можна елегантно зробити щось, що працює подібним чином.
Вот с этим вообще никто не спорит.
В C я вижу кучу goto, в которых разобраться очень сложно, и as far as I’m concerned, там по дефолту будет как минимум где-то утечка памяти, если этот код не просмотрели хотя бы 2 независимых ревьювера.
Чего именно «желаемого»? Если хочется именно «так что было как в сишечке», может потребоваться немного код переписать. Но как я указал — в конкретном примере нет причин считать, что сишечка делает более «правильно» чем раст.
Таки да. Аллокация памяти в куче не считается сайд эфектом, который нужно в определенном порядке выполнять с другими сайд эфектами. Rust может даже сделать оптимизацию, которая вообще уберет из кода аллокацию памяти в куче, если увидит, что аллокация освобождается и не покидает пределы какого-то кода.
От чомусь мені дуже не подобається динамічне виділення пам’яті. Тільки ріалтайм в ядро вкомітили, і апйать...
То завжди з тих ситуацій, що... «іт депендс»...
В загальному «Ай лайк» приклад від шановного Artyom Krivokrisenko, як самий лаконічний))
Втім давайте розкрутимо цю гру:
Ем... malloc для структури з двома полями?
З локами мютекса хіпа та пошуком доступного блока в (гіпотетично) пофрагментованому хіпові?
То може «зайве»
копіюваннямув вже не так й «страшно» й +можемо покладатися на обов’язкове RVO (й нічого не алокувати взагалі)?А якщо структурки (чи поля) більш великі, то може C++не unique_ptr (умовно відповідник Box з Расту)?
А якщо в нас таких структурок «дофігіще», й їх треба «алокувати швидко», то може це має бути «блокпул»?
й тоді (умовно, NDA не порушено):
Причому той Emplace може повертати чи то «мув онлі» подобу C++ного «юнік поінтер» для щойно алокованого, або навіть й з референс каунтами (що сторяться поруч своїх даних в тому ж блокпулі))
Звісно то все (такі обгортки) для блокпулів, то «нестандартні велосипеди», але якщо є один раз готове, то їх можна реюзати багатократно!
Й звісно цей підхід можна також розгромити, бо ж локфрі операції, всякі інтерлокед інкременти для умовного shared_ptr, то все теж «не безкоштовно»...
Але чи справді саме в тому фрагменті «затуп»? А ми його вже оптимізуємо...
А ще, якщо ми маємо «часто бігати» по отих «дофігіще» всіх таких структурок, то може їх треба покласти в одному масиві послідовно, а не виривати з пейджів порозкидуваних по всьому хіпові?...
А може їх взагалі «алокувати» в стекові сріда (чи й корутини))
І це ми ще не дійшли до Rust з його «борров чекером» та «лайфтаймами»...
Але навіть для «няшної Сшки» то може бути не
аде (правильно вирівняне) initHere ми вже повинні підіпхати самі...
й тоді ми його можемо взяти/заалокувати «де треба»...
(його ж потім треба буде й викинути «таким же способом» після data_destroy)
___________________________
Але я трохи про інше,
без юзкейса та знання наперед, такі приклади й постановки умов, це можна завжди «натягти сову на глобус» так, щоб конкретно той приклад з goto був єдино правильном рішенням...
(що в інших кейсах, втім, буде пекельним збиранням бліх...)
Проте люди спецом чогось «обрубують» всі решта множинних варіантів, й лишають тільки оте «істинно канонічне» з goto ручками...,
Втім й воно по деяких кодстанах теж не катить, також як і багато ретурнів на функцію — не катять... і тих «підходів» — безчисленний «зоопарк», особливо коли в одному проекті юзається код потягнутий з багатьох кодбаз, в кожній з яких в авторів були свої «мухи», щодо того де алокувати та як звільняти)
Ну і саме головне, щоб любителі кожного з тих підходів не допікали, то все що тільки можливо треба написати на... Пайтоні треба написати на Пайтоні, й взагалі триматися подалі від того світу «прелімінарі оптимізації»))
Проблема у тому, що в разі ядра ми гадки проце не маємо. Це може бути AMD Ryzen Threadripper, і тоді, напевно, це нас не хвилює. А може бути ARM Cortex-A5. Але наш код має працювати і там і там.
Звісно що у мене лише ілюстрація, а ресурси можуть виділятися з пулів, тощо. Плюс код може бути день в IRQ, де нам треба якнайшвидше витягнути, покласти та перейти до нижньої половини.
та ні, зазвичай ми якраз знаємо що робимо і тоді:
* умовий «хендл» якогото девайса з його ресурсами в нас добувається не так то й часто... то чи Rust чи С++ чи няшна Сшка — без різниці, головне убезпечити щоб юзер потім правильно позбувся того «хендла» (в цьму «убезпеченні» Rust та С++ таки кращі!)
* а якщо такі «алокації» робити на кожен пакет даних від (умовно) якогото
блутузадевайсу, який присилає дані, то за такі алокації треба «виривати руки»От якраз в IRQ треба запихати дані в чергу/буфер «як є» без ніяких алокацій та інших «мудрих дій», і вже «на тому боці» черги персати залежно від потреб... (або і не парсати, а одразу надавати можливість читати те, що вже прийшло, з юзерспейсу через /dev/чототам)
Ще раз, це код умовний. Для іллюстрації простіше написати
mallocчим якусь власну реалізацію пула.Тебе навіть не смутило те, що в ядрі немає
malloc. Невже важко уявити, що замість нього буде щосьlock(); if (list_empty(free_blocks)) { unlock(); return; /* Drop no space */ } struct list_head * elem = free_blocks->next; list_del_init(elem); unlock(); struct data * data = container_of(...); ... bad: lock(); list_add(free_blocks, elem); unlock();Це ближче до реалій, але втрачається ілюстративна цінність.
та я розумію що умовний, тому й просуваю умовний блокпул))
яка ілюстративна цінність?
там в ідеальному світі повинно бути
а не весь отой мазохізм з (умовно) rcu_read_lock() та rcu_read_unlock() й відчіплянням хеда вручну (це все має бути заховано, а не ручним розлокуванням для кожної гілки))
й той, хто ті блоки юзає, він взагалі не повинен знати деталі реалізації звідки вони беруться навіть для випадку Сшки треба гуглити про трушні блокпули в ThreadX а не про
гавнопадєлкикурсачі фінських студентів де вони по всьому пректу багатократно демонструють свої здібності роботи з двозвязними списками... бути «списком» — то «деталі реалізації» які взагалі не повинні «світитися» в місці виклику))litux.nl/...2327201/ch11lev1sec4.html
Мене цікавить, щоб ядро парцювало на моїй платформі, і працювало ефективно
Пітон це дніщє там, де треба хоч якась продуктивність. Особливо при обробці зображень.
Зате я вже вмію викликати сішні функції з пітона, які працюють з нампі :)
а мы точно под продуктивностью одно и тоже пониманием?
мои девы на питоне выдают х5 (условно) от с++
Швидкість виконання алгоритму
Не знаю, лазити по сішному кордампу мені приємніше, ніж по C++, особливо з boost. Воно ж усе видно: цицьки — ось, дупа — ось.
Якщо говорити про код, то мушу погодитись, що інколи код на С++ (як був згаданий boost) може бути написаний дуже важко для розуміння.
Стосовно coredump не погоджуюсь. Абсолютно немає різниці на чому (C++, boost, Qt) написаний код, навігація і аналіз дампу ані трохи не складніші ніж в програмі на С.
Мне очень интересно, почему растаманы лезут лечить чужие проекты, но не создали ни одного своего?
Вот и открылось предназначение Раста — он создан для того, чтобы порождать срачи.
Для цього увесь IT сектор створений.
Не-не, растаманы они как веганы и велосипедисты — лезут в любую щель и там продвигают свою повестку.
«Я не знаю о чем вы тут спорите, но borrow checker делает язык безопасным».
Хана Линуксу. Пингвинчик, мы тебя любили.
Та нє, поки тримають врапери у окремих папочках ще якось простіше поберегти нервову систему і просто у ті папки не заглядати.
Коли почнуть творити те, що не сподобається більшості комьюніті вони просто зроблять форк відмиють від іржі та почнуть собі жити далі.
Тому не думаю що прям щось смертельне відбудеться.
Субъективно мне кажется, что Линух стал настолько монструозным, что время прийти чему нибудь новому и легковесному.
Скоріше модульному. Але.. драйвери вже написані, усе працює, ніша зайнята. Які аргументи будуть для переходу на нову операційну систему?
Теоретично могла б бути ліцензія, але, наприклад, базова система FreeBSD значно монструозніша за окремо взяте ядро.
Тут не аргументы, тут скорее ощущение.
Ядро гнется под тяжестью мегасовмнстимости.
В тоже время большинство драйверов, да и архитектур нафиг не нужны.
Тут просто просится микроядро, от которого юный Линус яростно отплевался.
Косвенно я вижу две вещи — вынос части функциональности за пределы ядра, как например. dpdk. С другой стороны растет рынок мощных микроконтроллеров без виртуальной памяти и защищённого режима и RTOS для них.
Это проще, это дешевле, это отгрызет большой кусок рынка специализированных компьютеров.
Make QNX Open Source Again!
Дали же опять в доступ, для частного использования кажется.
Він настільки пішов по руках, що довіряти їм важко. Я б платив за ліцензію, але виключно з відкритим кодом, розумною ціною, і пріоритетною підтримкою.
В мене дитяча травма від спілкування з їхнім сапортом, коли вони лягли під Cisco і забили на інших клієнтів.
Тому наразі лінукс, незважаючи на нюанси з ріалтаймом і пам’яттю. Бо це можна самому дослідити, зрозуміти і виправити. А не black box тестувати. Хоча вартувало тоді реверснути, але були інші задачі. Ну й нарешті добили їх...
Дед-совкодрочер объясняет как хорошо было в 1969 когда ели тушенку по ГОСТу и катались на москвичах, фото в цвете, 2025
Нажаль довелось робити харакірі. Що не приємно — по суті Торвальдс пішов на підтримку гімногкоду, щоби догодити корпорації, бо уся ця чехопда по суті навколо драйверів до NVidea Nouveau (ті в свою чергу через тиск Пентагону пішли на Rust). Як писав сам Лінус — він в першу погодився на погане рішення лише із X11. Як бачимо вже не один раз.
Та ну... єдині граблі, які я бaчу 0 це то, що «скоро» dkms потребуватиме не лише «няшної Сшки», а й Rust :)
Кусків закоментованого коду я в коді на С не бачив. А от те що накомітили на Rust пішов поидивитись і прозрів, там навіть OpenSSL краще виглядає. Проблема то не в самому Rust, а там комітять наскоро зроблене лайно. Крім того для Rust із наявним Cargo і т.д. — це надумана проблема.
Так само думаю — що питання було політичне, прибрати чувака який не доволяє нашвидкоруч підлити хочь і погані, але драйвери під новітні відеокарти, від чого злежить мільярдні прибутки, бо ті усі серевери в датацентрах із AI лихоманкою — вони на Linux. Як бачимо зовсім нещодавно те саме зробили із Python, з кервної посади викинули Тіма Пєтерса, бо GIL — це більше заліза в датацентрах і менше прибутки тих хто послуги цих датацентрів продає, під послуги AI софту.
Комерціаналізація відкритих проектів робить свою справу, коли FOS діє як свого часу діяли Microsoft Гейтца та Балмера.
Теж цікаво подивитись. Лінку не пошарите?
це ж яке ж лайно вони б тоді накомітили на Сшці? ))
іншими словами чому з «кусків закоментованого коду» висновок що в них на «няшні Сшці» вийшло б «щось краще»?
Та не вийшло б! ))