2 điểm bởi GN⁺ 2023-11-17 | 1 bình luận | Chia sẻ qua WhatsApp
  • Hỗ trợ chiếm quyền thời gian thực của Linux là công việc đã chờ gần 20 năm để được đưa vào mainline, và Thomas Gleixner cho biết tại Linux Plumbers Conference 2023 rằng trở ngại lớn cuối cùng là printk()
  • Mục tiêu là để tiến trình có mức ưu tiên cao nhất có thể chạy với độ trễ ngắn có thể dự đoán, và để làm được điều này, nhiều phần cốt lõi của kernel đã được viết lại trong thời gian dài
  • printk() có thể được gọi trong bất kỳ ngữ cảnh nào nên phức tạp hơn nhiều so với việc xuất log đơn giản, và cơ chế xuất đồng bộ hiện tại xung đột với mục tiêu về độ trễ thời gian thực
  • Kể từ năm 2018, khoảng 300 bản vá đã được đưa upstream hoặc đang chờ trong linux-next; các việc còn lại là handover cho thông điệp khẩn cấp và xử lý an toàn cho trình điều khiển console
  • Khi việc dọn dẹp printk() hoàn tất và phần mã thời gian thực còn lại sẵn sàng trong linux-next, việc hợp nhất trong cùng một merge window cũng có thể xảy ra, nhưng Gleixner nói ông sẽ không dự đoán thời điểm hoàn tất nữa

Công việc chiếm quyền thời gian thực kéo dài gần 20 năm

  • Hỗ trợ thời gian thực của Linux đã xuất hiện lần đầu trên LWN vào năm 2004, và trong thời gian dài trông như thể “chỉ cần thêm một chút nữa là xong”
  • Năm 2009, LWN cũng đã dùng tiêu đề the realtime preemption endgame, nhưng tại Linux Plumbers Conference 2023, Gleixner cho rằng lần này cái kết thực sự đã rất gần
  • Với cá nhân Gleixner, đây gần như là công việc kéo dài 25 năm
    • Ông bắt đầu làm về hỗ trợ thời gian thực cho Linux vào năm 1999
    • Bản thân dự án cũng đã kéo dài gần 20 năm
  • Ông nói sẽ có “a big party” khi công việc kết thúc, nhưng printk() vẫn còn là trở ngại lớn cuối cùng

Độ trễ mà chiếm quyền thời gian thực muốn giảm

  • Mục tiêu của chiếm quyền thời gian thực là bảo đảm tiến trình có mức ưu tiên cao nhất luôn có thể chạy với độ trễ tối thiểu và có thể dự đoán
  • Để làm được điều đó, kernel phải có thể bị chiếm quyền trong càng nhiều tình huống càng tốt, còn các ngoại lệ phải được giới hạn trong phạm vi hẹp và rõ ràng
  • Cách vận hành cơ bản đã được định hình từ lâu, nhưng việc giải quyết các vấn đề chi tiết đã mất nhiều thời gian
  • Trong quá trình này, nhiều phần của kernel cốt lõi đã được viết lại, và lợi ích của nó mở rộng ra toàn bộ kernel, vượt ra ngoài các trường hợp sử dụng thời gian thực

Vì sao printk() là trở ngại cuối cùng

  • Khi mã kernel cần gửi thông điệp tới console và log, nó gọi printk() hoặc các hàm được xây dựng bên trên hàm này
  • Trông có vẻ như thao tác xuất đơn giản, nhưng printk() phải hoạt động trong gần như mọi ngữ cảnh
    • Có thể được gọi cả trong handler của non-maskable interrupt
    • Có thể được gọi lại từ bên trong một lời gọi printk() khác
    • Trong tình huống hệ thống crash, thông tin xuất ra có thể rất quan trọng nên khó có thể hạn chế theo ngữ cảnh gọi
  • Vì các yêu cầu này, printk() vướng vào những vấn đề phức tạp về đồng thời, khóa và xử lý driver
  • printk() của kernel hiện tại là một cấu trúc hoàn toàn đồng bộ
    • Lời gọi không trả về cho đến khi thông điệp được gửi tới tất cả các đích đã thiết lập
    • Gleixner mô tả cấu trúc này là “stupid”
    • Đặc biệt trong lúc boot, phần lớn đầu ra có thể chỉ là nhiễu, nhưng hệ thống vẫn phải chờ cho đến khi tất cả được gửi xong
  • Thời gian chờ này đối đầu trực tiếp với độ trễ mà công việc thời gian thực đang cố giảm
  • Từ lâu, các nhà phát triển thời gian thực đã chuyển đầu ra printk() sang một thread riêng để biến nó thành bất đồng bộ, nhưng mã đó giống nhiều bản hack hơn là một giải pháp căn cơ

