From: uncle Bob <ubob at mail.ru>
Newsgroups: http://www.lowlevel.ru
Date: Mon, 24 May 2004 14:31:37 +0000 (UTC)
Subject: Создание нового системного вызова в ОС Linux
Автор: uncle Bob <ubob at mail.ru>
Дата: 11.01.2004
Раздел: Низкоуровневое программирование в Linux
В статье рассмотрена методика добавления в состав ядра ОС Linux нового системного
вызова.
Общий механизм выполнения системных вызовов рассмотрен здесь:
http://zaya.spb.ru/intercept_lnx.txt
Задача - добавить в состав ядра версии 2.4.23 новый системный вызов, который
будет выполнять следующие действия:
- принимает от приложения пользователя указатель на строку ASCII-символов с
кодами 32 - 127 и длину этой строки в байтах;
- преобразует символы строки, находящиеся в диапазоне 0x61 - 0x7A (a - z)
в верхний регистр и возвращает эту строку обратно;
Для решения этой задачи необходимо добавить запись о новом системном вызове в
таблицу системных вызовов ядра sys_call_table. Эта таблица находится в файле
/usr/src/linux/arch/i386/kernel/entry.S. Новый вызов добавляем в самый конец
таблицы:
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/
.long SYMBOL_NAME(sys_exit) /* 1 */
.
.
.
.long SYMBOL_NAME(sys_upcase) /* новый системный вызов, 259 */
Новый системный вызов назовем sys_upcase. Его порядковый номер (для ядра
версии 2.4.23) равен 259.
Теперь необходимо добавить запись о новом вызове в файл
/usr/src/linux/include/asm-i386/unistd.h:
#define __NR_upcase 259
и в файл /usr/include/bits/syscall.h:
#define SYS_upcase __NR_upcase
Теперь осталось написать код, реализующий новый системный вызов. Вот как он
выглядит:
asmlinkage int sys_upcase(char *src, char *dst, int lenght)
{
int i = 0;
char *tmp_buff;
Системный вызов принимает три параметра - указатель на строку, которую
необходимо преобразовать, длину этой строки и указатель на область памяти,
куда необходимо поместить результат. Функция copy_from_user() копирует
исходную строку из адресного пространства пользователя в адресное пространство
ядра, а затем в цикле производится смена регистра символов, находящихся в
диапазоне 0x61 - 0x7A. Результат (преобразованная строка) копируется из
адресного пространства ядра в пространство пользователя при помощи функции
copy_to_user().
Эту функцию поместим в файл /usr/src/linux/fs/open.c, хотя место размещения
особой роли не играет.
После внесения всех изменений в ядро необходимо перекомпилировать.
Для того, чтобы процесс пользователя мог обращаться к новому системному вызову,
создадим в каталоге /usr/include заголовочный файл upcase.h следующего содержания:
Таким образом, системный вызов sys_upcase будет выполняться стандартным для
всех системных вызовов способом: сначала в регистры процессора загружаются
параметры вызова, а затем следует вызов прерывания int 0x80. __NR_##name будет
преобразована в номер системного вызова.
Рассмотрим пример обращения к новому системному вызову из приложения
пользователя:
#include
#include
int main()
{
char *src = "ab12cd34ef"; // эту строку будем преобразовывать
char *dst; // сюда будет помещен результат
int lenght = 0, rez = 0;