LINUX.ORG.RU

Выявлена дыра, позволяющая «уронить» компьютер с Linux под любым пользователем

 ,


0

2

В списке рассылки разработчиков ядра Linux (LKML) был обнародован код, позволяющий через вызов функции ядра socketpair() создать процесс, съедающий 100% процессорного времени и все файловые дескрипторы. Процесс, будучи запущенным от имени любого пользователя, может привести систему к состоянию полной неработоспособности.

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

>>> Подробности

★★★★★

Проверено: post-factum ()
Последнее исправление: post-factum (всего исправлений: 2)
Ответ на: комментарий от tailgunner

> Ситуация из практики: отлаживаемый драйвер делает kfree на неправильном указателе, и в результате отваливается autofs

У меня - ноль внимания: http://pastebin.ca/2007117 Не падает, не ругается. Словно ничего и не было.

> Ненамного.

До тех пор, пока драйвер - это просто пример. В реальности все сложнее. Например, если я пишу драйвер, который читает время в CMOS-е (пример из minix-а), как мне гарантировать, что никакой другой модуль в это же время не начнет писать в 70й порт, чтобы, например, считать параметры оборудования?

> Ты когда последний раз драйвер отлаживал?

Пример выше - не считается? ;) Недавно надо было прикрутить регулировку яркости для неподдерживаемой ядром модели ноутбука. Написанный на коленке маленький модуль ядра решил проблему. А ты? А что?

anonymous
()
Ответ на: комментарий от anonymous

>До тех пор, пока драйвер - это просто пример. В реальности все сложнее. Например, если я пишу драйвер, который читает время в CMOS-е (пример из minix-а), как мне гарантировать, что никакой другой модуль в это же время не начнет писать в 70й порт, чтобы, например, считать параметры оборудования?

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

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

morbo
()
Ответ на: комментарий от anonymous

>Пример выше - не считается? ;)

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

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

morbo
()
Ответ на: комментарий от anonymous

>> И ntfs-3g может завалить ядро (за счет своих собственных багов, не ядерных)?

Во FreeBSD и в OpenSolaris - может. Сейчас ты будешь возражать, что это был баг FUSE, а не ntfs3g, я знаю.

Именно так.

Это ничего не меняет. Появление FUSE не уничтожило магическим образом все баги.

Это меняет всё. Теперь ядро могут завалить только баги в FUSE, а не в 100500 файловых системах, написанных поверх нее.

По твоей ссылке Линус говорит то, что он всегда говорил - общеизвестные вещи, разбавленные FUD.

Таки да, это - общеизвестные вещи, и я о том же.

Болтовня и FUD. А в ядре тем временем появляются FUSE и UIO, что как бы намекает на то, что людям это нужно.

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

А что это будет для UDP-сокетов? А что будет, если все Х-клиенты потеряют соединение с Х-сервером?

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

Они решают проблему взаимовлияния компонентов ядра на уровне Си.

Например?

Тебе нужен пример того, что один процесс не может испортить данные другого?

Можно какой-нибудь пример, который не реализован еще в ядре линукса, но оформление которого в виде отдельного сервиса решило бы «проблему взаимовлияния»?

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

если я пишу драйвер, который читает время в CMOS-е (пример из minix-а), как мне гарантировать, что никакой другой модуль в это же время не начнет писать в 70й порт, чтобы, например, считать параметры оборудования?

Такой гарантии и в текущем ядре нет.

падение USB и сейчас ни на что не влияет.

Речь не об абстрактных «падениях» (что вообще такое «падение» для драйвера - просто куска кода в пространстве ядра?). Речь о том, что ошибка в USB может стать фатальной для _всей системы_.

Ты когда последний раз драйвер отлаживал?

Пример выше - не считается? ;)

Нет.

А ты? А что?

А я сегодня. Насчет «а что?» - ты как-то смело выдаешь фразы про «академическе выдумки» и «в реальности», при том, что путаешь общую память и shared memory.

P.S. я не поклонник микроядерного дизайна. Но, пока ядра пишутся на Си, чем меньше кода будет выполняться в одном адресном пространстве с ядром, тем лучше для надежности. Конечно, всё имеет свою цену, но я считаю, что вынесение из ядра драйверов и ФС вполне оправдано с точки зрения SE и здравого смысла. VMM, VFS, сетевые протоколы, вещи вроде контейнеров и прочего вполне могут оставаться в ядре - им и правда необходима общая память. Но драйверы и ФС - вещи вполне независимые.

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

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

Не будем говорить абстрактно. Вот пример кода не для какого-то теоретического идеального микроядра, а для реального: Example 3: CMOS System Clock Там есть вызовы sys_inb и sys_outb. Каким образом гарантировать то, что никакой другой сервис, который, например, читает параметры оборудования из CMOS, не вызовет в это же время sys_outb в тот же порт? Какой будет правильная реализация такого кода?

anonymous
()
Ответ на: комментарий от tailgunner

> Это меняет всё. Теперь ядро могут завалить только баги в FUSE, а не в 100500 файловых системах, написанных поверх нее.

Не забываем про цену. Цена такого решения: невозможность использовать несколько ядер, и потеря скорость в 5-10 раз даже в случае одного ядра. Это хорошо для всяких sshfs, fuse-smb и curlftpfs, где важна не скорость, а функции. Но совершенно недопустимо для основных файловых систем.

