- Bunnix, khởi đầu như một dự án làm cho vui lúc nghỉ ngơi, đã thử xem có thể xây được một hệ điều hành kiểu Unix cho x86_64 đến mức nào trong khoảng một tháng, và thực tế mất 27 ngày làm việc
- Kernel chủ yếu được viết bằng Hare, đồng thời dùng thêm các thành phần C như lwext4 để hỗ trợ ext4 và libvterm cho terminal video của kernel
- Hỗ trợ cả legacy boot lẫn EFI và đã được thử trên một số laptop thực tế, nhưng do chưa hỗ trợ USB nên cần bàn phím PS/2 hoặc giả lập PS/2 của BIOS
- Không gian người dùng chủ yếu gồm phần mềm bên thứ ba như dash, Doom, gzip, less, mandoc, sbase, tcc, Vim 5.7, còn libc là bản musl libc đã được chỉnh sửa cho phù hợp với Bunnix
- Bunnix hoạt động được nhưng còn nhiều lỗi và vẫn là hệ thống một người dùng; đây gần như là một thử nghiệm dẫn tới việc làm lại Helios và cải thiện thiết kế kernel hơn là một dự án để bảo trì lâu dài
Phạm vi và cách chạy Bunnix
- Bunnix là dự án hệ điều hành kiểu Unix cho x86_64, bắt đầu từ ngày 21 tháng 4 năm 2024
- Nếu bỏ qua những ngày không thực sự làm việc, tổng thời gian đầu tư là 27 ngày
- Có cung cấp Bunnix 0.0.0 iso để có thể tự chạy trực tiếp
- Trên qemu, có thể boot ISO bằng lệnh sau
qemu-system-x86_64 -cdrom bunnix.iso -display sdl -serial stdio
- Có thể ghi ISO ra USB stick để boot trên phần cứng thật
- Có khả năng chạy trên hầu hết máy AMD64
- Đã được thử trên ThinkPad X220 và Starlabs Starbook Mk IV
- Hỗ trợ cả legacy boot và EFI
- Hạn chế lớn nhất khi chạy là chưa hỗ trợ USB
- Cần bàn phím PS/2 hoặc giả lập PS/2 của BIOS
- Bàn phím của đa số laptop được kết nối theo kiểu PS/2
- Việc bàn phím USB có hoạt động qua giả lập PS/2 hay không còn tùy môi trường
- Bản port Doom có một số hạn chế về gán phím và cách thoát
- Di chuyển bằng WASD
- Bắn bằng Shift phải
- Mở cửa bằng Space
- Do không thể thoát game đúng cách nên phải khởi động lại sau khi chơi
Cấu trúc kernel và các tính năng hỗ trợ
- Kernel của Bunnix phần lớn được viết bằng Hare, đồng thời dùng thêm một số thành phần C
- Dùng lwext4 để hỗ trợ hệ thống tệp ext4
- Dùng libvterm cho terminal video của kernel
- Các driver được hỗ trợ tập trung vào phạm vi cần thiết cho phần cứng cơ bản và boot từ thiết bị lưu trữ
- PCI legacy
- Thiết bị khối AHCI
- Bảng phân vùng GPT và MBR
- Bàn phím PS/2
- Cổng serial của nền tảng
- Đồng hồ CMOS
- Framebuffer do bootloader thiết lập
- Hệ thống tệp ext4 và memfs
- Cũng bao gồm các chức năng kernel cơ bản cần cho một hệ thống kiểu Unix
-
Hệ thống tệp ảo và thiết bị
- Cung cấp
/dev với thiết bị khối, các thiết bị giả null, zero, full, /dev/kbd, /dev/fb0, TTY serial và video, cùng terminal điều khiển /dev/tty
- Có trình giả lập terminal khá hoàn chỉnh và hỗ trợ termios hoạt động ở mức tương đối
-
System call và mô hình người dùng
- Hỗ trợ khoảng 40 system call, gồm
clock_gettime, poll, openat, fork, exec, pipe, dup, dup2, ioctl...
- Hiện tại Bunnix là hệ thống một người dùng
- Không cưỡng chế chế độ tệp và quyền sở hữu kiểu Unix
- Nếu làm thêm vài ngày nữa thì có thể biến thành hệ thống nhiều người dùng
Bootloader và không gian người dùng
- Bunnix có hai bootloader
- Bootloader cho legacy boot tương thích multiboot và được viết bằng Hare
- Bootloader cho EFI được viết bằng C
- Hai bootloader sẽ nạp kernel dưới dạng tệp ELF và initramfs khi cần
- Bootloader EFI kèm zlib để giải nén initramfs
- Bootloader tương thích multiboot sẽ xử lý việc giải nén thay cho nó
- Không gian người dùng phần lớn được cấu thành từ mã nguồn bên thứ ba
- Colossal Cave Adventure
advent
dash /bin/sh
- Doom
- gzip
- less
lok /bin/awk
- lolcat
- mandoc
- sbase core utils
- tcc C compiler
- Vim 5.7
- libc là nhánh tách ra từ musl libc và đã được chỉnh sửa theo nhiều cách để phù hợp với yêu cầu của Bunnix
- Thư viện curses dựa trên netbsd-curses
- Hệ thống chạy được nhưng còn nhiều lỗi, và một số phần được làm gấp, nên cần chuẩn bị tinh thần đối mặt với crash
Những yếu tố giúp triển khai nhanh và các điểm khó
- Một phần mã của Bunnix đến từ dự án trước đó là Helios
- Bao gồm một số mã kernel liên quan đến thiết lập CPU phổ biến như GDT, IDT
- Một số driver như AHCI cũng được điều chỉnh cho phù hợp với hệ thống Bunnix
- Tác giả cho rằng nếu không có kinh nghiệm từ Helios thì khó có thể làm Bunnix nhanh đến vậy
- Hỗ trợ ext4 và tích hợp terminal ảo là những phần đặc biệt khó
- Phải mang vào các phụ thuộc bên ngoài là lwext4 và libvterm
- Tầng hệ thống tệp đã được viết lại vài lần và hiện vẫn còn lỗi
- Để triển khai đúng thiết kế hệ thống tệp kiểu Unix, gồm cả
openat và xử lý inode, tác giả phải đào sâu hơn vào bên trong lwext4
- Cũng thu được kinh nghiệm liên kết chung mã nguồn Hare, assembly và C trong dự án Hare
- Nhìn chung mọi thứ hoạt động tốt, nhưng phần tạo lớp nối ABI thì khá bất tiện
- Điều này làm nảy sinh nhu cầu tự động chuyển C header thành module forward declaration cho Hare
- Công việc liên quan đã có một phần trong
hare-c nhưng vẫn cần thêm
- Mục tiêu port Vim khiến độ khó của triển khai terminal tăng lên
- libvterm là thư viện state machine cho terminal rất tốt nhưng tài liệu còn thiếu
- Cần rất nhiều tinh chỉnh nhỏ để tích hợp chính xác
- Tác giả cũng đã dành thời gian cho tối ưu hiệu năng để mọi thứ chạy mượt hơn
- Scheduler là phần đã bỏ đi khá nhiều mã từ Helios và viết lại
- Cả Helios lẫn Bunnix đều là hệ thống một CPU
- Khác với Helios, Bunnix cho phép chuyển ngữ cảnh bên trong kernel
- Việc chuyển tác vụ có tính tiền nhiệm cũng đi vào và ra thông qua kernel
- Cấu trúc này đòi hỏi nhiều kernel stack và một cách chuyển tác vụ khác
- Khi có scheduler đủ vững, các tác vụ chặn như đọc đĩa hay
pipe(2) có thể được triển khai đơn giản bằng wait queue
- Việc triển khai signal là cần thiết để tương thích Unix
- Helios không nhắm tới Unix nên vẫn hoạt động mà không cần signal
- Trong Bunnix, phần này chủ yếu được điều chỉnh để
SIGCHLD hoạt động đúng cho bản port dash
- Phần triển khai signal cuối cùng vẫn ở mức rất cơ bản
Các bài học thiết kế dẫn trở lại Helios
- Bunnix là kernel nguyên khối, còn Helios là thiết kế microkernel không phải Unix
- Ở hệ thống tệp, dự án cho thấy tầm quan trọng của caching
- Helios chia phần triển khai hệ thống tệp ra nhiều driver và tiến trình riêng biệt
- Caching trong tầng hệ thống tệp rất quan trọng, kể cả chỉ để theo dõi các đối tượng còn sống
- Khi làm lại Helios, sẽ có nhiều việc phải refactor hoặc viết lại mã hệ thống tệp
- Cách tiếp cận driver trong kernel nguyên khối tự nhiên là đơn giản hơn
- Tuy vậy, tác giả cũng không hoàn toàn hài lòng với việc đưa quá nhiều thứ vào ring 0
- Vẫn có thể đưa một số yếu tố về luồng điều khiển của thiết kế nguyên khối vào scheduler của Helios
- Ở quản lý bộ nhớ, bitmap allocator hoạt động tốt hơn mong đợi
- Trong Helios, tác giả từng cố tránh bitmap allocator và quản lý bộ nhớ là một điểm gây khó chịu lớn
- Bunnix dùng một bitmap allocator đơn giản cho toàn bộ các trang thông thường của hệ thống
- Overhead không lớn như lo ngại và nó hoạt động rất tốt
- Tác giả cho rằng làm ra Bunnix trong 30 ngày sẽ là điều không thể nếu dùng thiết kế microkernel
- Kernel nguyên khối đơn giản hơn rất nhiều để triển khai
- Tuy vậy, các lợi thế của thiết kế microkernel vẫn hấp dẫn và câu trả lời tốt hơn có thể là hybrid kernel
Trạng thái dự án và các hướng cải thiện còn lại
- Bunnix gần như là một dự án nghệ thuật gần hoàn chỉnh hơn là thứ sẽ tiếp tục được đầu tư rất nhiều thời gian
- Thỉnh thoảng vẫn có thể làm thêm vài ngày
- Có thể nhận bản vá cải thiện từ cộng đồng qua public inbox
- Hướng phát triển OS tiếp theo là quay lại Helios để thực hiện các tái thiết kế lớn dựa trên những bài học từ Bunnix
- Các ưu tiên cải thiện có thể gồm
- Directory cache cho hệ thống tệp và cải thiện caching tổng thể
- Sửa lỗi ext4
- procfs và top
- mmap cho tệp
- Các signal bổ sung như
SIGSEGV
- Hỗ trợ nhiều người dùng
- Thiết bị khối NVMe
- Thiết bị khối IDE
- Hỗ trợ ATAPI và ISO 9660
- Hỗ trợ Intel HD audio
- Network stack
- Bộ công cụ Hare cho hệ thống cơ sở
- Tự self-hosting
1 bình luận
Ý kiến trên Hacker News
Thật sự rất tuyệt. Làm mình nhớ đến câu chuyện rằng Unix ban đầu cũng được tạo ra trong vài tuần khi gia đình Ritchie đi nghỉ ở California để thăm bố mẹ vợ
Nguồn là UNIX: A History and a Memoir của Brian W. Kernighan
Giờ trí nhớ hơi mơ hồ nên tôi cần kiểm tra lại
Tôi muốn đọc thêm tài liệu chi tiết hơn về đoạn “cuối cùng tôi đã hiểu tín hiệu hoạt động như thế nào từ trên xuống dưới, và nó thực sự rất xấu. Tôi luôn cảm thấy đây là một trong những phần yếu nhất trong thiết kế Unix, và dự án này cũng không làm tôi đổi ý”. Nếu có ai trên HN hoặc chính tác giả biết thêm thì tôi rất muốn nghe
https://www.amazon.com/Advanced-Programming-UNIX-Environment...
Đây là cuốn sách nói về API Unix ở không gian người dùng, bao gồm cả tín hiệu và tiến trình
Nếu muốn triển khai tín hiệu bên trong kernel thì tôi không chắc nên gợi ý gì, nhưng có lẽ là https://pdos.csail.mit.edu/6.828/2012/xv6.html
Đọc một cuốn sách giải thích rõ ràng, có hệ thống cách Unix hoạt động kèm theo ví dụ tự thân đầy đủ thực sự rất sảng khoái. Nếu không biết C thì đó có thể là rào cản, nhưng khi đọc bài blog cũng vậy
Tôi không nghĩ có cùng lượng thông tin như thế nằm đâu đó trên web. Ngay cả blog của tôi cũng có nhiều mẩu kiến thức vụn về Unix mà người ta vẫn còn đọc, nhưng không đạt đến mức đó
Tôi cho rằng để hiểu tín hiệu Unix thì tiếp cận bằng bài blog, Google hay LLM là một trong những cách kém hiệu quả nhất. Kể cả mua sách cũ cũng khó mà gọi là “rẻ”, nhưng đó là cuốn sách giữ giá cao vì thông tin trong nó xứng đáng, và với một lập trình viên đang đi làm thì nó vẫn tương đối rẻ
Tín hiệu là một nỗ lực gượng ép để vá thêm IPC bất đồng bộ vào thiết kế, nên rất dễ dính race condition. Nếu đang xử lý một tín hiệu mà lại nhận thêm tín hiệu khác thì sao, hoặc nếu tiến trình đang ở trong system call thì phải xử lý tín hiệu thế nào, đều khá mơ hồ. Phải quyết định là trì hoãn, đưa vào hàng đợi, hay kéo nó ra khỏi system call
Nếu mọi system call đều là bất đồng bộ, như nguyên tắc thiết kế mà nhiều hệ điều hành hiện đại áp dụng, thì khía cạnh đó được giải quyết. Nếu có một hệ thống kiểu kênh tin cậy cho IPC thì không chỉ tín hiệu mà cả giao tiếp liên tiến trình bất đồng bộ tinh vi hơn hoặc gọi thủ tục cũng có thể được triển khai
SIGSTOP/SIGCONT/SIGKILL trên thực tế không hẳn là gửi tín hiệu cho tiến trình mà là điều khiển tiến trình như tạm dừng, tiếp tục và kết thúc
Những thông điệp bất đồng bộ đơn giản như SIGHUP, SIGUSR1, SIGUSR2, SIGTTIN, SIGTTOU bị lạm dụng cho đủ thứ như đọc lại cấu hình, và còn kéo theo các cách lách kiểu hack như nohup để daemon hóa. gunicorn thậm chí dùng hai cái sau cho co giãn động. Trong nhóm này còn có những thứ cụ thể một cách kỳ quặc như SIGWINCH
Rồi còn có các tín hiệu như SIGILL, SIGSEGV, SIGFPE biểu thị lệnh sai, lỗi phân đoạn, ngoại lệ dấu phẩy động. Cả SIGSYS cũng thuộc loại mà ngay từ đầu đã không rõ có nên để bất đồng bộ hay không
Những cách tiếp cận khác cũng có đánh đổi riêng. Windows có event, SEH, các routine xử lý CTRL+C/CTRL+BREAK/thoát, IOCP, callback, v.v. Notes của Plan 9 là chuỗi nên có điểm hay là có thể gửi dữ liệu tùy ý cho tiến trình khác, nhưng việc dùng cùng một cơ chế đó cho cả điều khiển tiến trình thì vẫn có nhược điểm giống *nix; theo tôi nó chỉ là chuỗi thay vì số mà thôi
Bài viết nói về các vấn đề của tín hiệu Unix, đồng thời giải thích vì sao signalfd mà Linux tạo ra để giải quyết chuyện này lại không hoạt động tốt
https://pubs.opengroup.org/onlinepubs/009604499/functions/bs...
Một điểm quan trọng nữa là mã bên trong signal handler phải có tính reentrant. “Các hàm không reentrant nhìn chung không an toàn để gọi trong signal handler”
https://man7.org/linux/man-pages/man7/signal-safety.7.html
Tôi từng quan tâm đến Hare, nhưng khi đọc mục FAQ này thì thấy đây là một chính sách khá tự hủy: https://harelang.org/documentation/faq.html#will-hare-suppor...
Về cơ bản, tôi ủng hộ việc lập trình viên dùng giấy phép họ muốn, nhắm đến hệ điều hành họ muốn, và viết mã họ muốn
Nhưng điều đó không có nghĩa là chính sách cụ thể này là một ý hay. Ngay cả FSF, vốn thường được xem là nhóm theo triết lý phần mềm tự do cực đoan hoặc nguyên tắc nhất, cũng hỗ trợ Windows và POSIX. Có thể vừa phàn nàn vừa gọi nó là Woe32, nhưng Stallman đã nhiều lần lập luận khá thuyết phục rằng việc làm cho các dự án phần mềm tự do chạy được trên các hệ thống độc quyền thực ra có ích hơn cho cuộc đấu tranh hướng tới một thế giới không có phần mềm độc quyền
Mã thư viện được cấp phép theo MPL nên chỉ vì dùng Hare mà bạn không bị trói vào một giấy phép cụ thể. Nhưng tôi nghi ngờ tuổi thọ của một ngôn ngữ có thái độ kiểu “không hỗ trợ, đừng hỏi trên diễn đàn, đừng đến đây” đối với hơn 95% máy tính để bàn sẽ ra sao
Trớ trêu là khi tìm “harelang repo” trên Google, kết quả đầu tiên lại là một bản port macOS không chính thức, còn kho SourceHut thực tế thì không xuất hiện ở trang đầu
Ngôn ngữ hoặc sẽ lớn mạnh như quả cầu tuyết, hoặc sẽ biến mất. Tôi đang viết bình luận này trên máy Mac, nhưng nếu muốn thì tôi cũng có thể dùng một máy Linux ngay bây giờ. Vậy tại sao tôi phải học một ngôn ngữ buộc lập trình viên phải vượt qua một bài kiểm tra sự thuần khiết mà ngay cả FSF cũng không làm? Một phần rất lớn của mã nguồn mở và phần mềm tự do được viết trên Mac, và nhiều hơn bạn nghĩ cũng được viết trên Windows
Theo tôi, điều khiến Hare khác với Odin hay Zig chính là thái độ thuần khiết và loại trừ này. Chúc vui khi hack và thành công, nhưng với điều sau thì tôi bi quan
Mặt khác, đó cũng không phải đoạn duy nhất trong FAQ khiến người ta phải nhướng mày
“Không có trình quản lý gói, và việc tái sử dụng mã như một giá trị chung được khuyến khích ít hơn”
“qbe tạo ra mã chậm hơn LLVM, và hiệu năng nằm trong khoảng 25–75% thời gian chạy so với mã tương đương do LLVM tạo ra”
“Có thể dùng đa luồng trong Hare không? Có lẽ là không”
“Vậy tôi phải tự triển khai bảng băm à? Đúng vậy. Bảng băm là cấu trúc dữ liệu phổ biến mà nhiều chương trình Hare sẽ phải tự cài đặt từ đầu”
Ở thời điểm hiện tại, đây rõ ràng không phải là một ngôn ngữ được thiết kế với mục tiêu được chấp nhận rộng rãi. Điều đó cũng ổn, và ít nhất họ nói thẳng điều đó
Bạn có quyền nêu ý kiến, nhưng tôi không nghĩ kiểu chỉ trích này là xây dựng khi các nhà phát triển đã định sẵn định hướng
Không phải ban nhạc nào cũng phải lên bảng xếp hạng Billboard thì mới đáng nghe
Hỗ trợ những hệ điều hành mà chính các nhà phát triển không dùng là một đòi hỏi lớn
Có không ít ngôn ngữ tuy không quá phổ biến nói chung nhưng vẫn phát triển mạnh trong một ngách vững chắc và giữ vai trò quan trọng
Ấn tượng, rất ngầu, và đầy cảm hứng. Những ví dụ kiểu “làm ra thứ ấn tượng trong X ngày” đòi hỏi kinh nghiệm và tài năng tích lũy suốt nhiều năm
Tôi phải đưa chuỗi tiếng Anh vào catalog, cập nhật nhiều bài kiểm thử, chạy test trên hệ thống cục bộ, đưa thay đổi lên cụm staging, sửa những lỗi test ngoài dự kiến, đẩy lên production, nhờ người phụ trách dịch sang nhiều ngôn ngữ, rồi còn phải cập nhật tài liệu
https://www.ticalc.org/archives/files/fileinfo/463/46387.htm...
Điều cần để mở rộng nó thành thứ hoàn chỉnh không phải thiên tài đặc biệt mà là sự bền bỉ
Rất tuyệt khi được xem các cập nhật gần như hằng ngày trên Mastodon. Có thể thấy một người nhiều kinh nghiệm dần dần lắp ghép phần mềm phức tạp lại với nhau
https://fosstodon.org/@drewdevault/112319697309218275
Mã ở đây: https://git.sr.ht/~sircmpwn/bunnix/tree/master
Cấp phép theo GPLv3
Không gian người dùng phần lớn được ghép từ các nguồn bên thứ ba
Ban đầu tôi khá ngạc nhiên khi bấm vào ISO thấy hiện tải xuống 60MB, nhưng rồi cũng hiểu vì sao
Để so sánh, Linux 0.01 chỉ là bản tải xuống 71KB, nhưng nó chỉ chứa mã nguồn kernel mà thôi
Hare có vẻ là một ngôn ngữ thú vị
Tuy nhiên, trong thời đại đa lõi này, có vẻ như các hạn chế dưới đây sẽ làm hạn chế việc chấp nhận nó
Theo FAQ https://harelang.org/documentation/faq.html, với câu hỏi liệu có thể dùng multithreading trong Hare hay không, câu trả lời là “có lẽ là không”
Đối với đa hợp tác vụ vào/ra thì khuyến nghị event loop, còn khi cần dùng song song tài nguyên CPU thì khuyến nghị đa tiến trình kèm bộ nhớ chia sẻ
Nói một cách chặt chẽ thì vẫn có thể tạo thread trong chương trình Hare. Có thể liên kết với libc để dùng pthreads hoặc trực tiếp dùng system call clone(2). Các hệ điều hành được triển khai bằng Hare như Helios thường cũng triển khai multithreading
Tuy nhiên, standard library upstream không bảo đảm tính tái nhập nên bạn phải hoàn toàn tự chịu trách nhiệm để tránh tự bắn vào chân mình
Cá nhân tôi thích cách này cho phần lớn trường hợp sử dụng, vì nó giới hạn khả năng xảy ra data race chỉ trong vùng bộ nhớ chia sẻ. Xét từ góc độ data race, nó có cảm giác giống như “unsafe block” của bộ nhớ
Đây là nội dung từ “Linux System Call Table – Chromiumos” https://www.chromium.org/chromium-os/developer-library/refer... https://news.ycombinator.com/item?id=33395777
google/syzkalleR
System call của Fuschia / Zircon: https://fuchsia.dev/fuchsia-src/reference/syscalls
“Memory Sealing ‘Mseal’ System Call Merged for Linux 6.10”
https://news.ycombinator.com/context?id=40474551