0
0
Lập trình
Sơn Tùng Lê
Sơn Tùng Lê103931498422911686980

Viết thành phần C với WASI SDK - Mô hình Thành phần WebAssembly

Đăng vào 1 tuần trước

• 6 phút đọc

Giới thiệu về WASI-SDK

WASI-SDK là một công cụ hỗ trợ phát triển cho phép lập trình viên viết các thành phần WebAssembly bằng ngôn ngữ C. Khác với Rust với cargo-component, C/C++ không có công cụ tích hợp để xây dựng các thành phần WebAssembly. WASI SDK cung cấp các công cụ cần thiết để biên dịch mã C thành WebAssembly.

WASI-SDK bao gồm những gì?

WASI SDK bao gồm:

  • Trình biên dịch clang được cấu hình với một sysroot WASI (bao gồm tập hợp các tiêu đề và thư viện của nền tảng mục tiêu) cho mục tiêu wasm32-wasi.
  • Thư viện chuẩn C hỗ trợ WASI (libc) triển khai các giao diện WASI.
  • Hỗ trợ đa nền tảng cho các hệ điều hành và kiến trúc khác nhau.
  • Tương thích với Preview 2 để xây dựng các thành phần WebAssembly hiện đại.

Điều này cho phép bạn viết các plugin C có thể truy cập vào hệ thống tệp, mạng và các tài nguyên hệ thống khác thông qua các giao diện WASI, tương tự như các plugin Rust.

Cài đặt WASI-SDK

Dự án sử dụng một tập lệnh tùy chỉnh just dl-wasi-sdk hoạt động như một trình quản lý gói, tự động tải xuống và giải nén phiên bản chính xác của WASI SDK cho hệ điều hành/kiến trúc của bạn vào thư mục c_deps/ (hoạt động như node_modules cho các phụ thuộc C).

Cách viết một plugin C

Quy trình xây dựng

Quy trình xây dựng plugin C diễn ra qua hai bước:

  1. Tạo bindings: wit-bindgen c ./crates/pluginlab/wit --world plugin-api --out-dir ./c_modules/plugin-name tạo ra các bindings C từ giao diện WIT của bạn.
  2. Biên dịch và chuyển đổi: Sử dụng clang của WASI SDK để biên dịch mã C thành một mô-đun WebAssembly (P1), sau đó chuyển đổi nó thành một thành phần P2.

Quy trình xây dựng:

  • Biên dịch component.c, plugin_api.c, và plugin_api_component_type.o thành một mô-đun WebAssembly với -mexec-model=reactor:

    Copy
    ./c_deps/wasi-sdk/bin/clang component.c plugin_api.c plugin_api_component_type.o \
        -o plugin-name-c.module.p1.wasm -mexec-model=reactor
  • Chuyển đổi mô-đun P1 thành thành phần P2 bằng wasm-tools component new:

    Copy
    wasm-tools component new plugin-name-c.module.p1.wasm -o plugin-name-c.wasm

Cấu trúc tệp

Cấu trúc của các plugin C trong kho lưu trữ như sau:

Copy
c_deps/                           # Cài đặt WASI SDK
c_modules/
  plugin-echo/                    # Thư mục plugin
    component.c                   # Triển khai plugin của bạn
    plugin_api.c                  # Các bindings được tạo ra (từ wit-bindgen)
    plugin_api.h                  # Tệp tiêu đề được tạo ra (từ wit-bindgen)
    plugin_api_component_type.o   # Tệp đối tượng được tạo ra (từ wit-bindgen)
    plugin-echo-c.module.p1.wasm  # Mô-đun WebAssembly đã biên dịch (P1)
    plugin-echo-c.wasm            # Thành phần WebAssembly cuối cùng (P2)

Triển khai Plugin

Plugin C triển khai cùng một giao diện như phiên bản Rust, với các chữ ký hàm được tạo ra từ giao diện WIT bởi wit-bindgen:

  • exports_repl_api_plugin_name() tương ứng với fn name() -> String
  • exports_repl_api_plugin_man() tương ứng với fn man() -> String
  • exports_repl_api_plugin_run() tương ứng với fn run(payload: String) -> Result<PluginResponse, ()>

Dưới đây là các chi tiết triển khai chính - plugin-echo/component.c:

c Copy
#include "plugin_api.h"
#include <string.h>
#include <stdlib.h>

void exports_repl_api_plugin_name(plugin_api_string_t *ret)
{
    // Điền ret với "echoc" là tên plugin
    // plugin_api_string_dup() cấp phát bộ nhớ mới và sao chép chuỗi
    plugin_api_string_dup(ret, "echoc");
}

void exports_repl_api_plugin_man(plugin_api_string_t *ret)
{
    // Điền ret với văn bản hướng dẫn cho lệnh echo
    // plugin_api_string_dup() cấp phát bộ nhớ mới và sao chép chuỗi
    const char *man_text = "một số văn bản hướng dẫn ...\n";
    plugin_api_string_dup(ret, man_text);
}

