Системные вызовы накладные расходы
Я только начал изучать системные вызовы. Я хотел бы знать, что вызывает издержки, когда системный вызов сделан.
Например, если мы рассмотрим функцию getpid(), когда для getpid () выполняется системный вызов, я предполагаю, что если элемент управления находится в данный момент в дочернем процессе, то необходимо выполнить переключение контекста, чтобы войти в родительский процесс, чтобы получить pid., Может ли это способствовать накладным расходам?
Кроме того, когда вызывается getpid(), будет некоторая передача метаданных через границу пользовательского пространства, а также вход и выход из ядра. Так будет ли постоянное переключение между пользовательским пространством и ядром вызывать некоторые издержки?
3 ответа
Например, если мы рассмотрим функцию getpid(), когда для getpid () выполняется системный вызов, я предполагаю, что если элемент управления находится в данный момент в дочернем процессе, то необходимо выполнить переключение контекста, чтобы войти в родительский процесс, чтобы получить pid.,
Здесь не нужно переключать контекст на дочерний процесс - ядро должно иметь в своем распоряжении все необходимые данные. В большинстве случаев ядро переключает контексты только на процесс пользовательского пространства в планировщике или при возврате из системного вызова.
Также, когда вызывается getpid(), происходит передача метаданных через границу пользовательского пространства, а также вход и выход из ядра. Так будет ли постоянное переключение между пользовательским пространством и ядром вызывать некоторые издержки?
Да, если getpid()
часто звонили, накладные расходы наверняка накапливались. Есть несколько доступных подходов, которые могут избежать этих издержек для простых системных вызовов "getter", таких как getpid()
а также gettimeofday()
; Одним из таких подходов, который когда-то использовался в Linux, было сохранение (известного) результата системного вызова на специальной странице памяти. (Этот механизм был известен как vsyscall.)
Я сделал несколько более точных тестов на Linux 86-64 (скомпилирован с -O3):
ns relative(rounded) function
4.89 1 regular_function //just a value return
6.05 1 getpid //i think this one has got to be cached
17.7 4 sysconf(_SC_PAGESIZE)
22.6 5 getauxval(AT_EUID)
25.4 5 sysconf(_SC_NPROCESSORS_ONLN)
27.1 6 getauxval(AT_UID)
54.1 11 gettimeofday
235 48 geteuid
261 53 getuid
264 54 getppid
314 64 sysconf(_SC_OPEN_MAX)
622 127 pread@0 // IO funcs benchmarked with 1 bytes quantities
638 130 read // through a 1 Gigabyte file
1690 346 write
1710 350 pwrite@0
Самые дешевые "системные вызовы" - это те, которые проходят через вспомогательный вектор (~20–30 нс). Вызовы посередине (~250–310 нс) должны наиболее точно отражать среднюю нагрузку, поскольку с ними не нужно много работать в ядре.
Для сравнения, пары malloc+free с запросами небольшого размера (<64 байта => без системных вызовов) стоят около 70-80 нс (см. Мой ответ в разделе Стоимость статического выделения памяти по сравнению с динамическим выделением памяти в C).
Простите за обобщение (а не за каждое предложение).
Вызов системной службы (такой как возврат информации о процессе) имеет оболочку режима пользователя. Эта оболочка вызывает исключение, которое маршрутизируется через системную диспетчерскую таблицу, которая вызывает системную службу режима ядра.
Переключение в режим ядра требует чего-то похожего на переключение контекста процесса. Например, он требует перехода от пользовательского стека к стеку ядра (и других системных зависимых изменений).
Вызывающий процесс предоставляет буфер возврата в пользовательском режиме. Системная служба системы проверит, чтобы убедиться, что это допустимый буфер режима пользователя, прежде чем записывать данные ответа в целях безопасности.
Библиотечная функция, такая как getpid, которая возвращает только информацию о текущем процессе, может не требовать переключения в режим ядра.