Болтовня и FUD

Согласен. Все это микроядро - болтовня и FUD. :)

А в ядре тем временем появляются FUSE и UIO, что как бы намекает на то, что людям это нужно.

Наоборот. Это намекает на то, что идеи микроядра никому не нужны, вероятно, потому, что они не работают. FUSE ни коим образом не подтверждает, что надо что-то выносить из ядра. Наоборот, FUSE - это ЕЩЕ ОДИН МОДУЛЬ ЯДРА. Это и до FUSE было возможно, через LD_PRELOAD, например. Просто FUSE удобнее.

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

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

Как новый перезапущенный драйвер узнает, куда посылать пакеты, отправленные по сокету с socketid 174? Все открытые дескрипторы перестанут работать. То есть придется перезапускать все приложения, которые открывали сокеты. Это и есть ребут.

Тебе нужен пример того, что один процесс не может испортить данные другого?

Нет, мне нужен пример той проблемы, с которой должно бороться микроядро. Не абстрактное «влияние», а конкретная проблема. Я пока не вижу ни одного примера такой проблемы. А то мы долго можем сравнивать сказку (микроядро) с реальностью (линукс) и доказывать, что сказка лучше. Может пора переходить к более реальным аргументам?

И почему тебе обязательно нужен «не реализованный»?

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

Такой гарантии и в текущем ядре нет.

В текущем ядре миникса? Возможно и нет. А в ядре линукса - есть.

Речь не об абстрактных «падениях». Речь о том, что ошибка в USB может стать фатальной для _всей системы_.

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

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

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

anonymous
()
Ответ на: комментарий от anonymous

> Цена такого решения: невозможность использовать несколько ядер, и потеря скорость в 5-10

Кто и как мерял?

раз даже в случае одного ядра.

Я дро - в смысле вычислительное, core? Никакого отношения к рассматриваемому вопросу это не имеет, userspace-программы отлично параллеляться.

FUSE ни коим образом не подтверждает, что надо что-то выносить из ядра. Наоборот, FUSE - это ЕЩЕ ОДИН МОДУЛЬ ЯДРА.

Ну, с такой логикой тебе в церкви работать.

А UIO - это не то. Это просто интерфейс для работы между ядром и userspace.

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

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

Как новый перезапущенный драйвер узнает, куда посылать пакеты, отправленные по сокету с socketid 174?

Ему скажет ядро (ага, то самое, которое не упало).

Нет, мне нужен пример той проблемы, с которой должно бороться микроядро. Не абстрактное «влияние», а конкретная проблема.

Я не говорю про микроядра - только про userspace-драайверы и ФС. Пример проблемы, невозможной с userspace-драйверами, я уже привел - сбои autofs из-за неправильной работы драйвера левого character device.

В текущем ядре миникса?

Про миникс знаю мало.

А в ядре линукса - есть.

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

Речь не об абстрактных «падениях». Речь о том, что ошибка в USB может стать фатальной для _всей системы_.

Да. И в случае микроядра ошибка в USB тоже может стать фатальной для всей системы

Черт, совсем недавно падение USB-драйвера вызывало только неработоспособность мыши, а сейчас оно уже фатально для всей системы. Что, даже процессы, не использующие USB, тоже умрут?

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

Их больше не на чем писать.

Пока не на чем. Но работа ведется.

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

Какая экзальтация. Так и хочется спросить «Сколько тебе лет? (c)»

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

> Но, пока ядра пишутся на Си, чем меньше кода будет выполняться в одном адресном пространстве с ядром, тем лучше для надежности.

Ядро находится во всех адресных пространствах %)))

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

Не будем говорить абстрактно. Вот пример кода не для какого-то теоретического идеального микроядра, а для реального: Example 3: CMOS System Clock Там есть вызовы sys_inb и sys_outb. Каким образом гарантировать то, что никакой другой сервис, который, например, читает параметры оборудования из CMOS, не вызовет в это же время sys_outb в тот же порт? Какой будет правильная реализация такого кода?

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

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

PRIVATE int cmos_read_byte(offset)
int offset;
{
  unsigned long value = 0;

  sys_lockport(CMOS_PORT);
  sys_lockport(CMOS_PORT + 1);
  sys_outb(CMOS_PORT, offset);
  sys_inb(CMOS_PORT + 1, &value);
  sys_unlockport(CMOS_PORT + 1);
  sys_unlockport(CMOS_PORT);
  return value;
}
morbo
()
Ответ на: комментарий от anonymous

>Не забываем про цену. Цена такого решения: невозможность использовать несколько ядер,

Почему? Драйверы и подсистемы ядра получат возможность работать параллельно. В компьютере 100 устройств? Все 100 устройств будут обслуживаться своим процессом-драйвером, все устройства могут обслуживаться параллельно. При этом система станет очень асинхронной, т.к. каждый ресурс будет обслуживаться своим процессом. Подсистема VFS вообще может представлять собой группу процессов, по одному процессу на каждый жёсткий диск. Подсистема TCP/IP тоже моет представлять собой группу процессов, по одному процессу на каждую сетевую карту.

