Фон:
Затраты на системные вызовы намного больше, чем на вызовы функций (оценки варьируются от 20 до 100x), в основном из-за переключения контекста из пространства пользователя в пространство ядра и обратно. Обычно встроенные функции сохраняют накладные расходы на вызовы функций, а вызовы функций намного дешевле, чем системные вызовы. Разумеется, разработчики хотели бы избежать некоторых накладных расходов на системные вызовы, заботясь о как можно большем количестве операций в ядре за один системный вызов.
Проблема:
Это создало много (лишние?) Системные вызовы , как sendmmsg () , recvmmsg () , а также Chdir, открытый, lseek и / или символические ссылки сочетаний , как: openat
, mkdirat
, mknodat
, fchownat
, futimesat
, newfstatat
, unlinkat
, fchdir
, ftruncate
, fchmod
, renameat
, linkat
, symlinkat
, readlinkat
, fchmodat
, faccessat
, lsetxattr
, fsetxattr
, execveat
, lgetxattr
, llistxattr
, lremovexattr
, fremovexattr
, flistxattr
, fgetxattr
, pread
, и pwrite
т.д. ...
Теперь Linux добавил, copy_file_range()
что, по-видимому, объединяет системные вызовы read lseek и write. Это только вопрос времени, прежде чем это станет fcopy_file_range (), lcopy_file_range (), copy_file_rangeat (), fcopy_file_rangeat () и lcopy_file_rangeat () ... но, так как вместо X больше вызовов задействовано 2 файла, он может стать X ^ 2 Больше. Ладно, Линус и различные разработчики BSD не позволили бы ему зайти так далеко, но я хочу сказать, что, если бы был системный вызов в пакетном режиме, все (большинство?) Могли бы быть реализованы в пользовательском пространстве и уменьшить сложность ядра, не добавляя много если какие-либо накладные расходы на стороне libc.
Было предложено много сложных решений, которые включают в себя специальный поток системных вызовов для неблокирующих системных вызовов для пакетных системных вызовов; однако эти методы значительно усложняют как ядро, так и пользовательское пространство во многом так же, как libxcb и libX11 (асинхронные вызовы требуют гораздо большей настройки)
Решение?:
Общий пакетный системный вызов. Это позволило бы снизить наибольшую стоимость (переключение нескольких режимов) без сложностей, связанных с наличием специализированного потока ядра (хотя эта функциональность могла бы быть добавлена позже).
По сути, в syscall socketcall () уже есть хорошая основа для прототипа. Просто расширите его от получения массива аргументов до взятия массива возвратов, указателя на массивы аргументов (который включает в себя номер системного вызова), количества системных вызовов и аргумента flags ... что-то вроде:
batch(void *returns, void *args, long ncalls, long flags);
Одним из основных отличий будет то , что аргументы, вероятно , все нужно быть указатели для простоты так , что результаты предыдущих системных вызовов могут быть использованы в последующих системных вызовов (например , дескриптор файла из open()
для использования в read()
/ write()
)
Некоторые возможные преимущества:
- меньше пользовательского пространства -> пространство ядра -> переключение пользовательского пространства
- возможный ключ компилятора -fcombine-syscalls для автоматической пакетной обработки
- необязательный флаг для асинхронной операции (возврат fd для просмотра немедленно)
- возможность реализации будущих комбинированных функций системного вызова в пользовательском пространстве
Вопрос:
Возможно ли реализовать пакетный системный вызов?
- Я пропускаю некоторые очевидные ошибки?
- Я переоцениваю преимущества?
Стоит ли мне беспокоиться о реализации пакетного системного вызова (я не работаю в Intel, Google или Redhat)?
- Я уже исправил свое собственное ядро, но боюсь иметь дело с LKML.
- История показала, что даже если что-то широко полезно для «обычных» пользователей (не корпоративных конечных пользователей, не имеющих доступа к git-записи), оно никогда не может быть принято в апстриме (unionfs, aufs, cryptodev, tuxonice и т. Д.)
Ссылки:
источник
batch
системных вызовов вbatch
системные вызовы, вы можете создать сколь угодно глубокое дерево вызовов произвольных системных вызовов. По сути, вы можете поместить все приложение в один системный вызов.Ответы:
Я пробовал это на x86_64
Патч для 94836ecf1e7378b64d37624fbb81fe48fbd4c772: (также здесь https://github.com/pskocik/linux/tree/supersyscall )
И это похоже на работу - я могу написать привет на fd 1 и world на fd 2 с помощью только одного системного вызова:
В основном я использую:
как универсальный прототип системного вызова, который, как кажется, работает на x86_64, поэтому мой «супер» системный вызов:
Он возвращает количество попыток системных вызовов (
==Nargs
еслиSUPERSYSCALL__continue_on_failure
флаг передан в противном случае>0 && <=Nargs
), и ошибки копирования между пространством ядра и пользовательским пространством сигнализируются с помощью segfaults вместо обычного-EFAULT
.Чего я не знаю, так это того, как это будет портировать на другие архитектуры, но было бы неплохо иметь что-то подобное в ядре.
Если бы это было возможно для всех архитектур, я думаю, что могла бы существовать оболочка пользовательского пространства, которая обеспечивала бы безопасность типов через некоторые объединения и макросы (она могла бы выбрать член объединения на основе имени системного вызова, и тогда все объединения были бы преобразованы в 6 длинных или какой бы ни был архитектурный эквивалент де 6 длин).
источник
open
inwrite
иclose
. Это немного увеличит сложность из-за get / put_user, но, вероятно, оно того стоит. Что касается переносимости IIRC, некоторые архитектуры могут заглушить регистры системного вызова для аргументов 5 и 6, если системный вызов аргумента 5 или 6 пакетирован ... добавление 2 дополнительных аргументов для будущего использования исправит это и может быть использовано в будущем для параметров асинхронного вызова, если установлен флаг SUPERSYSCALL__asyncДва основных момента, которые сразу приходят на ум:
Обработка ошибок: каждый отдельный системный вызов может заканчиваться ошибкой, которая должна быть проверена и обработана вашим кодом пользовательского пространства. Поэтому пакетный вызов должен был бы в любом случае запускать код пространства пользователя после каждого отдельного вызова, поэтому преимущества пакетных вызовов пространства ядра будут сведены на нет. Кроме того, API-интерфейс должен быть очень сложным (если вообще возможно его спроектировать) - например, как бы вы выразили логику, например, «если третий вызов не удался, сделайте что-нибудь и пропустите четвертый вызов, но продолжите с пятым»)?
Многие «комбинированные» вызовы, которые действительно реализуются, предлагают дополнительные преимущества, кроме того, что нет необходимости перемещаться между пользователем и пространством ядра. Например, они часто избегают копирования памяти и использования буферов в целом (например, переносят данные непосредственно из одного места в буфере страницы в другое вместо того, чтобы копировать их через промежуточный буфер). Конечно, это имеет смысл только для конкретных комбинаций вызовов (например, чтение-запись-запись), а не для произвольных комбинаций пакетных вызовов.
источник