ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

如何在Linux内核中实现clone(2)syscall的另一种形式?

2019-10-26 12:50:49  阅读:319  来源: 互联网

标签:system-calls glibc c-3 linux fork


我正在尝试创建另一个版本的clone(2)syscall(在内核空间中)以创建带有一些其他参数的用户进程的克隆.此系统调用将执行与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()的工作原理,但找不到更好的文档.
谁能解释一下

> glibc如何处理clone()?
>而且内核中syscall的所有参数与glibc中的clone并不完全相同,那么如何处理这些变化?

解决方法:

How glibc handles the clone()?

通过特定于拱的装配包装.对于i386,请参阅glibc源代码中的sysdeps/unix/sysv/linux/i386/clone.S;有关详细信息,请参阅.对于x86-64,请参见sysdeps/unix/sysv/linux/x86-64/clone.S,依此类推.

普通的syscall包装器是不够的,因为要由用户空间来切换堆栈.上面的程序集文件对除syscall外在用户空间中实际需要执行的操作提供了非常有用的注释.

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

映射到syscall的C库函数是包装器函数.

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

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

# 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,

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()函数将始终使用errno == ENOTSUP返回-1.

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

在所有Linux体系结构中,指针和long的大小相同(int可能小于指针),因此我建议您在此类结构中使用long或固定大小的类型将数据传递到内核或从内核传递数据.

标签:system-calls,glibc,c-3,linux,fork
来源: https://codeday.me/bug/20191026/1936671.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有