Произойдёт сбой в сетевой карте - автоматически перезапустится драйвер, перезапустится рабочий процесса TCP/IP, обслуживающий эту сетевую карту. Все программы, установившие соединения через этот процесс TCP/IP и эту сетевую карту получат сообщение об ошибке - недействительный дескриптор. Программы могут отработать эту ошибку как потерю связи и завершить работу или переустановить подключение. Всё стройно и логично. А главное - надёжно.

и потеря скорость в 5-10 раз даже в случае одного ядра.


Развитие процессоров идёт по сценарию увеличение количества ядер.

Это хорошо для всяких sshfs, fuse-smb и curlftpfs, где важна не скорость, а функции. Но совершенно недопустимо для основных файловых систем.


Потерю скорости ещё никто не замерял.


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


А вот и нет. Ошибка в процессе в userspace не приведёт к повреждению структур ядра. Ошибка в модуле ядра в kernelspace может привести к повреждению структур ядра и отказу всей системы.

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

Как новый перезапущенный драйвер узнает, куда посылать пакеты, отправленные по сокету с socketid 174? Все открытые дескрипторы перестанут работать. То есть придется перезапускать все приложения, которые открывали сокеты. Это и есть ребут.


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

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


А с чего это ошибка в userspace-программе должна стать фатальной для ядра?

Только в случае микроядра кода будет больше, он будет сложнее.


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

А значит и самих ошибок будет больше, и отловить ошибку будет труднее.


Пока что наоборот - отловить ошибку труднее в драйвере тогда, когда он работает в kernelspace. В случае микроядра можно будет между микроядром и драйвером внедрить специальную программу-прокси, которая сможет вести журнал общения микроядра и драйвера. Или в Solaris и FreeBSD зря изобретают DTrace?

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


Их больше не на чем писать.


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

morbo
()
Ответ на: комментарий от tailgunner

>> Как новый перезапущенный драйвер узнает, куда посылать пакеты, отправленные по сокету с socketid 174?

Ему скажет ядро (ага, то самое, которое не упало).


Кстати, точно. Если сбой произойдёт в сетевом драйвере, а не в стеке TCP/IP, то все дескрипторы сокетов останутся валидными и программы вообще не заметят перезапуска сетевого драйвера.

morbo
()
Ответ на: комментарий от sv75

>> Но, пока ядра пишутся на Си, чем меньше кода будет выполняться в одном адресном пространстве с ядром, тем лучше для надежности.

Ядро находится во всех адресных пространствах %)))


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

morbo
()
Ответ на: комментарий от morbo

> здесь претензия не к Си, а к общему адресному пространству

Здесь претензия к _сочетанию_ факторов «Си» и «единое адресное пространство». Гарантированно безопасный язык вполне позволит запихнуть все компоненты в единое ядро - вон в Singularity вообще всё работает без защиты памяти.

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

Как они PD/PT кстати инициализируют там? %)

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

>> Цена такого решения: невозможность использовать несколько ядер, и потеря скорость в 5-10

Кто и как мерял?

Первые три теста в гугле, где сравнивались reiserfs, ext3, ntfs3g и еще что-то. Особенно отстает скорость записи. А что, есть другие данные?

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

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

Как новый перезапущенный драйвер узнает, куда посылать пакеты, отправленные по сокету с socketid 174?

Ему скажет ядро (ага, то самое, которое не упало).

А сокет, что, выделялся ядром? То есть сетевая подсистема была в ядре? А что же тогда делал драйвер? И чем тогда микроядро поможет в случае сабжа?

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

Так ты же пишешь драйверы? Открой исходник ядра и посмотри, как там это гарантируется. Я даже подскажу где смотреть - модуль rtc.

Черт, совсем недавно падение USB-драйвера вызывало только неработоспособность мыши, а сейчас оно уже фатально для всей системы. Что, даже процессы, не использующие USB, тоже умрут?

Опять сравнение сказки и реальности. В сказке может умереть. В реальности этого не происходит. Мы можем сравнить реальность (миникс) и реальность (линукс). В линуксе при глюках USB на странном железе у меня отваливалась мышь, но остальное работало. А в миниксе на «странном» железе есть такая штука - циклическая перезагрузка, когда падает все, и ядро уходит в ребут.

Пример проблемы, невозможной с userspace-драйверами, я уже привел - сбои autofs из-за неправильной работы драйвера левого character device.

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

anonymous
()
Ответ на: комментарий от morbo

> По твоей ссылке есть функция cmos_read_byte. Если убрать проверку на ошибки, и добавить предполагаемую блокировку, то пример драйвера, работающего в режиме пользователя, будет таким: [...]

С sys_outb и sys_inb все понятно. А как будут реализованы функции sys_lockport и sys_unlockport? Это - функции моего драйвера? Функции ядра? Вызов другого драйвера?

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

anonymous
()
Ответ на: комментарий от anonymous

>>> Цена такого решения: невозможность использовать несколько ядер, и потеря скорость в 5-10

Кто и как мерял?

Первые три теста в гугле, где сравнивались reiserfs, ext3, ntfs3g и еще что-то.

Ссылку на тест. Что-то я не слышал о реализации reiserfs в виде модуля FUSE.

Как новый перезапущенный драйвер узнает, куда посылать пакеты, отправленные по сокету с socketid 174?

Ему скажет ядро (ага, то самое, которое не упало).

А сокет, что, выделялся ядром? То есть сетевая подсистема была в ядре?

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

