Skip to main content

Command Palette

Search for a command to run...

01 - Rust là gì và tại sao dùng nó cho hệ thống nhúng?

Tại sao nên tìm hiểu Rust và cách nó cải thiện hệ thống nhúng

Updated
5 min read
01 - Rust là gì và tại sao dùng nó cho hệ thống nhúng?

Thực trạng của các hệ thống nhúng hiện tại

Đối với việc phát triển firmware cho các hệ thống nhúng, một trong những “tiêu chuẩn” cho toàn ngành là sử dụng C/C++

C/C++ rất mạnh trong việc can thiệp sâu vào hệ thống: làm việc với từng bit trong thanh ghi, quản lý cấp phát bộ nhớ, các thư viện tiêu chuẩn của vendor… Không thể phủ nhận rằng C/C++ đã là nền tảng của cả ngành công nghiệp này. Tuy nhiên, làm việc với nó giống như làm bếp khi có trong tay 1 con dao sắc vậy, nếu cẩn thận thì mọi chuyện sẽ tốt đẹp, hoặc nếu không thì hậu quả sẽ khó lường.

Rust đang nổi lên như một ngôn ngữ được gọi là “C++ -killer”, dù rằng mình không tin là vậy cho lắm 😅
Nhưng mình cho rằng Rust có những đặc điểm mà một ngôn ngữ dùng để phát triển hệ thống nhúng nên có. Và series này là để làm rõ điều đó 😆

Những khó khăn khi sử dụng C/C++ trong phát triển hệ thống nhúng và so sánh với Rust

  1. Memory Safety
    C/C++ là ngôn ngữ non-memory safe, nghĩa là sẽ có hàng loạt lỗi liên quan đến bộ nhớ như: Buffer overflow, Null pointer dereference, Dangling pointer,… Hệ thống nhúng có thể bị hard fault nếu không được kiểm tra cẩn thận.
    Rust lại là ngôn ngôn ngữ memory safe, điều đó có nghĩa là phần lớn thời gian thì bạn chẳng phải lo lắng gì về những điều trên. Tất nhiên vẫn sẽ có một vài ngoại lệ, nhưng chúng ta sẽ nhắc tới nó sau.

  2. Memory Management

    Việc quản lý bộ nhớ khi sử dụng C/C++ là thủ công, và phụ thuộc vào người phát triển. Đối với C++, có một nguyên tắc mà được sử dụng để quản lý bộ nhớ, đó là RAII (Resource Acquisition Is Initialization). Chương trình có được quản lý bộ nhớ tốt hay không, phần lớn dựa vào kinh nghiệm và cẩn trọng của người phát triển.
    Quản lý bộ nhớ với Rust thì lại sử dụng cơ chế Ownership & Borrowing. Đây là điểm khác biệt lớn giữa C/C++ và Rust. Cơ chế này cho phép người phát triển quản lý bộ nhớ an toàn và hiệu quả hơn, nhưng đồng nghĩa với việc sẽ phải “đánh vật” với compiler.

  3. Race Condition
    Một ví dụ điển hình là việc main() đang đọc dữ liệu cảm biến nhưng ngắt lại được gọi, khiến dữ liệu đọc được bị sai sót do thay đổi giữa chừng. Trong C/C++, người phát triển thường phải quyết vấn đề này với việc kết hợp volatile và tắt ngắt.

     int main() {
         while (1) {
             __disable_irq(); // Tắt ngắt toàn cục
             if (counter > 100) {
                 counter = 0;
             }
             __enable_irq();  // Bật ngắt lại
    
             // Vấn đề:
             // 1. Quên enable lại -> Treo hệ thống
             // 2. Disable quá lâu -> Miss các ngắt quan trọng khác
         }
     }
    

    Với Rust, câu chuyện này sẽ khác biệt một chút, compiler yêu cầu bạn sử dụng Atomic hoặc các cơ chế Mutex, Semaphore,… Điều này có thể gây khó khăn ban đầu cho người phát triển, tuy nhiên đảm bảo rằng một khi compile thành công thì sẽ gần như không có race condition.

     use core::sync::atomic::{AtomicBool, Ordering};
     // Biến static global, có thể truy cập từ mọi nơi an toàn
     static G_FLAG: AtomicBool = AtomicBool::new(false);
    
     #[entry]
     fn main() -> ! {
         loop {
             if G_FLAG.load(Ordering::Relaxed) {
                 G_FLAG.store(false, Ordering::Relaxed);
                 // Xử lý sự kiện...
             }
         }
     }
    
     #[interrupt]
     fn EXTI0() {
         // Ngắt ghi
         G_FLAG.store(true, Ordering::Relaxed);
     }
    

Rust có đáng để tìm hiểu nếu bạn đang phát triển hệ thống nhúng?

Mình nghĩ là có.

  1. Nếu bạn đang gặp các vấn đề trên và cảm thấy hứng thú với việc triển khai Rust

    Rust hiện tại cũng đã đang dần được các tập đoàn lớn chấp nhận, đơn cử như việc Espressif release esp-hal 1.0.0 vào tháng 10 vừa qua. Rust đem lại sự đảm bảo về chất lượng cho firmware của hệ thống, đồng thời giúp mã nguồn có khả năng mở rộng tốt hơn và logic hơn. Các chương trình được viết bởi Rust đem lại hiệu năng ngang ngửa C. Tất nhiên nếu bạn có thể viết mã C ở mức hoàn hảo thì quá tuyệt, nhưng thường thì việc viết Rust để đem lại chất lượng cao thì dễ dàng hơn. Khi làm việc cùng Rust có 1 cái compiler khó tính, nó yêu cầu bạn phải đảm bảo mọi thứ an toàn, và một chương trình an toàn là một chương trình tốt 👍

  2. Chưa đủ hứng thú để triển khai, nhưng đủ để muốn tìm hiểu thêm về Rust
    Nếu bạn đang chỉ tìm hiểu về Rust và không/chưa có ý định sử dụng nó trong hệ thống nhúng của bạn, không sao cả 😉 Đa số các hệ thống nhúng vẫn đang dùng C, và thường thì chẳng có vấn đề gì. Tất nhiên, bạn không muốn hệ thống do mình phát triển là ngoại lệ trong bức tranh đẹp đó. Và khi bạn tìm hiểu về Rust, bạn sẽ hiểu sâu hơn về cách Rust đang xử lý những vấn đề bạn thường gặp phải. Thứ bạn học được có thể không phải là cách triển khai Rust với hệ thống của bạn, mà là cách Rust ngăn việc những vấn đề sẽ xảy ra.

Bài viết sau: 02 - Thiết lập môi trường phát triển dự án nhúng sử dụng Rust