Почему существует политика ядра Linux, чтобы никогда не нарушать пространство пользователя?


38

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

Линус время от времени привлекает противоречие с его пламенем на LKML. По его собственному признанию, эти языки пламени часто нарушают пространство пользователя. Что подводит меня к моему вопросу.

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

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

(Очевидно, что у такой политики есть определенные недостатки, которые последовательно применяются, поскольку у Линуса иногда возникают «разногласия» с его старшими лейтенантами в LKML именно по этой теме. Насколько я могу судить, он всегда в этом разбирается.)


1
Вы неправильно написали имя Линуса во введении.
Исмаэль Мигель

Это был не я точно, но я забыл поднять голос и отдал свой голос сейчас.
Исмаэль Мигель

Ответы:


38

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

Теперь это правда, что большинство программ на самом деле не зависят напрямую от интерфейсов ядра ( системные вызовы ), а только от интерфейсов стандартной библиотеки C ( оболочки C) вокруг системных вызовов). О, но какая стандартная библиотека? Glibc? uClibC? Dietlibc? Bionic? MUSL? и т.п.

Но есть также много программ, которые реализуют специфичные для ОС сервисы и зависят от интерфейсов ядра, которые не предоставляются стандартной библиотекой. (В Linux многие из них предлагаются через /procи/sys .)

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

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

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

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

Это несколько нормально иметь четко определенный одностороннюю зависимость. Это печально, но иногда неизбежно. (…) То, что НЕ в порядке, это иметь двустороннюю зависимость. Если код HAL пользовательского пространства зависит от нового ядра, это нормально, хотя я подозреваю, что пользователи будут надеяться, что это будет не «ядро недели», а скорее «ядро последних нескольких месяцев».

Но если у вас есть двухсторонняя зависимость, вы облажались. Это означает, что вы должны выполнить обновление в режиме блокировки, и это просто НЕ ПРИНИМАЕТСЯ. Это ужасно для пользователя, но, что еще более важно, это ужасно для разработчиков, потому что это означает, что вы не можете сказать «ошибка произошла» и делать такие вещи, как попытка сузить ее пополам.

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

Официально ,

Обратная совместимость для [системных вызовов, объявленных стабильными] будет гарантирована в течение как минимум 2 лет.

На практике, однако,

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

Что чаще всего меняется, так это интерфейсы, предназначенные только для использования аппаратными программами, в /sys. ( /procс другой стороны, который с момента введения/sys был зарезервирован для сервисов, не связанных с аппаратным обеспечением, в значительной степени он никогда не ломался несовместимыми способами.)

В итоге,

нарушение пользовательского пространства потребовало бы исправлений на уровне приложения

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


1
Спасибо за ответ. Итак, интерфейсы, которые объявлены стабильными, являются надмножеством системных вызовов POSIX? Мой вопрос об истории заключается в том, как эта практика развивалась. Предположительно, оригинальные версии ядра Linux не беспокоились о нарушении пространства пользователя, по крайней мере, на начальном этапе.
Фахим Митха

3
@FaheemMitha Да, они сделали, с 1991 года . Я не думаю, что подход Линуса развивался, всегда были «интерфейсы для обычных приложений не меняются, интерфейсы для программного обеспечения, которые очень сильно связаны с ядром, меняются очень и очень редко».
Жиль "ТАК - перестань быть злым"

24

В любых взаимозависимых системах есть два основных варианта. Абстракция и интеграция. (Я специально не использую технические термины). С Abstraction вы говорите, что когда вы вызываете API, который, хотя код API может измениться, результат всегда будет одинаковым. Например, когда мы звоним, fs.open()нам все равно, сетевой диск, SSD или жесткий диск, мы всегда получим дескриптор открытого файла, с которым мы можем что-то сделать. С помощью «интеграции» цель состоит в том, чтобы предоставить «лучший» способ сделать что-либо, даже если путь изменится. Например, открытие файла для сетевого ресурса может отличаться от файла на диске. Оба способа довольно широко используются в современном рабочем столе Linux.

