Вызов системных вызовов из кода ядра - PullRequest
0 голосов
/ 14 марта 2019

Я пытаюсь создать механизм для чтения счетчиков производительности для процессов.Я хочу, чтобы этот механизм выполнялся из самого ядра (версия 4.19.2).

Я могу сделать это из пользовательского пространства системным вызовом sys_perf_event_open() следующим образом.

syscall (__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);

Я хотел бы вызвать этот вызов из пространства ядра.Отсюда я получил некоторую базовую идею Как использовать системный вызов Linux из модуля ядра Linux

Вот шаги, которые я предпринял для достижения этой цели:

  1. Чтобы убедиться, что виртуальный адрес ядра остается действительным, я использовал set_fs(), get_fs() и get_fd().

  2. Поскольку определено sys_perf_event_open()в / include / linux / syscalls.h Я включил это в код.

В конце концов код для вызова системного вызова выглядит примерно так:

mm_segment_t fs;
fs = get_fs();
set_fs(get_ds());
long ret =  sys_perf_event_open(&pe, pid, cpu, group_fd, flags);
set_fs(fs);

Даже после этих мер я получаю сообщение об ошибке "неявное объявление функции 'sys_perf_event_open'" .Почему это появляется, когда файл заголовка, определяющий его, уже включен?Связано ли это с тем, как следует вызывать системные вызовы из кода ядра?

Ответы [ 4 ]

3 голосов
/ 15 марта 2019

В целом (не относится к Linux) работу, выполняемую для системных вызовов, можно разделить на 3 категории:

  • переключение из пользовательского контекста в контекст ядра (и обратно при возвратедорожка).Это включает в себя такие вещи, как изменение уровня привилегий процессора, работа с gs, работа со стеками и снижение безопасности (например, для Meltdown).Эти вещи дороги, и если вы уже находитесь в ядре, они бесполезны и / или опасны.

  • , используя параметр "номер функции", чтобы найти подходящую функцию для вызова,и зову это.Обычно это включает в себя некоторые проверки работоспособности (функция существует?) И поиск в таблице, а также код для изменения входных и выходных параметров, которые необходимы, поскольку соглашения о вызовах, используемые для системных вызовов (в пространстве пользователя), не совпадают с соглашениями о вызовах, которыеобычные C-функции используют.Эти вещи дороги, и если вы уже находитесь в ядре, они бесполезны и / или опасны.

  • последняя нормальная функция C, которая в итоге вызывается.Это функция, которую вы могли бы (см. Примечание) вызывать напрямую, без использования какого-либо дорогостоящего, бесполезного и / или опасного барахла системного вызова.

Примечание.не в состоянии вызывать конечную нормальную функцию C напрямую, без использования (любой части) мусора системного вызова (например, если конечная нормальная функция C не подвергается воздействию другого кода ядра);тогда вы должны определить, почему .Например, может быть, он не отображается, потому что он изменяет состояние пространства пользователя, и вызов его из ядра повредит состояние пространства пользователя, поэтому он не будет выставлен / экспортирован в другой код ядра, так что никто случайно не нарушит все.В другом примере, возможно, нет причины, по которой он не подвергается воздействию другого кода ядра, и вы можете просто изменить его исходный код, чтобы он отображался / экспортировался.

1 голос
/ 03 июня 2019

Вызов системных вызовов изнутри ядра с использованием интерфейса sys_ * не рекомендуется по причинам, уже упомянутым другими. В частном случае x86_64 (который, я думаю, это ваша архитектура) и, начиная с версий ядра v4.17, теперь трудно не использовать такой интерфейс (за некоторыми исключениями). До этой версии можно было вызывать системные вызовы непосредственно, но теперь появляется ошибка, которую вы видите (вот почему в Интернете существует множество учебных пособий, использующих sys_ *). Предложенная альтернатива в документации Linux состоит в том, чтобы определить оболочку между системным вызовом и действительным кодом системного вызова, которая может вызываться в ядре, как любая другая функция:

int perf_event_open_wrapper(...) {
    // actual perf_event_open() code
}

SYSCALL_DEFINE5(perf_event_open, ...) {
    return perf_event_open_wrapper(...);
}

источник: https://www.kernel.org/doc/html/v4.19/process/adding-syscalls.html#do-not-call-system-calls-in-the-kernel

0 голосов
/ 03 июня 2019

вызов системных вызовов из кода ядра

(я в основном отвечаю на этот заголовок; резюмируя: запрещено даже думать об этом)

Я не понимаю вашу настоящую проблему (я чувствую, что вам нужно объяснить это больше в вашем вопросе, который неясен и лишен множества полезных мотивов и контекста). Но общий совет - следуя философии Unix - минимизировать размер и область уязвимости вашего ядра или кода модуля ядра, и депортировать, насколько это удобно, такой код в user-land, в частности с помощью systemd , как только ваш код ядра потребует некоторых системных вызовов. Ваш вопрос сам по себе является нарушением большинства культурных норм Unix и Linux.

Рассматривали ли вы использование эффективного взаимодействия ядра с землей пользователя , в частности netlink (7) с сокетом (7) . Возможно, вы также хочу какой-то конкретный драйвер нить ядра .

Моя интуиция могла бы заключаться в том, что (в некоторых пользовательских демонах, запускаемых с systemd в начале загрузки) AF_NETLINK с сокетом (2) точно подходит для вашего (необъяснимые) потребности. И eventd (2) также может иметь значение.

Но только размышление об использовании системных вызовов внутри ядра вызывает огромный вспыхивающий красный свет в моем мозгу , и я склонен полагать, что это является признаком серьезного неправильного понимания ядер операционной системы в целом. Пожалуйста, найдите время, чтобы прочитать Операционные системы: три простых части , чтобы понять философию ОС.

0 голосов
/ 15 марта 2019

О какой версии ядра мы говорим?

В любом случае, вы можете получить адрес sys_call_table, посмотрев на файл карты системы, или, если он экспортирован, вы можете найти символ (посмотрите на kallsyms.h), получив адрес таблицы syscall, вы можете обработать его как массив пустых указателей (void **) и найти нужные функции индексированными.то есть sys_call_table[__NR_open] будет адресом open, поэтому вы можете сохранить его в пустом указателе и затем вызвать его.

Редактировать: Что вы пытаетесь сделать, и почему вы не можете сделать это без вызова системных вызовов?Вы должны понимать, что системные вызовы являются API-интерфейсом ядра для пользовательской среды, и их не следует использовать изнутри ядра, поэтому следует избегать такой практики.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...