【问题标题】:Is there a system call function whose definition calls another system call function?是否存在定义调用另一个系统调用函数的系统调用函数?
【发布时间】:2018-04-26 13:06:45
【问题描述】:

在 Linux 中,是否存在定义调用另一个系统调用函数的系统调用函数?

或者在任何系统调用函数都不会调用另一个系统调用函数的意义上,所有系统调用函数是互斥的?

或者在上述意义上,系统调用函数大部分是互斥的?

谢谢。

【问题讨论】:

  • 系统调用不能调用其他系统调用,因为当您已经在内核中时,进行系统调用的所有工作是没有意义的。有一些系统调用可以做另一个系统调用的超集,例如 dup3 与 dup2 一样,再加上一些,并且在内部它们可能主要使用相同的代码。不过,我不确定我是否理解您的实际要求。

标签: c linux system-calls


【解决方案1】:

好吧,对于每个系统调用,只会生成一个陷阱,但有些调用共享代码,例如,sys_epoll_create(),一旦进入内核模式,就会调用sys_epoll_create1()(请参阅eventpoll.c for kernell 2.6.35):

SYSCALL_DEFINE1(epoll_create, int, size)
{
    if (size <= 0)
        return -EINVAL;

    return sys_epoll_create1(0);
}

在最近的 kernelk (4.17-rc2) 中,同样的功能 share code:

SYSCALL_DEFINE1(epoll_create1, int, flags)
{
    return do_epoll_create(flags);
}

SYSCALL_DEFINE1(epoll_create, int, size)
{
    if (size <= 0)
        return -EINVAL;

    return do_epoll_create(0);
}

所以你的问题的答案是肯定的,但这取决于内核版本...

【讨论】:

    【解决方案2】:

    作为一个例子,我们来看看 C 函数 write() 在 Linux 中是如何实现的。

    C 库实现本质上是:

    ssize_t write(int fd, const void *buf, size_t count)
    {
        long retval;
    
        retval = syscall(__NR_write, fd, buf, count);
        if (retval < 0) {
            errno = -retval;
            return -1;
        } else
            return retval;
    }
    

    syscall() 函数特定于每个硬件架构、操作系统和内核。它通常用汇编程序编写。它将参数加载到特定的寄存器(根据使用的内核调用约定),并调用到内核中。

    此时,安全边界被跨越,执行移至内核空间。

    内核中的write() syscall 实现本身使用调用进程的文件描述表来查找特定于该文件或套接字的一组文件操作;确切的代码路径取决于文件描述符是文件(在这种情况下,是它所在的文件系统)、管道、套接字、字符设备还是块设备。

    几乎所有系统调用都返回一个非负值表示成功,一个负值表示错误。

    在 Linux 内核中,很少有系统调用实现调用另一个实现系统调用的函数。相反,公共部分被分解为两个系统调用实现函数调用的内核内部函数。这使得维护更容易。

    如果我们查看 Linux 内核如何实现 faccessat()access() 系统调用,我们会发现它们都只是调用内核内部函数 do_faccessat()

    【讨论】:

      【解决方案3】:

      这取决于你对系统调用的定义。

      我不知道有任何系统调用陷阱,一旦它们进入内核,就会故意造成另一个系统调用陷阱。

      但是一些系统调用的实现——内核中的sys_xxx函数——可以使用其他一些系统调用的实现来结束。

      这方面的一个例子是creat,它除了调用sys_open 函数——open 系统调用的实现——使用一些特定的参数之外什么都不做:

      /*
       * For backward compatibility?  Maybe this should be moved
       * into arch/i386 instead?
       */
      SYSCALL_DEFINE2(creat, const char __user *, pathname, umode_t, mode)
      {
          return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
      }
      

      【讨论】:

        猜你喜欢
        • 2011-02-09
        • 2012-09-27
        • 1970-01-01
        • 1970-01-01
        • 2015-07-18
        • 2011-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多