С точки зрения разработчиков, это вопрос «работает с любой версией» или «работает с определенной версией». Отличным примером этого является OpenGL. Большинство игр настроено на работу с определенной версией OpenGL. Неважно, если вы компилируете из исходного кода. Если игра была написана для использования OpenGL 1.1, и вы пытаетесь заставить ее работать на 3.x, вы не сможете хорошо провести время. На другом конце спектра некоторые вызовы, как ожидается, будут работать, несмотря ни на что. Например, я хочу позвонить, fs.open()мне не важно, какая у меня версия ядра. Я просто хочу дескриптор файла.

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

С общественной точки зрения, без действительно очень веских причин, абстракция всегда лучше в сложной системе. Например, представьте, fs.open()работает ли по- разному в зависимости от версии ядра. Тогда простая библиотека взаимодействия с файловой системой должна будет поддерживать несколько сотен различных методов «открытого файла» (или, вероятно, блоков). Когда выйдет новая версия ядра, вы не сможете «обновиться», вам придется протестировать каждую часть программного обеспечения, которую вы использовали. Ядро 6.2.2 (подделка) может просто сломать ваш текстовый редактор.

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

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

Например, я представил патч для BuildNotify.py. Не потому, что я альтруист, а потому, что я использую инструмент, и мне нужна особенность. Это было легко, поэтому здесь есть патч. Если бы это было сложно или громоздко, я бы не использовал BuildNotify.pyи нашел бы что-то еще. Если бы каждый раз, когда выходило обновление ядра, мой текстовый редактор ломался, я бы просто использовал другую ОС. Мой вклад в сообщество (пусть и небольшой) не будет продолжаться или существовать, и так далее.

Итак, проектное решение было принято, чтобы абстрагировать системные вызовы, чтобы когда я это делал, fs.open()он просто работал. Это означает сохранение в течение fs.openдолгого времени после fs.open2()завоевания популярности.

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


1
API для пользовательского пространства, API syscall, четко определен (особенно подмножество POSIX) и стабилен, потому что удаление любой его части приведет к повреждению программного обеспечения, которое могли установить люди. Чего у него нет, так это стабильного драйвера API.
pjc50

4
@FaheemMitha, все наоборот. Разработчики ядра могут свободно нарушать API драйверов в любое время, если они исправят все драйверы в ядре до следующего выпуска. Это нарушает API пользовательского пространства или даже делает вещи, не относящиеся к API, которые могут нарушить пользовательское пространство, что вызывает эпическую реакцию Линуса.
Марк

4
Например, если кто-то решит изменить его, вернув в некоторых обстоятельствах другой код ошибки из ioctl (): lkml.org/lkml/2012/12/23/75 (содержит ругательства и личные атаки на ответственного разработчика). Этот патч был отклонен, потому что он сломал бы PulseAudio и, следовательно, весь звук в системах GNOME.
pjc50

1
@FaheemMitha, в основном, def add (a, b); вернуть a + b; end --- def add (a, b); с = а + б; возврат с; end --- def add (a, b); с = а + б + 10; возврат с - 10; Конец - это все «одинаковые» реализации доп. Что его так расстраивает, так это то, что люди делают def add (a, b); возврат (a + b) * -1; end По сути, изменение того, как работают «внутренние» вещи в ядре, это нормально. Изменение того, что возвращается к определенному и «общедоступному» вызову API, не является. Существует два вида вызовов API: «приватный» и «публичный». Он считает, что публичные вызовы API никогда не должны меняться без уважительной причины.
Котейр

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

8

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

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

Недостатки в том, что ядро ​​должно хранить старый код, старые системные вызовы и т. Д. Всегда (или, по крайней мере, после их использования).


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

Нет, извините, я не могу вспомнить, как это произошло. Вы можете написать Линусу или LKML и спросить его.
Cas

2
Mercurial это не ОС. Весь смысл ОС состоит в том, чтобы обеспечить запуск другого программного обеспечения поверх него, и ломать это другое программное обеспечение очень непопулярно. Для сравнения, Windows также очень долго поддерживала обратную совместимость; 16-битный код Windows был только недавно устаревшим.
pjc50

@ pjc50 Это правда , что Mercurial не является ОС, но независимо от того , есть в другое программное обеспечение, даже если только скрипты, которые зависят от него. И потенциально может быть нарушен изменениями.
Фахим Митха
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.