Fuchsia源码分析--系统调用流程

本文详细分析了Fuchsia系统调用的流程,从定义、编译到用户空间和内核空间的实现。重点探讨了zx_channel_create系统调用在FIDL文件channel.fidl中的定义,通过fidlc和kazoo工具生成的头文件,以及在用户空间的C++实现和内核空间的汇编实现。最后展示了如何在内核中响应并执行系统调用。
摘要由CSDN通过智能技术生成

最近一直在努力阅读Fuchsia的源码,但是说实话,Fuchsia目前以大量C11风格的代码,读起来还是挺费劲的,就好比当初刚开始读JAVA代码或者Python的代码的时候,希望经过坚持一段时间能够适应这个新的c++代码风格。
  一直在想以什么样的方式开始切入阅读整个的Fuchsia源码,尝试过比较多的突破点,最后还是选择下从系统调用入手,先把整个系统调用的流程整理清楚。所以暂时先现在zx_channel_create这样一个系统调用入手,从应用层一直追踪到kernel层,完成整个调用流程的梳理。这篇文章将从以下几个方面来梳理下Fuchsia中的系统调用。

  1. Fuchsia系统调用的定义
  2. Fuchsia系统调用定义文件的编译
  3. Fuchsia系统调用用户空间的调用流程
  4. Fuchsia系统调用Kernel空间的实现

Fuchsia系统调用的定义

我们能看到当前系统所有的系统调用的定义都放在fuchsia/zircon/vdso/目录中,如在这里插入图片描述
可以看到这个文件目录中有大量文件后缀名为.fidl的文件,我们要跟踪的zx_channel_create就是在channel.fidl中定义的,我们先打开这个文件看以下:

library zx;

using ObjType = uint32;

// TODO(scottmg): ZX_OBJ_TYPE_xyz here.

using HandleOp = uint32;

// TODO(scottmg): ZX_HANDLE_OP_xyz here.

struct HandleInfo {
    handle handle;
    ObjType type;
    rights rights;
    uint32 unused;
};

struct ChannelCallArgs {
    vector<byte> wr_bytes;
    vector<handle> wr_handles;
    // TODO(scottmg): mutable_vector_void
    vector<byte> rd_bytes;
    // TODO(scottmg): mutable_vector_handle
    vector<handle> rd_handles;
};

struct HandleDisposition {
    HandleOp operation;
    handle handle;
    ObjType type;
    rights rights;
    status result;
};

[Transport = "Syscall"]
protocol channel {
    /// Create a channel.
    channel_create(uint32 options) -> (status status, handle out0, handle out1);

    /// Read a message from a channel.
    /// Rights: handle must be of type ZX_OBJ_TYPE_CHANNEL and have ZX_RIGHT_READ.
    [ArgReorder = "handle, options, bytes, handles, num_bytes, num_handles, actual_bytes, actual_handles",
    HandleUnchecked]
    channel_read(handle<channel> handle,
                 uint32 options)
        -> (status status,
            vector_void_u32size bytes,
            vector_handle_u32size handles,
            optional_uint32 actual_bytes,
            optional_uint32 actual_handles);
........

我们可以看到这个fidl文件中有个比较重要的定义是[Transport = “Syscall”],在这个文件里我们有看到一个channel_create名字的定义。这个貌似跟我们前面说的zx_channel_create还有点区别,先别急,我们再找找。

Fuchsia系统调用定义文件的编译

我们从https://fuchsia.dev/fuchsia-src/reference/syscalls页面能够看到相关的介绍:
Syscall support is generated from //zircon/syscalls. The FIDL files in that directory are first run through fidlc which produces an intermediate format. That intermediate format is consumed by kazoo which produces output for both the kernel and userspace in a variety of languages. This output includes C or C++ headers for both the kernel and userspace, syscall entry points, other language bindings, and so on.

This tool is invoked as a part of the build, rather than checking in its output.

这段话的意思zircon/syscalls目录中的FIDL 文件首先会被fidlc生产一些中间格式,因为这些FIDL 文件中带有[Transport = “Syscall”],是被定义成系统调用的,所以会让kazoo工具会将这些中间格式生产kernel and userspace使用的c/c++头文件,在这里我们先不去计较哪些文件是kazoo工具生产的,也不区计较FIDL 文件是怎么被fidlc工具所使用。这些我们后面再来一一关注。

Fuchsia系统调用用户空间的调用流程

正常在userspace中,我们随便找一个文件,例如src/lib/fsl/io/device_watcher.cc,从这里我们开始追踪。

