2016-12-06 18 views
2

我想創建另一個版本的克隆(2)系統調用(在內核空間)創建一個用戶進程的克隆與一些額外的參數。這個系統調用將完成與克隆(2)完全相同的工作,但我想通過一個額外的參數從user_space.However內核的時候我看到的glibc的code 似乎每個參數都不會以相同的順序傳遞的克隆()如何在linux內核中實現clone(2)系統調用的另一種變體?

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

的用戶的呼叫,而一些他們是由glibc的代碼本身處理。我搜索了互聯網,瞭解glib的clone()如何工作,但找不到更好的文檔。 任何人都可以請解釋

  1. glibc如何處理克隆()?
  2. 而且內核中syscall的所有參數與glibc中的clone不完全相同,那麼這些變化如何處理?

回答

3

的glibc如何處理的clone()?

通過拱形特定的裝配包裝紙。對於i386,請參閱glibc源代碼中的sysdeps/unix/sysv/linux/i386/clone.S;對於x86-64,請參閱sysdeps/unix/sysv/linux/x86-64/clone.S,依此類推。

正常的系統調用包裝是不夠的,因爲它取決於用戶空間切換堆棧。除了系統調用之外,上面的程序集文件對用戶空間中實際需要完成的內容提供了非常豐富的註釋。


所有內核系統調用的參數不完全一樣,在glibc的克隆,因此如何在這些變化如何處理?

映射到系統調用的C庫函數是包裝函數。

考慮,例如,在POSIX.1 write() C庫低級別的I/O功能,以及Linux write()系統調用。參數與錯誤條件基本相同,但錯誤返回值不同。如果發生錯誤,C庫函數將返回-1errno,而Linux系統調用返回負值錯誤代碼(基本上匹配errno值)。

如果你看看例如sysdeps/unix/sysv/linux/x86_64/sysdep.h,你可以看到,基本的系統調用接口適用於Linux on 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是定義爲新的系統調用系統調用號預處理宏。由於系統調用號碼可能不包括在官方內核源代碼和頭文件中,上面的頭文件明確地爲每個支持的體系結構聲明瞭它。對於不支持的體系結構,splork()函數將始終返回-1errno == ENOTSUP

但是請注意,Linux系統調用僅限於6個參數。如果您的內核函數需要更多,則需要將參數打包到結構中,將該結構的地址傳遞給內核,並使用copy_from_user()將值複製到內核中相同的結構中。

在所有Linux架構中,指針和long具有相同的尺寸的(int可以比指針更小),所以我建議您使用long或固定大小的類型的這種結構將數據從內核傳遞到/。

相關問題