И чем тогда микроядро поможет в случае сабжа?

Еще раз повторяю - я говорю не о микроядрах, а о userspace-драйверах и ФС.

UIO - это интерфейс внутри одного драйвера, разделенного на две части - модуль ядра и userspace.

Учи матчасть. Модуль в ядре нужен только тогда, когда обрабатываются прерывания или используется DMA (и даже в случае прерываний - есть стандартный модуль uio_pci_generic, который передает прерывания из ядра в userspace, так что самому драйверу модуль ядра не нужен).

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

Так ты же пишешь драйверы? Открой исходник ядра и посмотри, как там это гарантируется.

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

Черт, совсем недавно падение USB-драйвера вызывало только неработоспособность мыши, а сейчас оно уже фатально для всей системы. Что, даже процессы, не использующие USB, тоже умрут?

Опять сравнение сказки и реальности.

Сравнение двух сказок, причем обе рассказаны тобой.

Пример проблемы, невозможной с userspace-драйверами, я уже привел - сбои autofs из-за неправильной работы драйвера левого character device.

И тут сравнение сказочного микроядра и реального ядра линукса.

Если тебе хочется, сравни userspace-драйверы Minix3 и in-kernel драйверы Linux, И то, и другое - реальность.

Микроядро может гарантировать, что работа с памятью в одном модуле не сможет повлиять на работу с памятью в другом, так? Хорошо. Монолитное ядро ТОЖЕ может это гарантировать

Каким образом?

Жду ссылки на сравнение производительности ядерных ext3 и reiserfs с их аналогами на FUSE.

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

> Произойдёт сбой в сетевой карте - перезапустится драйвер, перезапустится рабочий процесса TCP/IP. Все программы, установившие соединения через этот процесс сообщение об ошибке - недействительный дескриптор. Программы могут отработать эту ошибку и завершить работу или переустановить подключение.

Осталось переписать все работающие с сокетами программы в мире, чтобы они обрабатывали сообщение «недействительный дескриптор». :)

Кстати, процессов, выделявших дескрипторы, несколько будет? Если да, то кто и как будет по дескриптору определять, кому передать вызов write от очередного клиента?

> Потерю скорости ещё никто не замерял.

Сравнение между fuse ФС и native ФС? Да запросто: раз, два, три, четыре Все тесты показывают, что FUSE медленнее. Тут на ядерную ext4 иногда жалуются, что слишком медленно, а вы еще хотите замедлить?

> Ошибка в процессе в userspace не приведёт к повреждению структур ядра.

В Win98 - приводила. Может дело не в том, kernel-space это или user-space, а в защите памяти?

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

Эта фраза не соответствует практике. А раз неверна она, то неверно и следствие:

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

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

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

> А с чего это ошибка в userspace-программе должна стать фатальной для ядра?

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

В микроядере «стабильность системы» == «стабильность ядра и всех его модулей». Модули зависят друг от друга длинными цепочками. Упадет один - остальные попадают как доминошки.

Гипотетический пример: падает модуль usb устройства, возвращая некорректное состояние шины, из-за этого падает модуль usbsound, проверяющий наличие USB-колонок, за ним виснет soundcore, пославший сообщение в usbsound, но не получивший ответа. Этот вис детектится модулем watchdog и тот дает команду перезапустить soundcore. Ядро его убивает, затем пробует его считать с диска и загрузить снова. Для чтения она дает команду sata-модулю, который перед чтением важного модуля с диска делает sync, пытаясь также сбросить буферы и на воткнутую флешку (тоже sata), но usb умер, флешки нет, и sata тоже падает. Остается только ядро. Ядро живое. А система умерла.

Чтобы таких примеров не было - надо очень постараться. Каждый модуль должен корректно обрабатывать падение ЛЮБОГО из модулей, с которым он взаимодействует. Каждый вызов должен быть написан с учетом того, что функция НИКОГДА не вернет результат.

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

anonymous
()
Ответ на: комментарий от morbo

> Сетевая карта работает в режиме опроса. Когда придёт очередь работы драйвера, он обработает все пакеты за один запуск и передаст все пакеты системе TCP/IP. Принципиально это ничем не отличается от текущей ситуации. Ну потребуется на сто(тысячу) килобайт пакетов TCP SYN одно дополнительное переключение контекста. И что?

«Принципиально это ничем не отличается от текущей ситуации.»

Отличается. Сейчас сетевая карта не хранит сто(тысячу) килобайт пакетов.

Кстати, точно. Если сбой произойдёт в сетевом драйвере, а не в стеке TCP/IP, то все дескрипторы сокетов останутся валидными и программы вообще не заметят перезапуска сетевого драйвера.

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

anonymous
()
Ответ на: комментарий от tailgunner

> Ссылку на тест. Что-то я не слышал о реализации reiserfs в виде модуля FUSE.

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

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

То есть код ты не смотрел? Ну как хочешь...

> Ровно так же можно сделать и с userspace-драйверами.

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

> Сравнение двух сказок, причем обе рассказаны тобой.

Эти «сказки» были ответом на «сказки» про autofs.

>> Микроядро может гарантировать, что работа с памятью в одном модуле не сможет повлиять на работу с памятью в другом, так? Хорошо. Монолитное ядро ТОЖЕ может это гарантировать

> Каким образом?

