Академический Документы
Профессиональный Документы
Культура Документы
SCI
Реализация системных вызовов в Linux различается в зависимости от архитектуры, но
она также может отличатся в рамках одной архитектуры. Например, старые процессоры
x86 использовали механизм прерываний для перехода из пространства пользователя в
пространство ядра, но новые процессоры IA-32 обеспечивают инструкции, которые
оптимизируют этот переход (используя инструкции sysenter и sysexit). Поскольку
существует очень много вариантов и конечный результат является довольно сложным,
то я буду придерживаться поверхностного обсуждения.
Для добавления нового системного вызова в ядро, необходимо проделать три основных
шага:
#include <linux/linkage.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <asm/uaccess.h>
asmlinkage long sys_getjiffies( void )
{
return (long)get_jiffies_64();
}
asmlinkage long sys_diffjiffies( long ujiffies )
{
return (long)get_jiffies_64() - ujiffies;
}
asmlinkage long sys_pdiffjiffies( long ujiffies,
long __user *presult )
{
long cur_jiffies = (long)get_jiffies_64();
long result;
int err = 0;
if (presult) {
result = curr_jiffies — ujiffies;
err = put_user( result, presult );
}
На втором шаге я обновляю заголовочные файлы, чтобы освободить место для новых
функций в таблице системных вызовов. Для этого я обновляю заголовочный файл
arch/x86/include/asm/unistd_32.h с номером нового системного вызова. Изменения
выделены жирным шрифтом и показаны в Листинге 2.
Теперь я имею в ядре системные вызовы и номера для их представления. Все что
осталось сделать, это провести зависимости между номерами (таблицей индексов) и
самими функциями. Третий шаг — обновление таблицы системных вызовов. Как показано
в Листинге 3, я обновил файл arch/x86/kernel/syscall_table_32.S для новых функций,
к которым можно будет обратиться по индексам, показанным в Листинге 2.
obj-y += my_call.o
Чтение и запись пользовательской памяти
Для перемещения больших объектов, таких как структуры или массивы, Вы можете
использовать функции copy_from_user и copy_to_user. Эти функции перемещают весь
блок данных между пространством пользователя и пространством ядра. Функция
copy_from_user перемещает блок данных из пространства пользователя в пространство
ядра, а copy_to_user перемещает блок данных из пространства ядра в пространство
пользователя:
Теперь, когда в ядро включены несколько новых системных вызовов, давайте посмотрим
на то, что необходимо для их использования в приложении пространства пользователя.
Есть два пути, которые позволяют использовать новые системные вызовы. Первый метод
удобный (но не настолько, чтобы вы захотели включить его в производительный код),
второй метод традиционный и требует чуть больше усилий.
В первом методе, Вы вызываете Ваши новые системные вызовы через функцию syscall. В
функции syscall Вы можете вызвать системный вызов указав его номер и набор
аргументов. Например, маленькая программа, показанная в Листинге 4 вызывает Вашу
функцию sys_getjiffies, используя ее номер.
#include <linux/unistd.h>
#include <sys/syscall.h>
#define __NR_getjiffies 320
int main()
{
long jiffies;
jiffies = syscall( __NR_getjiffies );
printf( "Current jiffies is %lx\n", jiffies );
return 0;
}
syscall( SYS_getpid )
Макрос _syscall принимает до шести аргументов (хотя здесь показаны только три).
Здесь показано, как используется макрос _syscall, чтобы сделать Ваши новые
системные вызовы видимыми пространству пользователя. Листинг 6 демонстрирует
приложение, которое использует все Ваши системные вызовы, определив их с помощью
макроса _syscall.
#include <stdio.h>
#include <linux/unistd.h>
#include <sys/syscall.h>
#define __NR_getjiffies 320
#define __NR_diffjiffies 321
#define __NR_pdiffjiffies 322
_syscall0( long, getjiffies );
_syscall1( long, diffjiffies, long, ujiffies );
_syscall2( long, pdiffjiffies, long, ujiffies, long*,
presult );
int main()
{
long jifs, result;
int err;
jifs = getjiffies();
printf( "difference is %lx\n", diffjiffies(jifs) );
err = pdiffjiffies( jifs, &result );
if (!err) {
printf( "difference is %lx\n", result );
} else {
printf( "error\n" );
}
return 0;
}
Заметьте, что индексы __NR необходимы в этом приложении, потому что макрос
_syscall использует имя функции, чтобы создать индекс __NR (getjiffies ->
__NR_getjiffies). Результат заключается в том, что теперь Вы можете вызывать Ваши
функции ядра, используя их имена, также как и любой другой системный вызов.
на
strace ./call_test1
...
SYS_337(0xb78b0ff4, 0x8048460, 0xbf825588, 0xb77a345, 0xb78d3d20) = 468207
...
write(1, "Current jiffies is 724ef\n", 25Current jiffies is 724ef
) = 25
exit_group(0) = ?
$
$ ltrace -S ./call_test1
...
syscall(337, 0xb78b0ff4, 0x8048460, 0xbf825588, 0xb77a345 <unfinished...>
SYS_337(0xb78b0ff4, 0x8048460, 0xbf825588, 0xb77a345, 0xb78d3d20) = 468207
<...syscall resumed> )
...
Здесь можно видеть вызов функции syscall и передачу ей в качестве аргумента номер
системного вызова (337) и дальнейший вызов нашей функции sys_getjiffies (SYS_337).
Источники
• Статья Тима Джонса — Kernel command using Linux system calls;
• rflinux.ru — Системные вызовы в Linux;
• Manugarg — Sysenter Based System Call Mechanism in Linux 2.6.