Tuesday, September 8, 2020

05 - Nên đọc mã nguồn (source code) như thế nào?

Một trong số những người có ảnh hưởng lớn nhất đến ngành Công nghệ thông tin thế giới và đến cả cuộc sống của chúng ta đó là Linus Torvalds (sinh năm 1969 ở Phần Lan). Hàng ngày hàng giờ, hàng tỷ người vẫn đang sử dụng những sản phẩm có liên quan đến đại cao thủ lập trình đó. Hệ điều hành Linux do ông sáng tạo ra vào năm 1991 đã len lỏi trong hàng tỷ thiết bị IoT nhỏ bé cho đến các siêu máy tính có sức mạnh khủng khiếp: "Linux Runs on All of the Top 500 Supercomputers, Again!" (https://itsfoss.com/linux-runs-top-supercomputers/). Hàng tháng có 2.5 tỷ người thường xuyên sử dụng thiết bị Android (điện thoại, máy tính bảng...) theo [1], trong khi đó họ nhà táo chỉ có 1.4 tỷ thiết bị IOS mà thôi. Thị phần hệ điều hành cho thiết bị di động Android: 74.25% và IOS là 25.15% [2]. Android chính là một phiên bản rút gọn của Linux. Linus Torvalds là một người rất đam mê công nghệ và gần 30 năm qua ông vẫn code và chủ trì chính dự án Linux Kernel và Git, đồng thời ông cũng là một người thẳng thắn và có nhiều phát biểu rất sốc. Linus từng nói: "Chém gió chỉ là vớ vẩn, ông hãy trình code ra đi" - “Talk is cheap. Show me the code.” Hôm nay ông giáo sẽ chia sẻ kinh nghiệm đọc mã nguồn của Linus Torvalds: Linux Kernel nói riêng và nghiên cứu mã nguồn của một dữ án mã nguồn mở nói chung. Ông giáo đã từng đọc mã nguồn Linux Kernel có 10 triệu dòng lệnh, Bitcoin: 500 nghìn dòng lệnh và Ethereum: 600 nghìn dòng lệnh.

1. Thách thức khi đọc một dự án mã nguồn mở

Ngày nay có hơn 180.000 dự án mã nguồn mở, trong số đó có rất nhiều dự án đồ sộ, ví dụ như dự án nhân của hệ điều hành Linux phiên bản 5.8.2 đã là 27.8 triệu dòng lệnh [3]. Cách đây 15 năm khi ông giáo nghiên cứu bản 2.4 thì đâu đó mới chỉ có 10 triệu dòng lệnh mà thôi. Bơi trong một biển dòng lệnh như vậy sẽ khiến cho vô vàn code sĩ hụt hơi, choáng ngợp và thường là bỏ cuộc.

Vậy những khó khăn thách thức chủ yếu là gì:

  • Không biết bắt đầu từ đâu: giống như được thả vào một cách rừng bát ngát toàn cây và không biết đi về hướng nào.
  • Ít có tài liệu hướng dẫn chi tiết và mô tả thiết kế, nên không hiểu được logic của hệ thống. Đôi khi xem lại chính code mình viết cách đây vài năm là đã thấy khó hiểu huống chi đọc code của bao nhiêu người khác có văn hóa, trình độ và thói quen hoàn toàn khác.
  • Rất khó theo dõi, đọc code không giống như đọc truyện, không như các trang sách cứ tuần tự từ đầu đến cuối. Trong mã nguồn thường một hàm sẽ gọi nhiều hàm con, trong hàm con đó lại gọi hàm cháu, hàm chắt... Và các hàm này không được viết liền nhau mà sẽ nằm ở một vị trí khác nhau trong file mà thậm chí nằm ở những file khác và ở những thư mục khác, người đọc sẽ phải switch liên tục từ chỗ này ra chỗ khác từ file này qua file khác và qua cả các thư mục khác... 
  • Với nhưng dự án lớn thì thời gian và công sức sẽ là những thách thức rất lớn, đòi hỏi người đọc phải có tinh thần thép, không bị nản, không bị ngợp mới có thể đi được đến cùng.
Để có thể đọc và nghiên cứu mã nguồn của một dự án lớn, chúng ta cần phải trang bị những công cụ hữu hiệu và một chiến lược hay một phương phát đọc mã nguồn hiệu quả.

2. Công cụ phần mềm để đọc mã nguồn

Các công cụ này không nhất thiết phải có tuy nhiên có thì sẽ hỗ trợ công việc tốt hơn và hiệu quả hơn. Các công cụ này cũng là những công cụ mà ông giáo cảm thấy hay ho và lựa chọn chúng hoàn toàn là do sở thích cá nhân, người khác có thể dùng các công cụ quen thuộc khác hoặc không dùng công cụ gì cả.
  • Bộ soạn thảo mã nguồn tích hợp IDE (Integrated Development Environment): thường thì làm việc với ngôn ngữ nào thì sẽ lựa chọn IDE thường được sử dụng để viết ra các mã nguồn đó. Các công cụ này thường tích hợp nhiều tính năng, trong đó quan trọng là soạn thảo, gỡ rối và biên dịch. Có thể kể đến vài IDE phổ biến như: MS Visual Studio, Visual Studio Code, Android Studio, XCode, Netbeans, Eclipse, IntelliJ IDEA, thậm chí là cả VIM, và Notepad.
  • Công cụ soản thảo Text. Có nhiều lựa chọn, nhưng ông giáo rất hay dùng và yêu thích phần mềm PSPad do một lập trình viên người Czech và cũng ở Brno nơi ông giáo từng học 5 năm ở đó. PSPad là một phần mềm miễn phí rất nhỏ gọn, hỗ trợ soạn thảo và hiển thị từ khóa của rất nhiều ngôn ngữ lập trình, có khả năng soạn thảo Unicode Text cũng như soạn thảo file nhị phân Hex Editor, ngoài ra nó cũng là FPT Client để có thể soạn thảo Web HTML và đẩy lên server ngay trong PSPad mà không phải dùng phần mềm khác để upload...
  • Công cụ phân tích và tạo tài liệu cho mã nguồn tự động Doxygen:  "Doxygen is the de facto standard tool for generating documentation from annotated C++ sources, but it also supports other popular programming languages such as C, Objective-C, C#, PHP, Java, Python, IDL (Corba, Microsoft, and UNO/OpenOffice flavors), Fortran, VHDL and to some extent D". Công cụ tạo các tài liệu về mã nguồn rất chuyên nghiệp, tổng hợp các lớp, các hàm có trong dự án. Khi xây dựng tài liệu cho dự án ông giáo cũng hay sử dụng công cụ này. Từ năm 2008, ông giáo đã là người đầu tiên và duy nhất cho đến bây giờ chuyển ngữ phần mô tả tiếng Việt cho Doxygen nhờ vậy chúng ta sẽ có tài liệu mô tả mã nguồn bằng tiếng Việt.
  • Công cụ bóc tách và vẽ sơ đồ thuật toán từ mã nguồn Visustin. Đây là côg cụ cũng khá mạnh để tự động vẽ sơ đồ thuật toán (flowchart) từ mã nguồn. Đối với các dự án cần xây dựng tài liệu một cách chuẩn chỉnh thì ngoài các biểu đồ Use case, biểu đồ UML thì cũng cần phải vẽ cả sơ đồ thuật toán nữa. Visutin là công cụ mạnh nhất tại thời điểm 2011 là lúc ông giáo mua bản quyền phần mềm này. Vì nó là công cụ cũng khá hữu hiệu nên giá khá đắt: 500 USD cho 1 user, riêng nâng cấp cũng mất đến 250 USD.
  • Công cụ ghi chép trong quá triình làm việc Evernote: Đây là công cụ để take notes mạnh nhất cho đến thời điểm này, cho phép viết note đầy đủ các định dạng HTML, có nhiều template rất chuyên nghiệp, có khả năng tổ chức dữ liệu và tìm kiếm nhiều chiều: theo thư mục, theo key word, theo hashtag... Evernote có bản miễn phí và bản có phí (500K/năm), có bản desktop và mobile.
  • Công cụ quản lý tài liệu tham khảo Mendeley: Đây là phần mềm miễn phí tốt nhất cho việc quản lý tài liệu tham khảo và quản lý tài liệu trích dẫn, có addin tích hợp vào MS Word để tham chiếu tài liệu tham khảo ngay trong Word, hoặc cũng có thể xuất ra LaTeX. Có bản Mobile và Desktop và free 2G dữ liệu trên cloud.
3. Phương pháp đọc mã nguồn của ông giáo

Đây là phần chia sẻ, đúc kết kinh nghiệm của ông giáo trong hàng chục năm lập trình và nghiên cứu mã nguồn, các bạn sẽ không tìm thấy trên mạng hay trong các giáo trình hay sách tham khảo đâu nhé. Tùy theo mục tiêu của người nghiên cứu, hoặc phải nắm toàn bộ project hay chỉ cần đúng phần nhỏ mình quan tâm. Nếu chỉ cần nghiên cứu một thành phần thì chúng ta sẽ chỉ tìm đúng phần quan tâm và bỏ qua tất cả các phần khác. Có thể tìm theo thư mục hoặc dùng PSPad hay IDE để tìm theo key word. Giống như khi chúng ta crack phần mềm được bảo vệ bằng số serial number, chúng ta chỉ reverse mã nguòn đến đúng đoạn kiểm tra số serial number để bypass hoặc tìm đoạn mã để tạo genkey tools mà bỏ qua tất cả các mã nguồn còn lại.

Đối với việc cần tìm hiểu một project hoàn chỉnh. thì có thể thực hiện theo các chiến lược và phương pháp như sau:
  • Hãy làm việc như một hacker, chỉ khác là hacker thường phải dùng công cụ reverse engineering để có mã nguồn thì chúng ta có đã có mã nguồn mở, bản đẹp (tên biến tên hàm rất tường minh chứ không bị mất như khi làm reverse). Khi đã hành xử như một hacker thì chúng ta cần thu thập mọi thông tin, mọi manh mối liên quan đến project giống như hacker thực hiện hành vi APT (Advanced Persistent Threat). Tìm hiểu tài liệu của dự án, đọc các commnet trong mã nguồn, tìm kiếm trên google với các key word phù hợp, trang bị các kiến thức nền tảng liên quan, tìm đọc các sách tham khảo...
  • Tìm hiểu cấu trúc thư mục của mã nguồn, các tác giả thường đặt tên thư mục mã nguồn đều có những ý nghĩa gợi nhớ khá rõ ràng và tên các thư mục đó cũng nói lên nhiều điều, hay bản thân tên của các file mã nguồn cũng đều có những ý nghĩa gợi ý rất tốt.
  • Dùng các công cụ tìm kiếm mạnh để tìm sự xuất hiện của các hàm (function, sub) được định nghĩa ở đâu được tham chiếu ở những đâu, Doxygen cũng có thể tạo ra các báo cáo này.
  • Khi bắt đầu hãy tìm hiểu các chương trình chính, các luồng chính. Tìm các hàm main(), hay các thread khởi động...
  • Có thể tìm một phiên bản cũ ngắn gọn để hiểu logic chung, ví dụ khi nghiên cứu mã nguồn của linux kernel nếu chúng ta nghiên cứu ngay vào phiên bản hiện tại 5.8.2 thì sẽ khá là choáng ngợp với 27.8 triệu dòng lệnh, hơn 800M mã nguồn, thay vì như vậy chúng ta tìm bản 1.0 chỉ có vài ngàn dòng lệnh với cỡ 50K mã nguồn mà thôi. Hiểu được kiến trúc ban đầu của Linux Kernel rồi, quay ngược lại tìm hiểu phiên bản hiện hành sẽ dễ dàng hơn nhiều.
  • Có thể cho phần mềm dự án chạy ở chế độ gõ rối (debug) để theo dõi chương trình hoạt động ở một điểm nào đó, rồi cho nó chạy từng dòng lệnh để theo dõi input và các ouput cũng như các giá trị biến trung gian.
  • Và điều quan trọng nhất để cuối cùng đây: luôn luôn phải ghi chép, vì khối lượng công việc lớn, và bộ nhớ của chúng ta có hạn, không thể nhớ được một lượng thông tin khổng lồ, do đó mỗi khi tìm hiểu được một vấn đề gì thì cần ghi chép ngay, hoặc thấy vấn đề nào hay hoặc vấn đề nào cần lưu ý thì cũng phải ghi lại. Cách tốt nhất để ghi (take notes) là dùng phần mềm Evernote. Riêng ghi chép ở Evernote thì cũng cần có các chiến lược sau:
    • Sử dụng lối ghi indent (thò thụt) giống như python để thể hiện các cấu trúc lồng nhau, thậm chí có thể sử dụng cấu trúc thò-thụt này để có thể ghi được bản dồ tư duy (mind map) dạng text có cấu trúc.
    • Ghi làm sao để có thể dễ dàng tra cứu, và khi cần tìm được lại đúng ví trí của mã nguồn gốc.
    • Sử dụng các định dạng: đậm, nghiêng, gạch chân, tô màu để làm nổi các ý cần nhấn mạnh.
    • Khi tìm hiểu 1 hàm, thì cố gắng ghi tất cả thông tin về hàm đó và cả những hàm con của nó cùng ở một chỗ để không phải switch đến chỗ khác và như vậy chúng ta sẽ có một bức tranh toàn cảnh của hàm đó, có thể xây dựng một quy ước riêng, một ngôn ngữ riêng của mình để tạo thuận tiện nhất khi tra cứu và làm rõ thông tin của hàm cần tìm hiểu.
4. Minh họa

Hình dưới đây là minh họa tìm hiểu toàn bộ phần xử lý block trong mã nguồn của Ethereum. Trong hình sẽ mô tả luồng khởi tạo, các hàm quan trọng được tổ đỏ, các hàm cần lưu ý tô đậm, và những đoạn text được đóng khung trong đoạn <<...>> là nguyên bản trong mã nguồn, khi cần copy đoạn text trong khung đó để search lại đoạn mã nguồn gốc.
  • Trong thread sẽ khởi tạo để quản lý các block, với hàm NewBlockchain thông qua lệnh <<go bc.update() >>
  • Phần dưới mô tả hàm  InsertChain và các công việc cũng như các hàm được họi trong nó, trong số đó có hàm  VerifyHeaders  cũng là một hàm quan trọng và được mô tả các công việc liên quan.
  • Với cách ghi chép như vậy chúng ta có thể hiểu khá rõ các thủ tục và các công việc cần làm khi thêm một blockchain (inserChain) một cách tường minh, đầy đủ nhưng cũng rất ngắn gọn...

BLOCK
  • Khởi tạo luồng cập nhật block: <<NewBlockChain>> gọi <<go bc.update() >> trong đó có gọi hàm << bc.procFutureBlocks()>> theo thời gian <<futureTimer.C>> là 5s, trong hàm này sẽ lấy ra danh sách các blocks và sẽ gắn vào chain qua hàm  <<bc.InsertChain(blocks[i : i+1])>>.
  • Hàm <<InsertChain>>:
    • Kiểm tra số của block thứ i phải lớn hơn block thứ i-1 giá trị 1.
    • Kiểm tra giá trị băm của block cha phải bằng giá trị băm hiện tại <<chain[i].ParentHash() != chain[i-1].Hash()>>.
    • Tăng giá trị <<bc.wg.Add(1)>>, waitGroup.
    • Kiểm tra các header của các block. <<bc.engine.VerifyHeaders(bc, headers, seals)>>.
      • Nếu tổng số headers==0, hoặc chế độ ModefullFake thì dừng <<if ethash.config.PowMode == ModeFullFake || len(headers) == 0 >>.
      • Nếu số lượng works lớn hơn số header thì điều chỉnh số workers bằng số header <<workers := runtime.GOMAXPROCS(0)>>.
      • chạy vòng for thực hiện hàm xác thực <<ethash.verifyHeaderWorker(chain, headers, seals, index)>>, hàm này kiểm tra giá trị cha, và kiểm tra tính liên tục của số (number) của block, sau đó gọi hàm <<verifyHeader>>:
        • Kiểm tra header.Extra phải nhỏ hơn  params.MaximumExtraDataSize.
        • Nếu có uncle thì header.Time phải nhỏ hơn 2^256, khi không có uncle thì header.Time < now() + allowedFutureBlockTime (15 s),  header.Time cũng phải nhỏ hơn parent.Time.
        • Kiểm tra độ khó dựa trên tem thời gian và độ khó của block cha (trước đó): <<expected := ethash.CalcDifficulty(chain, header.Time.Uint64(), parent)>>. giá trị tính được phải bằng header.Difficulty.
        • Kiểm tra phải có header.GasLimit < 2^63-1.
        • Kiểm tra phải có header.GasUsed < header.GasLimit.
        • Kiểm tra phải có:  |parent.GasLimit - header.GasLimit| < parent.GasLimit / params.GasLimitBoundDivisor (1024) và header.GasLimit > params.MinGasLimit (5000).
        • Kiểm tra phải có: header.Number = parent.Number + 1.
        • Kiểm tra PoW <<ethash.VerifySeal(chain, header)>> xem có lỗi không.
        • Kiểm tra <<misc.VerifyDAOHeaderExtraData(chain.Config(), header)>> xem có lỗi không.


5. Kết luận

Trên đây là chia sẻ những kinh nghiệp thực tế của ông giáo, có thể nó không phù hợp với một số người hoặc có thể có những người có các phương pháp hay hơn, nhưng đây là cách ông giáo đã tìm hiểu Linux Kernel (10 triệu dòng lệnh), Bitcoin (500k dòng lệnh) và Ethereum (600k dòng lệnh), và qua việc tìm hiểu mã nguồn và ghi chép như vậy mà ông giáo có thể hiểu khá sâu sắc về Bitcoin cũng như Ethereum, khi học viên hỏi bất cứ vấn đề gì liên quan ông giáo đều có thể trả lời chính xác và thực tế nó được triển khai trong mã nguồn như thế nào, và cũng từ đó mới phát hiện ra rằng ngay cuốn kinh điển và nổi tiếng là Mastering Bitcoin của Andreas M. Antonopoulos cũng có 2 lỗi sai cơ bản mà không ai phát hiện ra. Không có thông tin hay kiến thức gì chính xác và chi tiết cụ thể về một dự án bằng chính mã nguồn của dự án đó. Một người lập trình toàn bộ một phần mềm, hay kiến trúc sư của một dự án thành công, thì người lập trình đó có thể được coi là chuyên gia trong lĩnh vực đó mặc dù backround ban đầu của anh ta chỉ là một lập trình viên. Cũng bằng cách như vậy mà ông giáo có thể thao thao hàng tiếng đồng hồ về tên lửa, về pháo, về thiên văn, về virus về các Chip về phương pháp phần tử hữu hạn, và về cả skating cho dancesport.

6. Tài liệu tham khảo

[1] https://venturebeat.com/2019/05/07/android-passes-2-5-billion-monthly-active-devices/ 

[2] https://gs.statcounter.com/os-market-share/mobile/worldwide

[3] https://www.linux.com/news/linux-in-2020-27-8-million-lines-of-code-in-the-kernel-1-3-million-in-systemd/


7. Phụ lục

Một số hình ảnh về công cụ và tài liệu mô tả về Linux Kernel.






Share:

6 nhận xét:

  1. Quá tuyệt vời! Cảm ơn "Ông Giáo" rất nhiều ạ!

    ReplyDelete
    Replies
    1. 05 - Nên Đọc Mã Nguồn (Source Code) Như Thế Nào? ~ Tuanvietkey >>>>> Download Now

      >>>>> Download Full

      05 - Nên Đọc Mã Nguồn (Source Code) Như Thế Nào? ~ Tuanvietkey >>>>> Download LINK

      >>>>> Download Now

      05 - Nên Đọc Mã Nguồn (Source Code) Như Thế Nào? ~ Tuanvietkey >>>>> Download Full

      >>>>> Download LINK 4C

      Delete
  2. chân thành cảm ơn ông giáo!

    ReplyDelete
  3. Cảm ơn thầy, thầy thực sự là một người rất tâm huyết

    ReplyDelete
  4. 05 - Nên Đọc Mã Nguồn (Source Code) Như Thế Nào? ~ Tuanvietkey >>>>> Download Now

    >>>>> Download Full

    05 - Nên Đọc Mã Nguồn (Source Code) Như Thế Nào? ~ Tuanvietkey >>>>> Download LINK

    >>>>> Download Now

    05 - Nên Đọc Mã Nguồn (Source Code) Như Thế Nào? ~ Tuanvietkey >>>>> Download Full

    >>>>> Download LINK

    ReplyDelete

Recent Posts

Definition List