Да куча способов. Например, так же, как это делает микроядро, только без создания процесса - заданием параметров защиты памяти. Можно и с созданием процесса - в ядре есть потоки. Например, Mondriaan Memory Protection - очередное академическое исследование, которое даже в LKML не упоминалось. И наконец, если автор модуля боится повредить чужую память, он может использовать для работы с памятью мелкий самописный аллокатор с проверками.

> Еще раз повторяю - я говорю не о микроядрах, а о userspace-драйверах и ФС.

Да? Значит я неверно понял фразу:

Микроядро не является «серебряной пулей», но многие проблемы всё же решает.

Мне только было интересно, какие же проблемы оно решает.

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

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

Это не только теория. Практическая ситуация с гигабитными драйверами в миниксе это подтверждает. Вообще, с гигабитом в миниксе все не очень хорошо, но как минимум одно исследование гуглится. Там хорошо видно, что ГИГАБИТ с трудом вытягивает 10MB/sec.

anonymous
()
Ответ на: комментарий от anonymous

>> Ссылку на тест. Что-то я не слышал о реализации reiserfs в виде модуля FUSE.

Ссылки - в соседнем сообщении.

Там сравниваются _разные_ реализации NTFS в userspace и ядре, это не очень интересно, потому что реализации тупо разные. Вот если бы сравнивались ext3 и ext3-fuse, reiserfs и reiserfs-fuse, да хоть fat32-fuse - было бы интересно, а так... Кстати, в тестах IOZone read и MP3 Encoding даже NTFS-3g выступила вполне достойно.

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

То есть код ты не смотрел? Ну как хочешь...

Я смотрел код. Там есть request_region, и что? Если я не сделал request_region, ядро всё равно не помешает мне выдать inb или outb на порт RTC. У него просто нет такой возможности.

Сравнение двух сказок, причем обе рассказаны тобой.

Эти «сказки» были ответом на «сказки» про autofs.

Ну да, если ты не видел, воспроизвести не смог - значит, сказка. Ты не страус часом?

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

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

*shrug* Причем тут миникс? Ну, написал студент драйвер, выяснил, что в Minix3 проблемы. Какое это отношение имеет к Linux? Где доказательства, что проблемы неразрешимы? Ответы «никакого» и «никаких».

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

>А сокет, что, выделялся ядром? То есть сетевая подсистема была в ядре? А что же тогда делал драйвер? И чем тогда микроядро поможет в случае сабжа?

Сокет выделялся сетевой подсистемой - процессом, который при падении процесса-драйвера продолжает работать.

morbo
()
Ответ на: комментарий от anonymous

>С sys_outb и sys_inb все понятно. А как будут реализованы функции sys_lockport и sys_unlockport? Это - функции моего драйвера? Функции ядра? Вызов другого драйвера?

Это функции ядра. А твой драйвер - обычный процесс, работающий в режиме userspace и делающий системные вызовы sys_outb, sys_inb, sys_lockport и sys_unlockport.

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


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

morbo
()
Ответ на: комментарий от anonymous

>Отличается. Сейчас сетевая карта не хранит сто(тысячу) килобайт пакетов.

А как же тогда работает режим так называемый polling в FreeBSD? Работает не для всех сетевых карт, но

Кстати, точно. Если сбой произойдёт в сетевом драйвере, а не в стеке TCP/IP, то все дескрипторы сокетов останутся валидными и программы вообще не заметят перезапуска сетевого драйвера.


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


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

morbo
()
Ответ на: комментарий от anonymous

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

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

Драйвер заблокировал порт и упал? Блокировка снимается. Программа открыла файл и упала - файл закрывается. Разница есть? Разницы нет.

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


самописный


Велосипедостроение в действии?

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


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


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

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

morbo
()
Ответ на: комментарий от tailgunner

> Там сравниваются _разные_ реализации NTFS в userspace и ядре, это не очень интересно, потому что реализации тупо разные.

Очевидно, реализация в ядре и в fuse всегда будут разные. В этом и смысл сравнения - проверить, какая реализация будет быстрее. Пока что FUSE всегда медленнее.

> Вот если бы сравнивались ext3 и ext3-fuse, reiserfs и reiserfs-fuse, да хоть fat32-fuse - было бы интересно, а так...

Так все равно они будут разные. Хотя, если хочется - проведи эксперимент, сравни vfat с fatfuse или libfat. Про первую автор итак пишет, что она раз в шесть медленнее.

> Кстати, в тестах IOZone read и MP3 Encoding даже NTFS-3g выступила вполне достойно.

Логично, когда мало вызовов, или когда скорость итак низкая (при кодировании mp3 она определенно низкая), то и тормоза заметны не будут.

> Я смотрел код. Там есть request_region, и что? Если я не сделал request_region, ядро всё равно не помешает мне выдать inb или outb на порт RTC. У него просто нет такой возможности.

Ну да, да... А еще там есть spin_lock_irq и spin_unlock_irq, и они гарантируют, что никто ничего не вызовет. Но в ядре они могут быть, а в юзерспейсе - нет.

> Причем тут миникс? Какое это отношение имеет к Linux?

Это подтверждение теории, только и всего. Теория говорит: если выносить драйвера в userspace то могут быть проблемы со сложностью и БУДУТ проблемы с производительностью. Практика реализация драйверов в userspace на примере minix-а подтверждает: проблемы ЕСТЬ. :)