Việc tái cấu trúc printk() từ năm 2018

  • Vấn đề printk() bắt đầu được xử lý nghiêm túc từ năm 2018, và khoảng 300 bản vá đã được đưa upstream hoặc đang chờ trong linux-next
  • Hiện có 3 bộ bản vá cuối cùng đang được tiến hành để hoàn tất công việc
  • Một trong những chi tiết khó nhất là cơ chế handover
    • Khi kernel cần xuất thông điệp khẩn cấp như khi crash, nó có thể phải giành quyền điều khiển console đang xuất một thông điệp có mức ưu tiên thấp
    • Thực hiện việc này an toàn trong bất kỳ ngữ cảnh nào là điều không dễ
  • Một nhiệm vụ khác là đánh dấu các trình điều khiển console không thể dùng an toàn trong một số ngữ cảnh nhất định
    • Ví dụ, nếu cần thiết lập chế độ video để xuất thông điệp trong lúc có non-maskable interrupt thì việc đó không thể hoạt động
  • Gleixner trả lời rằng trong năm gần đây không có thay đổi khái niệm căn bản nào
    • Kernel có 76 trình điều khiển console cần được sửa
    • Mã handover đã được thay đổi để có thể cập nhật từng driver một, thay vì phải sửa tất cả driver cùng lúc
    • Thảo luận bổ sung về công việc printk() gần đây có trong bài viết này

Xuất bất đồng bộ và điều kiện hợp nhất vào mainline

  • Khi Masami Hiramatsu hỏi thông điệp kernel nào cần được xuất đồng bộ, Gleixner trả lời rằng gần như mọi thứ nên được làm bất đồng bộ
  • Xuất bất đồng bộ giúp giảm độ trễ do lời gọi printk() gây ra, đồng thời cho phép mỗi console có một kernel thread riêng
    • Console nhanh có thể hoạt động theo tốc độ của nó mà không phải chờ console chậm nhất
  • Mã đã được thay đổi để các thông điệp quan trọng được sao chép hoàn toàn vào buffer thông điệp trước khi dòng đầu tiên được xuất ra
    • Đây là biện pháp phòng trường hợp một trình điều khiển console lỗi làm hỏng toàn bộ hệ thống
  • Để có thứ tự xuất an toàn hơn, hệ thống ghi trước vào console được biết là an toàn
    • Ví dụ, nếu có persistent-memory store, thông điệp sẽ được lưu trước khi gửi tới thiết bị vật lý
    • Đây là cách để bảo toàn nội dung đầu ra ngay cả khi driver lỗi làm hệ thống chết
  • Gleixner nói công việc đã gần xong, nhưng printk() rất khó dự đoán nên ông sẽ không nói thời điểm hoàn tất nữa
  • Dù vậy, ông bày tỏ hy vọng phần mã chiếm quyền thời gian thực còn lại sẽ vào mainline trước dịp kỷ niệm 20 năm vào cuối năm 2024
  • Khi Clark Williams hỏi liệu sau khi các bản vá printk() được upstream, phần mã thời gian thực còn lại có được đưa vào trong cùng merge window hay không, Gleixner trả lời có điều kiện là “yes”
    • Nếu toàn bộ mã đã được staged trong linux-next và trông sẵn sàng, có thể thử làm như vậy

