2016-12-06 2 views
2

Я пытаюсь создать еще одну версию клона (2) syscall (в пространстве ядра), чтобы создать клон пользовательского процесса с некоторыми дополнительными параметрами. Этот системный вызов будет выполнять ту же работу, что и clone (2), но Я хочу передать один дополнительные параметры ядра из user_space.However когда я вижу Glibc-х code , кажется, что каждый параметр не передаются в том же порядке, как вызов пользователя клона()Как реализовать другой вариант клона (2) syscall в ядре linux?

int clone(int (*fn)(void *), void *child_stack, 
      int flags, void *arg, ... 
      /* pid_t *ptid, void *newtls, pid_t *ctid */); 

, а некоторые из них обрабатываются самим кодом glibc. Я искал в Интернете, чтобы узнать, как работает glib's clone(), но не может найти никакой лучшей документации. Может кто-нибудь объяснить, пожалуйста,

  1. Как glibc обрабатывает клон()?
  2. А также все параметры syscall в ядре не совсем такие же, как clone в glibc, так как эти вариации обрабатываются?

ответ

3

Как glibc обрабатывает клон()?

Через арку-специфические монтажные обертки. Для i386 см. sysdeps/unix/sysv/linux/i386/clone.S в источниках glibc; для x86-64, см. sysdeps/unix/sysv/linux/x86-64/clone.S и т. д.

Обычные обертки для системных вызовов недостаточны, так как в пользовательском пространстве можно переключать стеки. В приведенных выше сборных файлах есть довольно информативные комментарии относительно того, что на самом деле нужно делать в пользовательском пространстве в дополнение к syscall.


Все параметры системного вызова в ядре не точно так же, как клон в glibc, так как обрабатываются эти вариации?

Функции библиотеки C, которые отображаются в системном режиме, являются функциями оболочки.

Рассмотрим, например, POSIX.1 write() C библиотека низкоуровневой функции ввода-вывода и системный стол Linux write(). Параметры в основном такие же, как и условия ошибки, но значения возврата ошибки различаются. Функция библиотеки C возвращает -1 с errno при возникновении ошибки, тогда как в syscall Linux возвращаются отрицательные коды ошибок (которые в основном соответствуют значениям errno).

Если вы посмотрите на, например, sysdeps/unix/sysv/linux/x86_64/sysdep.h, вы можете увидеть, что основной системный вызов оболочка для Linux на x86-64 сводится к

# define INLINE_SYSCALL(name, nr, args...) \ 
    ({          \ 
    unsigned long int resultvar = INTERNAL_SYSCALL (name, , nr, args);  \ 
    if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (resultvar,)))   \ 
     {                  \ 
     __set_errno (INTERNAL_SYSCALL_ERRNO (resultvar,));     \ 
     resultvar = (unsigned long int) -1;         \ 
     }                  \ 
    (long int) resultvar; }) 

который только называет фактический системный вызов, а затем проверяет, является ли возвращение системного вызова значение, указанное сообщение об ошибке; и если это так, то изменяется результат на -1 и устанавливает errno соответственно. Это просто забавно, потому что он полагается на расширения GCC, чтобы заставить его вести себя как одно утверждение.


Допустим, вы добавили новый системный вызов в Linux, скажем

SYSCALL_DEFINE2(splork, unsigned long, arg1, void *, arg2); 

и, по каким-либо причинам, вы хотите, чтобы выставить его в пользовательское приложение как

int splork(void *arg2, unsigned long arg1); 

Нет проблем! Все, что вам нужно, чтобы обеспечить минимальный файл заголовка,

#ifndef _SPLORK_H 
#define _SPLORK_H 
#define _GNU_SOURCE 
#include <sys/syscall.h> 
#include <errno.h> 

#ifndef __NR_splork 
#if defined(__x86_64__) 
#define __NR_splork /* syscall number on x86-64 */ 
#else 
#if defined(__i386) 
#define __NR_splork /* syscall number on i386 */ 
#endif 
#endif 

#ifdef __NR_splork 
#ifndef SYS_splork 
#define SYS_splork __NR_splork 
#endif 

int splork(void *arg2, unsigned long arg1) 
{ 
    long retval; 

    retval = syscall(__NR_splork, (long)arg1, (void *)arg2); 
    if (retval < 0) { 
     /* Note: For backward compatibility, we might wish to use 
        *(__errno_location()) = -retval; 
       here. */ 
     errno = -retval; 
     return -1; 
    } else 
     return (int)retval; 
} 

#else 
#undef SYS_splork 

int splork(void *arg2, unsigned long arg1) 
{ 
    /* Note: For backward compatibility, we might wish to use 
       *(__errno_location()) = ENOTSUP; 
      here. */ 
    errno = ENOTSUP; 
    return -1; 
} 

#endif 

#endif /* _SPLORK_H */ 

SYS_splork и __NR_splork препроцессора макросы, определяющие количество системных вызовов для нового системного вызова. Так как номер syscall, вероятно, не включен (но?) В официальные источники и заголовки ядра, указанный выше заголовочный файл явно объявляет его для каждой поддерживаемой архитектуры. Для архитектур, где он не поддерживается, функция splork() всегда будет возвращать -1 с errno == ENOTSUP.

Обратите внимание, что системные вызовы Linux ограничены 6 параметрами. Если вашей функции ядра требуется больше, вам нужно упаковать параметры в структуру, передать адрес этой структуры в ядро ​​и использовать copy_from_user() для копирования значений в одну и ту же структуру в ядре.

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

Смежные вопросы