> Где доказательства, что проблемы неразрешимы?

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

anonymous
()
Ответ на: комментарий от morbo

> Сокет выделялся сетевой подсистемой - процессом, который при падении процесса-драйвера продолжает работать.

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

С дескрипторами, кстати, в этом случае будет отдельный вопрос. Например, такой процесс означает, что он обслуживает только дескрипторы сокетов, так? Дескрипторы файлов обслуживает другой процесс? То есть с вероятностью близкой к 100% файловый дескриптор, выданный мне при открытии очередного файла, будет совпадать с сетевым дескриптором, выданным при открытии сокета?

anonymous
()
Ответ на: комментарий от anonymous

>> Там сравниваются _разные_ реализации NTFS в userspace и ядре, это не очень интересно, потому что реализации тупо разные.

Очевидно, реализация в ядре и в fuse всегда будут разные.

Очевидно, что Солнце вращается вокруг Земли.

если хочется - проведи эксперимент, сравни vfat с fatfuse или libfat.

Я немного представляю себе сложность этого эксперимента, так что нет, спасибо. Кроме того, он не имеет смысла - FUSE уже в ядре и ее никто не собирается выбрасывать.

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

«FatFuse is a linux fuse implementation capable of reading FAT16 and FAT32 filesytems. It is written in python».

«Version 0.4.0 22 March 2007»

Т.е. ФС, написанная на Питоне, и заброшенная 3 года на версии 0.4, всего в 6 раз медленнее ядерного драйвера. Не так уж плохо.

Ну да, да... А еще там есть spin_lock_irq и spin_unlock_irq, и они гарантируют

Бугага. Я не сделал вызова request_region, и spinlock_lock вызывать не стану. Ты правда не понимаешь, что порт 70 защищен от внутриядерного кода только добровольным соглашением драйверов?

Теория говорит: если выносить драйвера в userspace то могут быть проблемы со сложностью и БУДУТ проблемы с производительностью.

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

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

Эх... и этот человек спорит о userspace-драйверах.

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

>Ну да, да... А еще там есть spin_lock_irq и spin_unlock_irq, и они гарантируют, что никто ничего не вызовет. Но в ядре они могут быть, а в юзерспейсе - нет.

Есть код, который имеет непосредственный доступ к оборудованию - он будет в ядре. А есть код, который будет обращаться к оборудованию через посредника - ядро. Этот код и будет в userspace. То, что spin_lock_irq нельзя _выполнить_ в userspace не означает, что код, выполняющий _вызов_ этой функции, должен быть тоже внесён в ядро. То, что нельзя выполнить в userspace, можно попросить сделать у ядра.

morbo
()
Ответ на: комментарий от anonymous

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

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

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


С дескрипторами, кстати, в этом случае будет отдельный вопрос. Например, такой процесс означает, что он обслуживает только дескрипторы сокетов, так? Дескрипторы файлов обслуживает другой процесс? То есть с вероятностью близкой к 100% файловый дескриптор, выданный мне при открытии очередного файла, будет совпадать с сетевым дескриптором, выданным при открытии сокета?


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

morbo
()
Ответ на: комментарий от morbo

> Драйвер заблокировал порт и упал? Блокировка снимается. Программа открыла файл и упала - файл закрывается. Разница есть? Разницы нет.

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

Велосипедостроение в действии?

Велосипедостроением это будет если есть другие такие же реализации. Но тогда и самому писать не придется, можно взять готовую. А если аналога нет - то нет и «велосипеда».

Конечно, между микроядрами и монолитными ядрами есть ещё много промежуточных техник и решений. ... останется сделать несколько шагов в сторону микроядра

Пока что мы делаем все наоборот. :) Мы медленно превращаем микроядро в монолитное. Сначала мы переносим в ядро менеджер блокировок и управление памятью. Позже у нас там появится paging. И мы уже упоминали, что драйвера будут работать через shared memory. Следом мы перенесем в ядро файловый кеш, как часть менеджера памяти. Потом там же окажется работа с дескрипторами, иначе нельзя будет прозрачно реализовать работу с файлами и сокетами. Последними туда переползут драйвера и файловые системы, чтобы обеспечить нужную производительность, все равно они итак работали через огромные куски shared memory. И мы получим Линукс. :)

Кстати, почему-то никого особо не смущает система виртуализации Xen

А причем тут Xen? Он выполняет другие функции. В частности он не дает двум гостям использовать общий аппаратный ресурс. В линуксе я могу запусить две копии glxgears, и они обе будут рендериться на видеокарте. А в xen-е два гостя могут использовать аппаратные возможности рендеринга одной видеокарты?

anonymous
()
Ответ на: комментарий от tailgunner

> Я немного представляю себе сложность этого эксперимента, так что нет, спасибо.

В чем же сложность? Вон, phoronix тестирует, ничего сложного. Тестируются же не сферические файловые системы в вакууме, а реальные.

Я не сделал вызова request_region, и spinlock_lock вызывать не стану. Ты правда не понимаешь, что порт 70 защищен от внутриядерного кода только добровольным соглашением драйверов?

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

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

На этот вопрос и отвечает практика. Причем по всем параметрам примерно одинаковые результаты, и по сравнению minix-linux в скорости сети, и по сравнению fuse-native в скорости ФС - скорость падает в разы.