1 bình luận

 
GN⁺ 2023-11-17
Các ý kiến trên Hacker News
  • QNX đã làm đúng phần này từ nhiều thập kỷ trước. Microkernel có giới hạn trên cho mọi việc nó làm, và mã cũng chỉ vài chục nghìn dòng
    Microkernel chỉ làm cấp phát bộ nhớ, điều phối CPU và truyền thông điệp giữa các tiến trình. Mọi thứ còn lại, gồm driver và logger, đều nằm trong không gian người dùng và có thể bị các luồng có mức ưu tiên cao hơn chiếm quyền
    Kernel QNX không xử lý chuỗi. Không phân tích cú pháp, không định dạng, không thông điệp. Linux đã trở nên quá cồng kềnh cho thời gian thực, và vì phải làm cho hàng triệu dòng mã kernel đều có thể bị chiếm quyền, nên bản thân cấu trúc của nó không phù hợp với thời gian thực. Vì vậy việc sửa mất 20 năm

    • Một ví dụ hiện đại là seL4. Theo tôi biết, nó không cấp phát bộ nhớ động và cũng đã được kiểm chứng hình thức cho nhiều thuộc tính
      Đóng góp lớn nhất cho thiết kế kernel có lẽ là cách sử dụng capability trên diện rộng để đưa quyền kiểm soát ra không gian người dùng một cách an toàn nhưng linh hoạt
    • Chẳng phải QNX được dùng trong hệ thống thông tin giải trí trên xe sao? Tôi tò mò còn dùng ở đâu nữa
      Bản thân việc kernel phình to không khiến tôi quá bận tâm. Linux được đầu tư rất nhiều thời gian phát triển, và dù desktop không có mức ưu tiên cao như server, công việc tạo ra kernel hiệu năng tốt cho các thiết bị di động cũng sẽ có lợi cho người dùng desktop
    • Kernel SDP 8 hiện có 15.331 dòng, tính cả chú thích và Makefile
    • Trông giống một hàm main được cấu trúc tốt trong C hoặc ngôn ngữ họ C. main chỉ điều phối các lời gọi hàm khác, và ở đây dù có khác biệt là kernel QNX làm ít khởi tạo hơn, khái niệm lớn thì vẫn tương tự
      Tôi không phải nhà phát triển kernel, nhưng cách giữ đơn giản như vậy có vẻ tốt
    • Khoảng 90% trong “hàng triệu dòng kernel” là driver thiết bị. Muốn chạy trên phần cứng tùy ý thì cuối cùng microkernel cũng vẫn cần chúng
  • Có một ví dụ cho thấy ngay cả trên hệ thống đang hấp hối, kernel vẫn cố gắng bằng cách nào đó đẩy thông điệp log ra ngoài, và việc đó được dùng thế nào trong môi trường triển khai thực tế
    https://netflixtechblog.com/kubernetes-and-kernel-panics-ed6...

  • Tôi tò mò liệu nếu vấn đề này được sửa, nó có thể thay thế được kha khá một số tổ hợp phần cứng/phần mềm được tạo ra cho thời gian thực hay không. Giờ có rất nhiều lựa chọn chip ARM và x86 rẻ, tiêu thụ điện thấp và xung nhịp cao
    Vì xung nhịp rất cao, ngay cả khi có trường hợp bị lỡ, thường vẫn có nhiều chu kỳ dư thừa nên tính thời gian thực hoàn hảo có thể kém quan trọng hơn. Tôi biết như vậy không thanh lịch cũng chẳng hiệu quả, nhưng đôi khi hàng phổ thông lại thắng độ chính xác

    • Những tác vụ cần hard real-time không thể thỏa mãn bằng “dù lỡ vẫn còn nhiều chu kỳ dư”. Đây cũng không chỉ là vấn đề chu kỳ CPU
      Một tác vụ viết tệ có thể giữ kernel lại và khiến nó không làm được việc hữu ích. Cốt lõi của hard real-time là “không gì có thể ngăn tác vụ quan trọng này chạy”. Trong ô tô hay hàng không vũ trụ, hệ thống điều khiển phải chạy trong mọi tình huống
    • Các ứng dụng có yêu cầu thời gian thực thật sự thường có yêu cầu nghiêm ngặt đến mức không thể chấp nhận dù chỉ một khả năng lỗi rất nhỏ. Hãy nghĩ đến điện tử hàng không, thiết bị y tế, ô tô, ứng dụng quân sự
      Nếu thật sự cần thời gian thực thì là thật sự cần, không có khái niệm “đủ gần”. Dù vậy đây chỉ là ấn tượng của một người ngoài cuộc
    • Khi làm ứng dụng thời gian thực bằng chip ARM tiêu thụ điện thấp, xung cao, người ta hoàn toàn không dùng hệ điều hành. Với mục đích đó cũng không cân nhắc x86
      Hệ điều hành, kể cả RTOS, can thiệp quá nhiều. Tôi không biết thay đổi này sẽ thay đổi điều gì. Tuy nhiên còn tùy ứng dụng, và có nhiều trường hợp chỉ cần mức “gần thời gian thực”, nên nó có thể hữu ích cho các mục đích đó
    • Đúng, nhưng điều này sẽ không thần kỳ loại bỏ nhu cầu về core chuyên dụng. Có lẽ sẽ là kiểu chỉ thị cho scheduler chỉ đặt tác vụ thời gian thực không thể bị chiếm quyền lên một core LITTLE
  • Cuộc thảo luận ở đây tập trung vào sự phân biệt giữa ứng dụng thời gian thực “hard” và “soft”. Với hard real-time, nhiều khả năng ngay từ đầu bạn đã không muốn dùng hệ điều hành đa dụng như Linux; còn với soft real-time như họp video hay phát âm thanh, thỉnh thoảng bị khựng hoặc rơi vài khung hình cũng không phải thảm họa
    Lập luận là RT Linux sẽ trở thành một giải pháp mạnh cho các mục đích soft real-time như vậy. Nhưng các trường hợp soft được nêu hiện nay cũng đã làm được bằng embedded Linux. Phần mềm video hoặc phát âm thanh độ trễ thấp đâu phải là không thể; 20 năm trước cũng đã làm được
    Vấn đề xuất hiện khi I/O không thể bị chiếm quyền thường xuyên chen vào trên một hệ thống bận, nhưng trong môi trường nhúng thì trường hợp đó hiếm. Có những lý do thuyết phục để làm cho kernel có thể bị chiếm quyền hoàn toàn và trao thêm quyền kiểm soát scheduling, nhưng điều đó không mấy liên quan đến lý do Linux phải thay thế hệ điều hành thời gian thực tối giản hay mã bare-metal
    Nó giống một biện pháp vệ sinh tốt hơn, và khiến hệ điều hành hoạt động tốt hơn khi chịu tải ngay cả với các ứng dụng không thời gian thực

  • Đây là tin tốt, nhưng ngay cả khi kernel Linux trở thành thời gian thực, phần cứng nhiều khả năng vẫn không phải thời gian thực vì cache và những “phép màu” phức tạp bên trong CPU
    Phần cứng lớn và phức tạp không phù hợp với thời gian thực đúng nghĩa. Vì vậy AbsInt và các công cụ thời gian thực thi tệ nhất (WCET) chủ yếu xử lý các kiến trúc CPU đơn giản. 8051 chắc sẽ thật sự sống mãi. Nhân tiện, còn có Zephyr RTOS nữa

    • Theo tôi biết, các tính năng của CPU hiện đại không ngăn cản việc dùng cho thời gian thực. Bất cứ thứ gì có giới hạn trên và có thể suy luận được đều có thể dùng để xây dựng hệ thống thời gian thực
      Chỉ cần giả định các tình huống như hoàn toàn không có cache hit và tải tối đa. Nếu có thể đặt giới hạn trên cho thời gian mất bao lâu thì ổn
    • Tôi nghĩ nó khá hữu ích trên các bo mạch cấp vi điều khiển “lớn” như Raspberry Pi. Ở đó có một kiểu văn hóa thời gian thực, và dù không trực tiếp bit banging bằng CPU, nhìn từ bên ngoài thì mọi thứ vẫn diễn ra đúng lúc
      Timer có thể nhận đầu vào từ encoder vuông góc và chỉ gửi interrupt khi bị wrap, hoặc có thể nối hệ thống GPIO với DMA để stream bộ nhớ ra các chân output mà không cần CPU can thiệp. Cũng có thể stream tới DAC hoặc DMA transfer từ ADC vào bộ nhớ. Những thứ như vậy thường bỏ qua cache để có độ trễ dự đoán được
    • SpaceX dùng bộ xử lý x86 trong tên lửa. Chiếc drone trực thăng nhỏ mà NASA gửi lên Sao Hỏa cũng dùng một nhân ARM “khá lớn”, đủ để chạy Android cũ
    • Nói phần cứng lớn và phức tạp không phù hợp với thời gian thực đúng nghĩa thì không hẳn chính xác. Có những nhân thời gian thực cao cấp như Arm Cortex-R82
      Trên thực tế, nhiều hệ thống thời gian thực phải xử lý và tổng hợp lượng dữ liệu cảm biến ngày càng tăng, nên chúng cũng ngày càng mạnh hơn
    • 68000 mới là vị vua thực sự của thời gian thực
  • Hồi mới đi làm, tôi đã trải qua rất nhiều cuộc phỏng vấn bực mình nơi người phỏng vấn không biết thời gian thực thực sự là gì. Nhiều người bỏ lỡ khái niệm “và độ trễ dự đoán được” trong bài viết, và dường như nghĩ thời gian thực chỉ có nghĩa là “nhanh”

    • Tôi còn muốn bỏ hẳn phần “tối thiểu”. Cốt lõi của thời gian thực là tác vụ có giới hạn trên dự đoán được. Điều này có nghĩa là trung bình nó có thể chậm hơn hệ thống không thời gian thực
      Nếu điều khiển hệ thống phanh ô tô, “độ trễ trung bình 50ms nhưng tối đa 80ms” có thể chấp nhận được, còn “độ trễ trung bình 1ms nhưng có thể dài tùy ý và có thể mất vài giây” thì không
    • Như câu nói cũ, “real time” không phải là “real fast”. Việc phân biệt giữa hard real-time và soft real-time khiến vấn đề hơi mờ đi, nhưng tôi nghĩ nhiều lập trình viên cũng không thật sự hiểu thời gian thực là gì
  • Logging đồng bộ lại gây vấn đề. Ở công ty chúng tôi đã gặp chuyện tương tự vì GLOG (thư viện logging của Google); ví dụ nếu stdout là file thì có thể bị chặn ở disk I/O
    Khi dịch vụ của chúng tôi bị khựng hơn 100ms, 90–99% nguyên nhân là GLOG

    • Tôi thường có cuộc trao đổi kiểu này với đồng nghiệp về logging. “Có API best-effort và API đảm bảo chuyển giao.” “Tôi muốn đảm bảo chuyển giao!” “Nếu giao diện logging đảm bảo chuyển giao bị offline hoặc chậm, dịch vụ sẽ bị gián đoạn, vậy có ổn không?” “Không được, không được gián đoạn!”
      “Nếu nhất định phải ghi log nhưng không thể ghi log thì sẽ làm gì?” Dạo này tôi chỉ trỏ vào định lý CAP và nói logging cũng giống các hệ thống phân tán khác. Có lẽ vì có bài Wikipedia với hình tam giác và từ “định lý” nên mọi người có vẻ chấp nhận
    • Đã có lần toàn bộ môi trường production bị đứng khi máy chủ syslog dừng. Chúng tôi đang đẩy log qua TCP, và việc blocking đó lan ra toàn bộ môi trường production
      Sau đó chúng tôi chuyển sang truyền qua UDP. Vì mất một phần log còn tốt hơn mất toàn bộ production
    • Trong thư viện logging của $MSFT cũng có kiểu vấn đề “thư viện logging làm hỏng tất cả”. Hãy tưởng tượng 100 thread, mỗi thread có một buffer logging 300MB
      Dĩ nhiên là nó phá nát bộ nhớ, và server crash ngay cả trên SKU đắt nhất của Azure App Service
    • Nếu tính sẵn sàng của sản phẩm phụ thuộc vào ±100ms thì thiết kế đã sai nghiêm trọng, và tôi không nghĩ đó là lỗi của thư viện logging. Người dùng sẽ không quan tâm liệu sau khi bấm nút có mất thêm 100ms để hoàn tất hay không
  • Tự nhiên nhớ chuyện ngày xưa. Khoảng 17–18 năm trước, tôi đã compile kernel Debian với RT_PREEMPT để dùng cho thiết bị khoa học cần timing chặt hơn
    Độ trễ và jitter khi đó rất ấn tượng. Từ đó đến nay tôi gần như không nghĩ tới nữa, nhưng có vẻ nó có nhiều chỗ dùng khi làm ứng dụng nhúng bằng Raspberry Pi mà không muốn chuyển sang vi điều khiển chạy RTOS

    • Việc nhắc tới Raspberry Pi khá thú vị. Một hai ngày trước tôi thấy một bài viết nói rằng RpiOS khởi động và chạy bên trên một RTOS
      Điều đó đặc biệt thú vị vì trước đây tôi cũng từng thấy đề xuất chạy Linux như một tác vụ của RTOS. Cách làm là chạy những thứ cần deadline hard real-time trong RTOS, để chúng không bị ảnh hưởng bởi độ trễ mà hệ thống bộ nhớ ảo có thể gây ra. Tôi không nhớ đó chỉ là ý tưởng hay đã được triển khai thật, và cũng chỉ thấy nhắc một lần rằng RpiOS nằm trên RTOS nên khá tò mò
  • Tôi thắc mắc điều này có ý nghĩa gì với người dùng phổ thông. Đây là tính năng chỉ bật trong những tình huống rất đặc thù, hay cũng có thể mang lại một hệ thống phản hồi tốt hơn cho đại chúng?

    • Theo tôi hiểu thì thời gian thực làm hệ thống chậm hơn. Để trở thành thời gian thực, mọi thứ đều phải có phân bổ thời gian
      Mỗi tác vụ nhận một ngân sách X và không được vượt quá. Nếu trường hợp tốt nhất thì nhanh nhưng trường hợp xấu nhất lại chậm, điều đó có nghĩa là hệ thống luôn phải giả định theo trường hợp xấu nhất
    • RT không nhất thiết cải thiện độ trễ, mà đặt một giới hạn trên cố định cho một số tác vụ. Tuy vậy, những công việc cần thiết để làm cho RT khả thi chắc chắn có thể cải thiện độ trễ trong trường hợp thông thường
      Ví dụ tránh các lời gọi printk() đồng bộ chính là như vậy, và ngay cả khi không bật RT thì nó cũng nên cải thiện độ trễ khi hệ thống chịu tải. Tôi nghĩ một kernel RT đã được upstream hoàn toàn sẽ không hoạt động khác kernel thông thường, trừ khi thật sự chạy các tiến trình RT. Lý do việc upstream mất nhiều thời gian là vì cần các đánh đổi để làm cho RT khả thi, và theo bài viết thì giờ đây những đánh đổi đó không còn nhiều
    • Nếu “người dùng phổ thông” nghĩa là người dùng desktop thì sẽ không có thay đổi lớn. Nhưng với thiết bị nhúng như điều khiển công nghiệp và thiết bị viễn thông thì đây là chuyện lớn
      Vì khi cần lập lịch thời gian thực, họ cũng có thể dùng kernel mainline mới nhất
    • Theo tôi hiểu, Linux sẽ trở thành một lựa chọn trong những tình huống cần RTOS. Nó dành cho các hệ thống quan trọng như hàng không, thiết bị y tế, và không ảnh hưởng nhiều đến người dùng phổ thông
    • Nhóm người dùng cuối desktop phổ biến nhất có thể hưởng lợi là những người làm âm thanh. Ở đó độ trễ, đặc biệt là jitter, có thể khá phiền phức
  • Tôi tò mò mọi người nghĩ gì về Xenomai[1]. Tôi đã dùng vài năm nay mà không gặp vấn đề gì
    Trên BeagleBone Black, jitter thường ở mức vài trăm nano giây, và tôi xem đó là thời gian thực “cứng”. Có thể lập lịch các tác vụ định kỳ ở mức vài chục micro giây và chưa từng bị lỡ lần nào
    Khác với Real-Time Linux, vốn cố gắng làm cho chính Linux có thể bị preempt, Xenomai về bản chất là kernel riêng và chạy Linux như một tác vụ bên trên nó. Nó cung cấp ABI để các tác vụ do người dùng tạo có thể chạy song song với Linux hoặc với mức ưu tiên cao hơn. Ví dụ, nó có thể đi vòng qua vấn đề printk(): Xenomai không bận tâm và sẵn sàng context switch khỏi printk để chạy tác vụ của người dùng
    Nhược điểm là trong ngữ cảnh Xenomai, bạn không thể thực hiện các system call thông thường. Vẫn có thể làm, nhưng hiển nhiên sẽ phá vỡ mô hình thời gian thực. Ví dụ nếu gọi printf() hoặc malloc() bên trong một tác vụ Xenomai thì nó không còn có thể bị preempt. ABI của Xenomai sao chép tối đa những gì có thể cần ở khía cạnh system call, và nếu bạn hài lòng với việc tự xử lý cấp phát heap thì nó hoạt động rất tốt
    [1]: https://xenomai.org/