gpt4 book ai didi

c - 如何在 linux 内核中实现 clone(2) 系统调用的另一种变体?

转载 作者:太空狗 更新时间:2023-10-29 11:03:33 32 4
gpt4 key购买 nike

我正在尝试创建另一个版本的 clone(2) 系统调用(在内核空间中)以使用一些附加参数创建用户进程的克隆。此系统调用将执行与 clone(2) 完全相同的工作但我想从 user_space 向内核传递一个额外的参数。但是当我看到 glibc 的 code 时似乎每个参数的传递顺序都与用户调用 clone() 的顺序不同

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 如何处理 clone()?
  2. 而且内核中的系统调用的所有参数与glibc中的克隆不完全相同,那么如何处理这些变化?

最佳答案

How glibc handles the clone()?

通过 arch-specific assembly wrappers。对于 i386,请参阅 sysdeps/unix/sysv/linux/i386/clone.S在 glibc 源代码中;对于 x86-64,请参阅 sysdeps/unix/sysv/linux/x86-64/clone.S , 等等。

普通的系统调用包装器是不够的,因为切换堆栈取决于用户空间。上面的汇编文件有非常有用的注释,说明除了系统调用之外,用户空间中实际需要做什么。


All the parameters of syscall in kernel are not exactly the same as clone in glibc, so how is these variation handled?

映射到系统调用的 C 库函数是包装函数。

例如,考虑 POSIX.1 write() C 库低级 I/O 函数和 Linux write() 系统调用。参数和错误条件基本相同,但错误返回值不同。如果发生错误,C 库函数返回 -1 并设置 errno,而 Linux 系统调用返回负错误代码(基本上匹配 errno 值).

如果您查看例如sysdeps/unix/sysv/linux/x86_64/sysdep.h ,您可以看到 x86-64 上的 Linux 的基本系统调用包装器归结为

# 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 是定义新系统调用的系统调用编号的预处理器宏。由于系统调用编号可能(还没有?)包含在官方内核源代码和 header 中,因此上述 header 文件为每个受支持的体系结构明确声明了它。对于不支持的体系结构,splork() 函数将始终返回 -1errno == ENOTSUP

但是请注意,Linux 系统调用仅限于 6 个参数。如果您的内核函数需要更多,则需要将参数打包到一个结构中,将该结构的地址传递给内核,然后使用 copy_from_user() 将值复制到内核中的相同结构.

在所有 Linux 架构中,指针和 long 的大小相同(int 可能比指针小),所以我建议您使用 long 或此类结构中的固定大小类型,用于将数据传入/传出内核。

关于c - 如何在 linux 内核中实现 clone(2) 系统调用的另一种变体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40987930/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com