的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庫函數將返回-1
和errno
,而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()
函數將始終返回-1
與errno == ENOTSUP
。
但是請注意,Linux系統調用僅限於6個參數。如果您的內核函數需要更多,則需要將參數打包到結構中,將該結構的地址傳遞給內核,並使用copy_from_user()
將值複製到內核中相同的結構中。
在所有Linux架構中,指針和long
具有相同的尺寸的(int
可以比指針更小),所以我建議您使用long
或固定大小的類型的這種結構將數據從內核傳遞到/。