Đưa “You” vào CPU
(cpu.land)- Đây là phần mở đầu của một dự án tự học nhằm hiểu đến cùng điều gì thực sự xảy ra khi chạy một chương trình trên máy tính
- Câu hỏi cốt lõi bắt đầu từ việc liệu chương trình có được chạy trực tiếp trên CPU hay không, rồi mở rộng sang cách system call hoạt động và cách nhiều chương trình chạy đồng thời
- Ngoài các bài giảng đại học, rất khó tìm được tài liệu hệ thống toàn diện, nên tác giả phải tự sàng lọc các tài liệu có chất lượng khác nhau và những thông tin mâu thuẫn
- Sau vài tuần nghiên cứu và ghi chép gần 40 trang, tác giả hiểu rõ hơn quá trình từ khi máy tính khởi động đến lúc chạy chương trình
- Tác giả cho rằng ngay cả những độc giả nghĩ mình đã biết cũng có thể học được điều mới; nếu không có thời gian, nên đọc Chương 3 trước
Điểm xuất phát để hiểu việc chạy chương trình
- Điểm xuất phát của bài viết là chính xác điều gì xảy ra khi chạy một chương trình trên máy tính
- Tác giả đã có một mức kiến thức low-level nhất định, nhưng gặp khó khăn khi kết nối nhiều mảnh kiến thức thành một luồng thống nhất
- Các câu hỏi phát triển theo ba hướng
- Chương trình có thật sự chạy trực tiếp trên CPU không
- System call là gì và thực tế hoạt động như thế nào
- Nhiều chương trình chạy đồng thời bằng cách nào
Quá trình tìm hiểu và thứ tự đọc
- Vì không có nhiều tài liệu hệ thống toàn diện, tác giả phải tự nghiên cứu từ nhiều nguồn, và chất lượng cũng như nội dung giữa các tài liệu khác nhau
- Qua vài tuần nghiên cứu và gần 40 trang ghi chú, tác giả hiểu rõ hơn cách máy tính hoạt động từ quá trình khởi động đến khi chạy chương trình
- Mục tiêu là tự tạo ra một bài giải thích vững chắc, đúng kiểu tài liệu mà tác giả từng muốn tìm
- Với những độc giả cảm thấy mình đã biết nội dung hoặc không có nhiều thời gian, tác giả khuyên đọc Chương 3 trước
- Bài tiếp theo là Chương 1: The “Basics”
1 bình luận
Ý kiến trên Hacker News
Tôi là người tạo ra cái này. Cảm ơn mọi người đã đọc và đúng chất Hacker News, đã chỉ ra rất nhiều điểm cần sửa. Putting the "You" in CPU vẫn đang được làm dở, ban đầu tôi định trau chuốt thêm và bổ sung nội dung rồi khoảng tuần sau mới đăng lên HN
Tôi 17 tuổi, đã nghỉ học trung học một năm trước và bắt đầu làm toàn thời gian tại Hack Club(https://hackclub.com/). Tôi đã lập trình từ khi còn nhớ được, và khoảng 6 năm trước bắt đầu học tại nhà để tập trung hơn vào lập trình và các mối quan tâm khác
Tất cả đều là tự học nên tôi chưa từng học lớp hệ thống nào ở đại học, và tôi không hài lòng với câu trả lời của mình cho câu hỏi “điều gì xảy ra khi chạy một thứ gì đó”. Vì vậy tôi đã dành rất nhiều thời gian để học sâu nhất có thể, và trong quá trình đó nhận ra rằng hệ điều hành và phần cứng thật sự rất thú vị để học, nhưng tài liệu trực tuyến về chủ đề này thì thường rất tệ
Khi tìm hiểu, nhiều khi tôi rơi vào các file PDF slide bài giảng từ năm 2014, hoặc các câu trả lời StackOverflow thực ra là sai hoặc đơn giản hóa quá mức. Vì thế tôi viết Putting the "You" in CPU với hy vọng nó trở thành tài liệu tốt hơn cho những người muốn tự học lĩnh vực này. Nó chưa hoàn hảo vì tôi vẫn còn phải viết thêm vài đoạn về SMP, nhưng tôi nghĩ nó tốt hơn nhiều so với hầu hết tài liệu tôi từng thấy. Đây cũng là lần đầu tôi tự làm hình minh họa và sơ đồ; càng về các chương sau càng khá hơn, và tôi khá tự hào về vài hình ở phần cuối
Toàn bộ được mở mã nguồn trên GitHub: https://github.com/hackclub/putting-the-you-in-cpu
Thỉnh thoảng tôi tìm được câu trả lời trong các thread Reddit nhưng thường lẫn nhiều nhiễu, và tôi cũng nghi ngờ Reddit có phải nơi đáng tin để lưu giữ tri thức không. StackOverflow thì chủ yếu là các câu hỏi giải quyết một tác vụ cụ thể nên câu trả lời tất yếu ngắn, vì thế đặc biệt không hữu ích
Với kiến thức phổ thông thì giờ tôi bỏ qua tìm kiếm Google đã hỏng và vào thẳng Wikipedia, nhưng Wikipedia cũng có giới hạn về mức độ sâu khi viết về chủ đề kỹ thuật. Tôi tự hỏi liệu có cần một wiki tập hợp toàn diện các chi tiết về cấu trúc bên trong máy tính không. Tài liệu rải rác thì có rất nhiều, nhưng nếu gom được vào một chỗ thì sẽ thật tuyệt
Tài liệu đó cho biết nhiều hơn về thế giới có thể trở thành như thế nào, hơn là hiện tại nó đang ra sao. Làm OSDEV sẽ học được nhiều thứ thú vị, chẳng hạn cách bộ xử lý 32-bit từng dùng PAE để định địa chỉ 36-bit, hay cách bộ xử lý 64-bit dùng định địa chỉ 52-bit để vắt thêm tiền
Bạn cũng sẽ hiểu vì sao máy tính khởi động ở chế độ 16-bit rồi phải làm đủ trò nhào lộn assembly cấp hệ thống để đi tới long mode, hay vì sao chế độ 8-bit vẫn còn tồn tại. Nếu bạn thích học về nạp binary thì vẫn còn nhiều thứ khiến bạn ngạc nhiên. Ví dụ, ngay cả trong chế độ 64-bit vẫn còn phân đoạn bộ nhớ, nhưng trong long mode nó bị ép thành ánh xạ phẳng
Tôi thích những tài liệu như thế này. Dạo này tôi đang đặt chân vào thế giới RISC-V, và có vẻ rõ ràng rằng những người muốn thúc đẩy phần mềm tự do và mã nguồn mở tiến lên cần loại kiến thức này để phần mềm chạy tốt trên nhiều máy tính đơn bo mới xuất hiện
Trong trường hợp của tôi, trong quá trình học máy tính hoạt động thế nào, việc định nghĩa kiến trúc tập lệnh, triển khai trình mô phỏng và assembler, rồi tự viết assembly là khá thường gặp. Người chăm hơn còn triển khai bằng FPGA hoặc viết backend LLVM
Phía hệ điều hành cũng tương tự. Tự triển khai kernel để học, hoặc ít nhất chỉnh sửa một kernel hiện có, là lộ trình khá phổ biến
Nếu muốn biết thêm máy tính hoạt động thế nào ở mức rất cơ bản, tôi thật sự khuyên đọc cuốn Code của Charles Petzold. Sách bắt đầu từ các nguyên lý đầu tiên rồi đi lên tới mức khá cao
Một trong những điểm tài liệu này làm tốt là chỉ ra kỳ vọng và kết quả học được thực tế trong quá trình thu thập thông tin. Kiểu như “tôi tưởng là x vì y, nhưng thực ra lại là a vì b!”
Gỡ bỏ hiểu lầm thường là bước cốt lõi để hình thành tri thức mới, và sẽ hữu ích khi tác giả thừa nhận rằng người đọc phải vượt qua hiểu lầm của chính mình trong lúc xử lý thông tin. Điều đó thân thiện với độc giả hơn nhiều so với thái độ “đây là đáp án đúng, nghĩ khác thì là đồ ngốc”
Chương 6 giải thích sai về giá trị trả về của fork(). Trái với những gì được viết, tiến trình cha phải nhận PID của tiến trình con, còn tiến trình con nhận 0. Tôi đã tạo pull request cho phần này
Trong phần giải thích rằng
add eax, 512được dịch thành05 00 02 00 00, byte đầu tiên05là opcode để cộng một số 16-bit vào thanh ghi EAX, còn các byte còn lại là 512 (0x200) theo little-endian.Nhưng giải thích này chỉ nói về 3 byte đầu trong lệnh dài 5 byte. Tôi thắc mắc các byte
00còn lại là gì. Có lẽ ý định là viết rằng opcode đó cộng một số 32-bit vào EAX chăngaxBài viết hay. Là một lập trình viên web tự học, tôi thấy những tài liệu như thế này rất có giá trị. Nó giúp hiểu cụ thể hơn cách mọi thứ thực sự vận hành, thoát khỏi nhiều lớp trừu tượng
Trông như một tài liệu tốt, đi vào trọng tâm khá nhanh, và cũng hơi thú vị. Nó không giống “hướng dẫn monad” như tôi đã nghĩ. Tôi sẽ đọc tiếp
Tài liệu hay. Tuy vậy tôi vẫn còn một thắc mắc về kernel. Trong [0], bài viết giải thích rằng shebang được xử lý trong kernel và vì nó lấy từ
bufchứ không tải toàn bộ tệp, nên luôn bị cắt theo độ dài củabufBốn năm trước, có người khó chịu vì kernel cắt các đường dẫn dài quá 128 ký tự, nên đã tăng gấp đôi kích thước bộ đệm, khiến điểm bị cắt cũng tăng gấp đôi. Vì vậy ngày nay trên Linux, nếu dòng shebang vượt quá 256 ký tự thì phần sau 256 ký tự sẽ biến mất hoàn toàn
Tôi không hiểu vì sao lại âm thầm cắt đường dẫn tệp của ai đó. Không phải nơi nào khác mà chính kernel thì càng không nên làm vậy. Đường dẫn tệp dài 256 byte có thể là vô lý, nhưng chuỗi đường dẫn + đối số dài 256 byte thì gần như chắc chắn sẽ xuất hiện lúc nào đó. Âm thầm làm hỏng script là sai
[0]: https://cpu.land/how-to-run-a-program
Vài năm trước, có người đã thử vá kernel để các shebang quá dài ít nhất sẽ thất bại rõ ràng thay vì âm thầm thất bại. Nhưng việc đó gây vấn đề trên NixOS. Đúng kiểu Nix, họ đã dùng nhiều shebang quá dài với các đường dẫn nix-store dài. Trước đây chúng bị cắt âm thầm nhưng vẫn chạy, còn đột nhiên tất cả các script đó đều thất bại, làm phá vỡ tương thích ngược. Vì vậy bản vá phải bị hoàn tác, và từ đó không có thay đổi tương tự nào xuất hiện nữa
Xem thêm chi tiết tại https://lwn.net/Articles/779997/
Bản thân shebang thậm chí còn không xử lý đúng hơn một đối số. Nếu viết
#!/bin/program -args somescriptthì nó sẽ được tách thành['/bin/program','-args somescript'], gần như luôn không phải kết quả mong muốn. Điều này chỉ giới hạn trên Linux, và shebang cũng không thật sự khả chuyển phổ quát trên mọi UnixNếu muốn làm việc phức tạp, tốt hơn là dùng
#!/bin/sh, rồi trong chính tệp hiện tạiexecvới các đối số cần thiếtMột điểm còn thiếu trong liên kết là có thể dùng
envđể tìm trình thông dịch trong đường dẫn hiện tại. Ví dụ#!/usr/bin/env python3sẽ tìmpython3mà người dùng đang dùng mà không cần biết vị trí của nóĐiều này có thể hữu ích cho các script chạy trong môi trường ảo, nơi hoạt động bằng cách ghi đè
PATHtới trình thông dịch Python. Tuy nhiên không nên làm vậy với chương trình được cài đặt. Vì không nên để chương trình đã cài đặt cho phép người dùng kiểm soát trình thông dịch nào sẽ được dùng, nên cần dùng đường dẫn tuyệt đốienvcó tùy chọn-S, có thể phân tích dòng lệnh từ một chuỗi đơn để xử lý giới hạn này. Dù vậy, với người đã tránh các mẹo vặt trong dòng shebang suốt nhiều năm, cùng lắm đây vẫn có vẻ là một thực hành đáng ngờ