Окончательные шаги регистрации системного вызова
Окончательные шаги регистрации системного вызова
После того как системный вызов написан, процедура его регистрации в качестве официального системного вызова тривиальна и состоит в следующем.
• Добавляется запись в конец таблицы системных вызовов. Это необходимо сделать для всех аппаратных платформ, которые поддерживают этот системный вызов (для большинства системных вызовов — это все возможные платформы). Положение системного вызова в таблице — это номер системного вызова, начиная с нуля. Например, десятая запись таблицы соответствует системному вызову с номером девять.
• Для всех поддерживаемых аппаратных платформ номер системной функции должен быть определен в файле include/linux/unistd.h.
• Системный вызов должен быть вкомпилирован в образ ядра (в противоположность компиляции в качестве загружаемого модуля[30]). Это просто соответствует размещению кода в каком-нибудь важном файле каталога kernel/.
Давайте более детально рассмотрим эти шаги на примере функции системного вызова, foo(). Вначале функция sys_fоо() должна быть добавлена в таблицу системных вызовов. Для большинства аппаратных платформ таблица системных вызовов размещается в файле entry.S и выглядит примерно следующим образом.
ENTRY (sys_call_table)
.long sys_restart_syscall / * 0 * /
.long sys_exit
.long sys_fork
.long sys_read
.long sys_write
.long sys_open /* 5 */
...
.long sys_timer_delete
.long sys_clock_settime
.long sys_clock_gettime /* 280 */
.long sys_clock_getres
.long sys_clock_nanosleep
Необходимо добавить новый системный вызов в конец этого списка:
.long sys_foo
Нашему системному вызову будет назначен следующий свободный номер, 283, хотя мы этого явно и не указывали. Для каждой аппаратной платформы, которую мы будем поддерживать, системный вызов должен быть добавлен в таблицу системных вызовов соответствующей аппаратной платформы (нет необходимости получать номер системного вызова для каждой платформы). Обычно необходимо сделать системный вызов доступным для всех аппаратных платформ. Следует обратить внимание на договоренность указывать комментарии с номером системного вызова через каждые пять записей, что позволяет быстро найти, какой номер какому системному вызову соответствует.
Далее необходимо добавить номер системного вызова в заголовочный файл include/asm/unistd.h, который сейчас выглядит примерно так.
/*
* This file contains the system call numbers.
*/
#define __NR_restart_syscall 0
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
#define __NR_open 5
...
#define __NR_mq_unlink 278
#define __NR_mq_timedsend 279
#define __NR_mq_timedreceive 280
#define __NR_mq_notify 281
#define __NR_mq_getsetattr 282
В конец файла добавляется следующая строка.
#define __NR_foo 283
В конце концов необходимо реализовать сам системный вызов foo(). Так как системный вызов должен быть вкомпилорован в образ ядра во всех конфигурациях, мы его поместим в файл kernel/sys.c. Код необходимо размещать в наиболее подходящем файле. Например, если функция относится к планированию выполнения процессов, то ее необходимо помещать в файл sched.c.
/*
* sys_foo - всеми любимый системный вызов.
*
* Возвращает размер стека ядра процесса
*/
asmlinkage long sys_foo(void) {
return THREAD_SIZE;
}
Это все! Загрузите новое ядро. Теперь из пространства пользователя можно вызвать системную функцию foo().