anonymous
()
Ответ на: комментарий от anonymous

>> Я не сделал вызова request_region, и spinlock_lock вызывать не стану. Ты правда не понимаешь, что порт 70 защищен от внутриядерного кода только добровольным соглашением драйверов?

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

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

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

Грустно.

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

> Если дескриптор стал недействительным, то любые операции с ним будут завершаться с сообщением об ошибке «несуществующий дескриптор». Эту ситуацию можно обрабатывать точно так же, как ситуацию «ошибка чтения» или «ошибка записи» или «потеря связи».

Не так. Эту ситуацию нужно обрабатывать ДОПОЛНИТЕЛЬНО к тем ситуациям ошибки чтения и др. Если программа этого не делает... А многие не делают.

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

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

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

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

anonymous
()
Ответ на: комментарий от tailgunner

> Грустно.

А что грустного? Наоборот, это же хорошо. Функция spin_lock_irq что-то делает, не просто так она имеет такое имя. :)

anonymous
()
Ответ на: комментарий от anonymous

> А что грустного?

Что ты нахватался умных слоа, но не понимаешь элементарных вещей.

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

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

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

В ней есть потоки ядра, количество которых равняется количеству процессорных ядер системы. Каждому потоку ядра монопольно принадлежат определённые ресурсы и определённые процессы. Процессы могут обращаться непосредственно только к тем ресурсам, которые принадлежат тому же потоку ядра. Чтобы обратиться к чужому ресурсу, процесс просит свой поток ядра перевести себя на другой поток ядра.

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

Таким образом все блокировки в системе сводятся к обозначению принадлежности ресурсов к ядрам и к критическим секциям.

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

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

Пока что мы делаем все наоборот. :) Мы медленно превращаем микроядро в монолитное. Сначала мы переносим в ядро менеджер блокировок и управление памятью. Позже у нас там появится paging. И мы уже упоминали, что драйвера будут работать через shared memory. Следом мы перенесем в ядро файловый кеш, как часть менеджера памяти. Потом там же окажется работа с дескрипторами, иначе нельзя будет прозрачно реализовать работу с файлами и сокетами. Последними туда переползут драйвера и файловые системы, чтобы обеспечить нужную производительность, все равно они итак работали через огромные куски shared memory. И мы получим Линукс. :)


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

А причем тут Xen? Он выполняет другие функции. В частности он не дает двум гостям использовать общий аппаратный ресурс.


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

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

В линуксе я могу запусить две копии glxgears, и они обе будут рендериться на видеокарте. А в xen-е два гостя могут использовать аппаратные возможности рендеринга одной видеокарты?


А почему-бы и нет? Только прошу учитывать, что Xen - это не операционная система, а пример работающего микроядра. Микроядро является только частью операционной системы.

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

morbo
()
Ответ на: комментарий от anonymous

>Не так. Эту ситуацию нужно обрабатывать ДОПОЛНИТЕЛЬНО к тем ситуациям ошибки чтения и др. Если программа этого не делает... А многие не делают.

Я вообще понимаю, что «старая программа», о которой идёт речь, работает через слой эмуляции POSIX. Слой эмуляции POSIX может преобразовывать ошибку «недействительный идентификатор» в «ошибку чтения», тогда программа будет работать так, как и было расчитано программистом. Если же программа изначально пишется нативно, без использования слоя POSIX, то почему бы программисту и не предусмотреть ситуацию «недействительный идентификатор»?

Не может. Тогда не будет работать вызов select. Дескриптор должны быть натуральным числом, и чем меньше - тем лучше.


Они и останутся натуральными числами. А почему они должны быть маленькими?

Замечу, что select - это ядерный вызов.


А будет библиотечная функция слоя эмуляции POSIX. А «под капотом» это будет сообщение, отправляемое процессом стека TCP/IP процессу-клиенту.

morbo
()
Ответ на: комментарий от morbo

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

Описался:

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

morbo
()
Ответ на: комментарий от morbo

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

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

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

это система с гибридным ядром

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

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

Да, в линуксе тоже есть потоки ядра. В dragonfly просто такой специфический шедулинг.

Каждому потоку ядра монопольно принадлежат определённые ресурсы и определённые процессы.

Да? То есть работа с дисковыми ресурсами, например, может быть только в одном потоке? А работа с сетью в другом? А если программа работает и с сетью и с дисками, то она должна прыгать между потоками? Еще остались те, кто думают, что такой метод проще? ;)

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

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

С нетерпением жду ядерного модуля для просмотра видео.

Уже есть. nvidia+libvdpau

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

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

Для примера, на гигабитном ethernet нужно получать до 60000 пакетов в секунду. Даже перемещение мыши по экрану вызывает в 10 раз больше переключений контекста, чем видеоплеер - в этом легко убедиться, посмотрев vmstat.

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

Верно. Только не разграничением, а управлением доступом. Любая программа должна иметь возможность обратиться к любому ресурсу. Без этой задачи ядром можно назвать что попало, например, BIOS или GRUB. Именно эту задачу Xen НЕ решает. И именно в этой задаче, в вопросе взаимодействия - сложность качественной реализации ядра, особенно микроядра.

anonymous
()
Ответ на: комментарий от morbo

