<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Art</title><description>Home</description><link>https://firefly.cuteleaf.cn/</link><templateTheme>Firefly</templateTheme><templateThemeVersion>6.16.8</templateThemeVersion><templateThemeUrl>https://github.com/CuteLeaf/Firefly</templateThemeUrl><lastBuildDate>September 14, 2026 at 11:56:52 AM</lastBuildDate><item><title>Page Tables</title><link>https://firefly.cuteleaf.cn/posts/xv6-page-tables/</link><guid isPermaLink="true">https://firefly.cuteleaf.cn/posts/xv6-page-tables/</guid><description>How xv6 uses RISC-V page tables to give each process its own address space.</description><pubDate>Mon, 14 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Comming soon.&lt;/p&gt;</content:encoded></item><item><title>Intro and Overview</title><link>https://firefly.cuteleaf.cn/posts/xv6-intro-and-overview/</link><guid isPermaLink="true">https://firefly.cuteleaf.cn/posts/xv6-intro-and-overview/</guid><description>Notes on xv6, a simple Unix-like teaching operating system.</description><pubDate>Sun, 13 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;xv6 là một unix OS, được phát triển nhằm mục đích education của đại học MIT. Hệ điều hành xv6 lúc đầu được viết cho kiến trúc x86, nhưng về sau đã được chuyển đổi sang kiến trúc RISC-V để phù hợp với kiến trúc mở và chạy trên multicore. xv6 được chạy giả lập trên QEMU với khoảng ~6000 line code chủ yếu viết bằng ngôn ngữ C và assembly.&lt;/p&gt;
&lt;p&gt;xv6 được thiết kế với mục đích nhỏ gọn nhưng vẫn mang những đặc trưng cốt lõi của Unix bao gồm&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Processes&lt;/strong&gt;: chạy trong các không gian địa chỉ ảo (virtual address spaces) riêng biệt, được hỗ trợ bởi các bảng phân trang (page tables).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Filesystem&lt;/strong&gt;: hỗ trợ tệp và cấu trúc thư mục phân cấp kiểu Unix.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pipes&lt;/strong&gt;: cho phép truyền dữ liệu (pipe data) từ chương trình này sang chương trình khác.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multitasking&lt;/strong&gt;: có ngắt bộ đếm thời gian (timer interrupt) để các tiến trình chạy song song thông qua chia sẻ thời gian (time slicing).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;System calls&lt;/strong&gt;: hỗ trợ 21 lời gọi hệ thống cơ bản (so với 300-500 của các hệ điều hành thực tế).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;xv6 tích hợp 1 shell đơn giản gồm 10 lệnh:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;cat&lt;/strong&gt;, &lt;strong&gt;echo&lt;/strong&gt;, &lt;strong&gt;grep&lt;/strong&gt;, &lt;strong&gt;kill&lt;/strong&gt;, &lt;strong&gt;ln&lt;/strong&gt;, &lt;strong&gt;ls&lt;/strong&gt;, &lt;strong&gt;mkdir&lt;/strong&gt;, &lt;strong&gt;rm&lt;/strong&gt;, &lt;strong&gt;wc&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;xv6 cũng đã lược bỏ những thức phức tạp như: không có user IDs, không có quy trình login hay xác thực. Các bit bảo vệ tệp như Read, Write, Execute cũng được loại bỏ. Không có lệnh mount, chỉ sử dụng 1 hệ thống tệp. Không hỗ trợ Paging out, không thể chuyển không gian ảo ra disk, nghĩa là không thể chạy nhiều tiến trình vượt quá dung lượng RAM vật lý. Không hỗ trợ Networks, không có cơ chế giao tiếp IPC giữa các processes.&lt;/p&gt;
&lt;p&gt;Các System Calls có trong xv6&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;fork&lt;/strong&gt; , &lt;strong&gt;wait&lt;/strong&gt; , &lt;strong&gt;exit&lt;/strong&gt;, &lt;strong&gt;kill&lt;/strong&gt;, &lt;strong&gt;getpid&lt;/strong&gt;, &lt;strong&gt;sleep&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;open&lt;/strong&gt;, &lt;strong&gt;close&lt;/strong&gt;, &lt;strong&gt;read&lt;/strong&gt;, &lt;strong&gt;write&lt;/strong&gt;, &lt;strong&gt;pipe&lt;/strong&gt;, &lt;strong&gt;exec&lt;/strong&gt;, &lt;strong&gt;mknod&lt;/strong&gt;, &lt;strong&gt;link&lt;/strong&gt;, &lt;strong&gt;unlink&lt;/strong&gt;, &lt;strong&gt;fstat&lt;/strong&gt;, &lt;strong&gt;chdir&lt;/strong&gt;, &lt;strong&gt;dup&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;sbrk&lt;/strong&gt; (mở rộng vùng nhớ heap), &lt;strong&gt;uptime&lt;/strong&gt; (xem thời gian kernel đã chạy).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ref:&lt;br /&gt;
[1] &lt;a href=&quot;https://pdos.csail.mit.edu/6.828/2025/xv6/book-riscv-rev5.pdf&quot; target=&quot;_blank&quot;&gt;https://pdos.csail.mit.edu/6.828/2025/xv6/book-riscv-rev5.pdf&lt;/a&gt;&lt;br /&gt;
[2] &lt;a href=&quot;https://pdos.csail.mit.edu/6.828/2025/&quot; target=&quot;_blank&quot;&gt;https://pdos.csail.mit.edu/6.828/2025/&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Kernel và ranh giới cô lập</title><link>https://firefly.cuteleaf.cn/posts/xv6-kernel-and-isolation/</link><guid isPermaLink="true">https://firefly.cuteleaf.cn/posts/xv6-kernel-and-isolation/</guid><description>Why an operating system needs a kernel, and how RISC-V privilege modes and system calls enforce isolation.</description><pubDate>Sun, 13 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2&gt;Yêu cầu của một hệ điều hành&lt;a href=&quot;#yêu-cầu-của-một-hệ-điều-hành&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Mục tiêu đầu tiên của một hệ điều hành là cho phép nhiều việc chạy cùng lúc. Ví dụ, ta dùng &lt;code&gt;fork&lt;/code&gt; và &lt;code&gt;exec&lt;/code&gt; để chạy đồng thời một compiler và một editor. Để làm được điều đó, hệ điều hành phải có ba khả năng:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multiplexing&lt;/strong&gt;: chia sẻ tài nguyên, để một CPU có thể chạy xen kẽ giữa compiler và editor.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Isolation&lt;/strong&gt;: cô lập các tiến trình, để tiến trình này lỗi không làm hỏng tiến trình khác. Nếu editor bị crash, compiler vẫn chạy bình thường.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interaction&lt;/strong&gt;: các tiến trình vẫn phải trao đổi được thông tin với nhau khi cần. Từ đây nảy sinh một vấn đề: isolation không thể tuyệt đối, vì nếu cô lập hoàn toàn thì pipe sẽ không hoạt động. Do đó, hệ điều hành phải cân bằng được giữa isolation và interaction.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Android là một ví dụ thực tế rất rõ. Mỗi ứng dụng chạy dưới một Linux UID riêng, trong một sandbox riêng, không đọc được file và không nhìn thấy bộ nhớ của ứng dụng khác. Đó là isolation rất mạnh, và nhờ nó, một ứng dụng quảng cáo rác không dễ gì móc được dữ liệu ngân hàng của bạn.&lt;/p&gt;&lt;p&gt;Nhưng cũng chính vì thế, một việc tưởng chừng tầm thường như mở một tấm ảnh do ứng dụng khác tạo ra lại phải đi qua ContentProvider, qua Binder IPC, qua FileProvider với URI tạm thời có thời hạn, rồi qua hộp thoại xin quyền. Lập trình viên phải viết hàng chục dòng code chỉ để làm điều mà trên desktop Linux cũ chỉ cần một lời gọi &lt;code&gt;open()&lt;/code&gt; với đường dẫn.&lt;/p&gt;&lt;p&gt;Tăng cô lập thêm một nấc thì interaction phải trả giá bằng độ trễ, độ phức tạp, và cả những khoảnh khắc người dùng phải đứng đó bấm “Allow”.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;Tại sao cần hệ điều hành?&lt;a href=&quot;#tại-sao-cần-hệ-điều-hành&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Có một cách suy nghĩ khác: biến các system call thành một thư viện để ứng dụng link vào, và để ứng dụng tự điều khiển phần cứng. Một số hệ thống nhúng và hệ điều hành thời gian thực (RTOS) như Zephyr hay ThreadX làm đúng như vậy, vì cách này nhanh và dễ dự đoán thời gian chạy.&lt;/p&gt;&lt;p&gt;Nhưng khi có nhiều ứng dụng, mọi ứng dụng đều phải hoạt động một cách đúng đắn, ví dụ phải tự nhường CPU định kỳ. Cách này gọi là &lt;strong&gt;cooperative time-sharing&lt;/strong&gt; (chia sẻ thời gian kiểu hợp tác). Nó chỉ ổn khi mọi ứng dụng tin nhau và không có lỗi. Thực tế thì lại khác: các ứng dụng thường không tin nhau và có lỗi. Chỉ cần một vòng lặp vô hạn là đủ khiến cả hệ thống bị treo.&lt;/p&gt;&lt;p&gt;Do đó, để cô lập mạnh, hệ điều hành không cho ứng dụng chạm thẳng vào phần cứng, mà bọc tài nguyên thành các dịch vụ trừu tượng. Ví dụ:&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Tài nguyên&lt;/th&gt;&lt;th&gt;Thay vì truy cập trực tiếp&lt;/th&gt;&lt;th&gt;Unix cung cấp&lt;/th&gt;&lt;th&gt;Lợi ích&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Ổ đĩa&lt;/td&gt;&lt;td&gt;Đọc/ghi từng sector&lt;/td&gt;&lt;td&gt;&lt;code&gt;open&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;write&lt;/code&gt;, &lt;code&gt;close&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Có đường dẫn file tiện dụng. Kernel toàn quyền quản lý đĩa&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CPU&lt;/td&gt;&lt;td&gt;Ứng dụng tự nhường CPU&lt;/td&gt;&lt;td&gt;Kernel tự động chuyển CPU giữa các tiến trình, lưu và khôi phục thanh ghi&lt;/td&gt;&lt;td&gt;Ứng dụng không cần biết mình đang bị chia sẻ. Kể cả vòng lặp vô hạn cũng không làm treo máy&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Bộ nhớ&lt;/td&gt;&lt;td&gt;Ứng dụng tự chọn vùng RAM vật lý&lt;/td&gt;&lt;td&gt;&lt;code&gt;exec&lt;/code&gt; dựng ảnh bộ nhớ cho tiến trình&lt;/td&gt;&lt;td&gt;Kernel quyết định đặt tiến trình ở đâu, thậm chí đẩy bớt dữ liệu ra đĩa khi thiếu RAM. Chương trình được lưu dưới dạng file&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Giao tiếp&lt;/td&gt;&lt;td&gt;Tự trao đổi dữ liệu&lt;/td&gt;&lt;td&gt;File descriptor&lt;/td&gt;&lt;td&gt;Ẩn chi tiết dữ liệu nằm ở pipe hay file. Nếu tiến trình trước trong pipeline thoát, kernel tự báo end-of-file cho tiến trình sau&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;Giao diện system call của Unix được thiết kế để vừa tiện cho lập trình viên, vừa cho phép cô lập mạnh. Đây không phải cách duy nhất, nhưng đã được chứng minh là một cách tốt.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;User mode, supervisor mode và system call&lt;a href=&quot;#user-mode-supervisor-mode-và-system-call&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Cô lập mạnh nghĩa là ứng dụng, dù có lỗi hay cố tình phá hoại, cũng không được sửa, thậm chí đọc, dữ liệu và code của kernel, hay truy cập bộ nhớ của tiến trình khác. Phần mềm thuần tuý không làm được việc này, mà cần CPU hỗ trợ bằng phần cứng thông qua các mức quyền (privilege levels).&lt;/p&gt;&lt;section&gt;&lt;h3&gt;Ba mức quyền của RISC-V&lt;a href=&quot;#ba-mức-quyền-của-risc-v&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;Hình 2.1 RISC-V privilege levels.&quot; loading=&quot;lazy&quot; width=&quot;2322&quot; height=&quot;1294&quot; src=&quot;/_astro/risc_v_priv_levels_1.BoTe7xkw_Z1PcJAB.webp&quot; /&gt;&lt;figcaption&gt;Hình 2.1 RISC-V privilege levels.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Hiểu đơn giản thì:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Code chạy ở user mode thuộc &lt;strong&gt;user space&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Code chạy ở supervisor mode thuộc &lt;strong&gt;kernel space&lt;/strong&gt;. Phần mềm chạy ở đây gọi là &lt;strong&gt;kernel&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;Tổ chức kernel&lt;a href=&quot;#tổ-chức-kernel&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Có một câu hỏi được đặt ra: phần nào của hệ điều hành nên chạy ở supervisor mode? Từ đó hình thành hai trường phái chính.&lt;/p&gt;&lt;section&gt;&lt;h3&gt;Monolithic kernel (kernel nguyên khối)&lt;a href=&quot;#monolithic-kernel-kernel-nguyên-khối&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Toàn bộ hệ điều hành nằm trong kernel. Mọi system call đều được xử lý ở supervisor mode, và cả hệ điều hành là một chương trình duy nhất.&lt;/p&gt;&lt;p&gt;Comming soon.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;Microkernel&lt;a href=&quot;#microkernel&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Mục tiêu của microkernel là giảm bug trong kernel, bằng cách chỉ giữ lại tối thiểu chức năng trong kernel. Khi đó, rất ít code chạy ở supervisor mode, và kernel dễ hiểu, dễ kiểm chứng tính đúng đắn.&lt;/p&gt;&lt;p&gt;Phần lớn hệ điều hành chạy dưới dạng các server ở user mode.&lt;/p&gt;&lt;p&gt;Comming soon.&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item></channel></rss>