 zx::channel client, server;
  if (zx::channel::create(0, &client, &server) != ZX_OK) {
    return nullptr;
  }

我们看到是通过zx::channel::create来调用的,channel是一个继承自object的类,这个也符合fuchsia是一个以C++实现的kernel的特征,一切都是对象。
我们再找到channel类的实现zircon/system/ulib/zx/channel.cc:

#include <lib/zx/channel.h>

#include <zircon/syscalls.h>

namespace zx {

zx_status_t channel::create(uint32_t flags, channel* endpoint0, channel* endpoint1) {
  // Ensure aliasing of both out parameters to the same container
  // has a well-defined result, and does not leak.
  channel h0;
  channel h1;
  zx_status_t status =
      zx_channel_create(flags, h0.reset_and_get_address(), h1.reset_and_get_address());
  endpoint0->reset(h0.release());
  endpoint1->reset(h1.release());
  return status;
}

}  // namespace zx

在这里我们终于第一次看到zx_channel_create这个函数了,惊不惊喜?接下来我们就来看看几个方面的内容:
1)zx_channel_create这个函数是在哪里定义的。
2)zx_channel_create这个函数是在哪里实现的。
2)zx_channel_create这个函数与前面channel.fidl中所定义的channel_create有什么样的关系。

我们先来关注第一个方面:

zx_channel_create函数的定义

我们注意到zircon/system/ulib/zx/channel.cc有引用一个头文件:

#include <zircon/syscalls.h>

这个头文件的位置在于:./zircon/system/public/zircon/syscalls.h
这个头文件是这样写的:


#ifndef SYSROOT_ZIRCON_SYSCALLS_H_
#define SYSROOT_ZIRCON_SYSCALLS_H_

#include <zircon/string_view.h>
#include <zircon/syscalls/object.h>
#include <zircon/syscalls/pci.h>
#include <zircon/syscalls/profile.h>
#include <zircon/syscalls/types.h>
#include <zircon/types.h>

__BEGIN_CDECLS

#define _ZX_SYSCALL_DECL(name, type, attrs, nargs, arglist, prototype) \
  extern attrs type zx_##name prototype;                               \
  extern attrs type _zx_##name prototype;

#ifdef __clang__
#define _ZX_SYSCALL_ANNO(attr) __attribute__((attr))
#else
#define _ZX_SYSCALL_ANNO(attr)  // Nothing for compilers without the support.
#endif

#include <zircon/syscalls/internal/cdecls.inc>

#undef _ZX_SYSCALL_ANNO
#undef _ZX_SYSCALL_DECL

// Compatibility wrappers for deprecated syscalls also go here, when
// there are any.

// This DEPRECATED interface is replaced by zx_system_get_version_string.
zx_status_t zx_system_get_version(char* version, size_t version_size) __LEAF_FN;
zx_status_t _zx_system_get_version(char* version, size_t version_size) __LEAF_FN;

__END_CDECLS

#endif  // SYSROOT_ZIRCON_SYSCALLS_H_

这个里面定义了一个_ZX_SYSCALL_DECL的宏,也引用了一个inc头文件:

#include <zircon/syscalls/internal/cdecls.inc>

这个头文件位置就在于:./out/default.zircon/gen/include/zircon/syscalls/internal/cdecls.inc
一看就是一个编译过程中所生成的文件。
打开这个文件看看,我们有找到跟channel_create相关的内容:

_ZX_SYSCALL_DECL(channel_create, zx_status_t, /* no attributes */, 3,
    (options, out0, out1), (
    uint32_t options,
    _ZX_SYSCALL_ANNO(acquire_handle("Fuchsia")) zx_handle_t* out0,
    _ZX_SYSCALL_ANNO(acquire_handle("Fuchsia")) zx_handle_t* out1))

_ZX_SYSCALL_DECL这个宏定义在./zircon/system/public/zircon/syscalls.h中,也就在引用#include <zircon/syscalls/internal/cdecls.inc>的前面。所以在编译过程中cdecls.inc文件中的内容会被引用进syscalls.h中,而且通过宏展开,展开之后会形成一个zx_channel_create的函数定义。我们看下_ZX_SYSCALL_DECL的宏定义

#define _ZX_SYSCALL_DECL(name, type, attrs, nargs, arglist, prototype) \
  extern attrs type zx_##name prototype;                               \
  extern attrs type _zx_##name prototype;

对着cdecls.inc中的内容,我们看看是不是这里定义了zx_channel_create函数。

我们再来看看zx_channel_create函数是在哪里实现的:

zx_channel_create函数的实现

找这个函数的实现还真的费了一番手脚,主要是这个实现方式比较巧妙,让人拍案叫绝,真的可以仔细把玩,慢慢品味,没想到代码还是这么玩。

首先我们找到./zircon/system/ulib/zircon/syscalls-x86.S这个文件,因为是在x86的环境上跟踪fuchsia的源码,如果是在arm环境上下,可以去看这个目录下的arm版本的实现。

// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/* define and implement the zircon syscall wrappers for x86-64 */

#include "syscall-entry.h"
#include "zircon-syscall-x86.S"

.text

.cfi_sections .eh_frame, .debug_frame

// The following assembly code converts arguments from the x86-64 SysV
// ABI's function calling conventions to the conventions
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值