>> Не может. Тогда не будет работать вызов select. Дескриптор должны быть натуральным числом, и чем меньше - тем лучше.

А почему они должны быть маленькими?

Для этого надо объяснять, как работает select, а мне - лень. :-) Предлагаю посмотреть его реализацию.

Замечу, что select - это ядерный вызов.

А будет библиотечная функция слоя эмуляции POSIX.

Ну... Это уже никуда не годится. Каким образом библиотечная функция будет перехватывать ядерные вызовы? Попытаюсь объяснить на пальцах. Есть такой архиватор RAR, он распространяется в виде статически слинкованного 32-битного бинарника. И даже собранный лет пять назад он работает в современных 64-битных (!) ядрах. Все потому, что он использует стандартизированные вызовы. Как теперь наличие слоя эмуляции поможет мне запустить этот RAR в нашей гипотетической системе с микроядром? Как я заставлю его вызывать методы библиотеки, а не ядра?

Проблема, на самом деле, глубже. Например, gcc генерирует точно такие же бинарники, которые точно так же будут делать ядерные вызовы... Или мы под наше микроядро напишем свой компилятор и glibc, которые будут заменять ядерные вызовы на наши сообщения? Микроядро - это просто... ;-)

anonymous
()
Ответ на: комментарий от anonymous

>> Ты не понял. ... Т.е. драйвер падает, пакеты перестают доходить, драйвер перезапускается, делается повторная передача пакетов, и приложение видит всего лишь паузу.

Как новый перезапущенный драйвер узнает, куда посылать пакеты, отправленные по сокету с socketid 174? Все открытые дескрипторы перестанут работать. То есть придется перезапускать все приложения, которые открывали сокеты. Это и есть ребут.

Вы оба не вполне правы. Да, все файловые дескрипторы станут невалидными, но это не ребут. Это, например, EBADF в ошибках POSIX и программы открывающие сокет вполне могут реконнектиться (и, пожалуй, должны так делать) получив такую ошибку. Разработчики программ ведь всегда проверяют возвращаемые коды ошибок, не так ли? :)

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

rudchenkos
()
Ответ на: комментарий от rudchenkos

О, тут что-то шевелится? 0_о

Да, все файловые дескрипторы станут невалидными, но это не ребут. Это, например, EBADF в ошибках POSIX

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

Разработчики программ ведь всегда проверяют возвращаемые коды ошибок, не так ли? :)

А проблема как раз в том, что дескриптор, который БЫЛ валидным на предыдущей итерации цикла вдруг стал невалидным на следующей. Многие ли распространенные программы учитывают и проверяют сейчас ТАКУЮ ошибку? Или программу таки придется перезапустить для «обработки» ошибки?

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

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

anonymous
()
Ответ на: комментарий от tailgunner

>> Да, все файловые дескрипторы станут невалидными

Не станут.

Потому что они будут обрабатываться основным ядром системы? ;)

anonymous
()
Ответ на: комментарий от anonymous

Да, все файловые дескрипторы станут невалидными

Не станут.

Потому что они будут обрабатываться основным ядром системы? ;)

Нет, конечно.

В качестве новогоднего подарка вот тебе ответ (я сейчас в настроении объяснять очевидные вещи :))

Итак, сервер сети и драйвер карты - это разные процессы; в сервере сети есть:

struct socket {
 unsigned open:1; /* этот сокет открыт */
 /* тут куча полей */
 pid_t driver; /* это PID процесса-драйвера сетевой карты */
};

и

struct socket sockets[MAX_NR_SOCKETS];

Клиенты в качестве файловых дескрипторов получают _индексы_ в массиве sockets. Теперь предполагаем, что процесс-драйвер рухнул и был перезапущен. Это значит, что сетевой сервер должен будет... правильно, поменять значение поля driver. Как нетрудно понять, все индексы в массиве останутся неизменными => у клиентов сервера сети все дескрипторы сокетов останутся валидными.

Если тебе и это объяснение непонятно, то просто найди себе другую работу :D

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

> В качестве новогоднего подарка вот тебе ответ (я сейчас в настроении объяснять очевидные вещи :))

Замечательно. :) Тогда и я последую этому примеру. :)

Итак, сервер сети и драйвер карты - это разные процессы

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

Клиенты в качестве файловых дескрипторов получают _индексы_ в массиве sockets.

Так просто оно работать не будет. Идея, в целом, правильная, если бы на каждый процесс был свой «сервер сети», как это сделано в монолитном ядре. Но в нашем микроядре «сервер» - общий для всех, а значит в нем должен быть индивидуальный массив sockets для каждого процесса. Ведь дескриптор 1 есть у всех процессов, и у всех он - разный. Так что в целом получится что-то вроде map-а массива сокетов.

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

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

Такие вот неожиданные проблемы вылезают в микроядре на каждом шагу.

anonymous
()
Ответ на: комментарий от anonymous

> Но сабжевый баг ведь был не в драйвере карты, а как раз в этом самом «сервере сети»

Речь идет не о сабжевом баге, а о баге в драйвере сетевой карты.

Такие вот неожиданные проблемы вылезают в микроядре на каждом шагу.

Ребенок открывает для себя мир. Еще немного, и он откроет для себя, что все эти «проблемы» были решены еще до его рождения.

tailgunner ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.