bool exports_repl_api_plugin_run(plugin_api_string_t *payload, exports_repl_api_plugin_plugin_response_t *ret)
{
    // Đặt trạng thái thành công (0 = thành công, 1 = lỗi)
    ret->status = REPL_API_TRANSPORT_REPL_STATUS_SUCCESS;

    // Đặt stdout để chứa payload
    // is_some = true có nghĩa là chuỗi tùy chọn có giá trị
    ret->stdout.is_some = true;

    // Tạo một chuỗi được kết thúc bằng null từ payload
    // Payload có ptr và len, chúng tôi cần đảm bảo rằng nó được kết thúc bằng null
    char *temp_str = malloc(payload->len + 1);
    if (temp_str == NULL)
    {
        // Xử lý lỗi cấp phát
        ret->stdout.is_some = false;
        ret->stderr.is_some = false;
        return false;
    }

    // Sao chép dữ liệu payload và kết thúc bằng null
    memcpy(temp_str, payload->ptr, payload->len);
    temp_str[payload->len] = '\0';

    // Sử dụng plugin_api_string_dup để tạo chuỗi đầu ra
    plugin_api_string_dup(&ret->stdout.val, temp_str);

    // Giải phóng chuỗi tạm thời
    free(temp_str);

    // Đặt stderr thành none (không có đầu ra lỗi)
    ret->stderr.is_some = false;

    // Trả về true để thành công (false sẽ chỉ ra lỗi)
    // Điều này tương ứng với Ok(response) trong mô hình Rust Result<T, ()>
    return true;
}

Lưu ý về quản lý bộ nhớ

  • Các tham số đầu vào (như payload) do runtime sở hữu - chúng KHÔNG ĐƯỢC giải phóng bởi plugin.
  • Các tham số đầu ra (như ret) được điền bởi plugin, được giải phóng bởi runtime.
  • plugin_api_string_dup() cấp phát bộ nhớ mới cho các bản sao chuỗi.
  • Các hàm _free được tạo ra tự động xử lý việc dọn dẹp.

Sự khác biệt chính so với Rust

  • Quản lý bộ nhớ thủ công cho các chuỗi tạm thời.
  • Xử lý rõ ràng độ dài chuỗi so với kết thúc null.
  • Giá trị trả về boolean thay vì mô hình Result<T, ()> của Rust.
  • Quản lý trực tiếp các cấu trúc C được tạo ra.

Thực hành tốt nhất và mẹo hiệu suất

  • Sử dụng các công cụ tự động hóa: Sử dụng các công cụ như just để tự động hóa quy trình xây dựng giúp tăng tốc độ phát triển.
  • Quản lý Bộ nhớ Cẩn thận: Hãy thận trọng với việc cấp phát và giải phóng bộ nhớ để tránh rò rỉ bộ nhớ.
  • Kiểm tra và Gỡ lỗi: Sử dụng các công cụ gỡ lỗi để theo dõi và kiểm tra mã của bạn trong môi trường WebAssembly.

Các cạm bẫy thường gặp

  • Cố gắng giải phóng bộ nhớ không thuộc sở hữu: Tránh giải phóng bộ nhớ cho các tham số do runtime cung cấp.
  • Không xử lý lỗi đầy đủ: Đảm bảo rằng bạn xử lý tất cả các trường hợp ngoại lệ và lỗi có thể xảy ra trong mã của bạn.

Kết luận

Việc phát triển các thành phần C với WASI SDK mở ra nhiều cơ hội cho lập trình viên. Bằng cách tuân theo các hướng dẫn và thực hành tốt nhất, bạn có thể tạo ra các plugin mạnh mẽ mà có thể hoạt động trên nhiều nền tảng. Hãy bắt đầu khám phá WASI SDK và phát triển các ứng dụng WebAssembly của riêng bạn ngay hôm nay!

Câu hỏi thường gặp (FAQ)

1. WASI SDK có hỗ trợ những hệ điều hành nào?
WASI SDK hỗ trợ nhiều hệ điều hành khác nhau, bao gồm cả Linux và macOS.

2. Tôi có thể sử dụng WASI SDK với ngôn ngữ khác không?
WASI SDK chủ yếu được thiết kế cho C/C++, nhưng cũng có thể sử dụng với các ngôn ngữ khác thông qua các bindings tương ứng.

3. Làm thế nào để gỡ lỗi mã WebAssembly?
Bạn có thể sử dụng các công cụ gỡ lỗi như Chrome DevTools để gỡ lỗi mã WebAssembly.

Hãy bắt đầu hành trình phát triển WebAssembly của bạn với WASI SDK ngay hôm nay!

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào