1 điểm bởi GN⁺ 1 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • BusyBox là một multicall binary cung cấp nhiều lệnh trong một tệp thực thi duy nhất; ngay cả wget mặc định của Alpine cũng được chạy thông qua BusyBox
  • Trong container Alpine, /usr/bin/wget không phải binary thực sự mà là một liên kết tượng trưng trỏ tới /bin/busybox
  • Khi chạy, BusyBox đọc tên được gọi từ argv[0] và dùng tên cuối cùng của đường dẫn để quyết định applet nào sẽ được thực thi
  • Mỗi applet được tìm theo tên rồi đi vào hàm main tương ứng; wget được triển khai trong wget.c và cuối cùng chạy wget_main
  • Có thể kiểm tra các lệnh đã được biên dịch bằng busybox --list; trong ví dụ Alpine, có 304 lệnh được hiển thị và mỗi tiện ích trông như một phiên bản rút gọn

Cách BusyBox hoạt động

  • BusyBox sử dụng cấu trúc multicall binary, cung cấp nhiều lệnh trong một tệp thực thi duy nhất
  • Trong container Alpine, /usr/bin/wget không phải là tệp thực thi thật mà là một liên kết tượng trưng trỏ tới /bin/busybox
    docker run --rm -it alpine sh
    / # which wget
    /usr/bin/wget
    / # ls -lah /usr/bin/wget
    lrwxrwxrwx    1 root     root          12 Apr 15 04:51 /usr/bin/wget -> /bin/busybox
    
  • Trong /usr/bin, có hơn 130 tệp thực thi trông như được tạo ra từ một binary duy nhất, điều này gắn liền với cấu trúc multicall binary của BusyBox
  • Khi chạy, BusyBox lấy tên được gọi từ argv[0], sau đó chỉ lấy phần tên cuối của đường dẫn để quyết định sẽ chạy applet nào
    applet_name = argv[0];
    if (applet_name[0] == '-')
      applet_name++;
    applet_name = bb_basename(applet_name);
    
  • Nó cũng hoạt động nếu truyền rõ tên applet như busybox ls -1; nếu đưa vào tên không tồn tại thì sẽ in ra applet not found
    / # busybox ls -1
    bin
    dev
    etc
    home
    
    / # busybox meheh
    meheh: applet not found
    

Cấu trúc applet và cách cài đặt

  • BusyBox tìm applet theo tên rồi gọi hàm main của applet đó
    int applet = find_applet_by_name(name);
    // ...
    run_applet_no_and_exit(applet, name, argv);
    // ...
    xfunc_error_retval = applet_main[applet_no](argc, argv);
    
  • Mỗi applet có một tệp C riêng; wget được triển khai trong wget.c
  • Thiết lập cho từng applet được định nghĩa dưới dạng chú thích trong mã nguồn; cấu hình WGET bao gồm wget (41 kb), được bật mặc định, phần trợ giúp mô tả đây là tiện ích tải tệp không tương tác từ máy chủ HTTP và FTP, cùng với mục tiêu build là wget.o
    //config:config WGET
    //config:	bool "wget (41 kb)"
    //config:	default y
    //config:	help
    //config:	wget is a utility for non-interactive download of files from HTTP
    //config:	and FTP servers.
    //applet:IF_WGET(APPLET(wget, BB_DIR_USR_BIN, BB_SUID_DROP))
    //kbuild:lib-$(CONFIG_WGET) += wget.o
    
  • Cuối cùng, applet wget đi vào wget_main
    int wget_main(int argc UNUSED_PARAM, char **argv)
    
  • BusyBox cũng hỗ trợ hard link; trong busybox --install -s, -s có nghĩa là tạo liên kết tượng trưng
    busybox --install -s
    
  • Có thể xem danh sách các lệnh được đưa vào bản biên dịch bằng busybox --list; trong môi trường Alpine ví dụ, có 304 lệnh
    / # busybox --list | wc -l
    304
    
  • Nhiều lệnh trong Alpine hoạt động như giao diện tới binary dựa trên BusyBox, và mỗi tiện ích trông như một phiên bản rút gọn so với tiện ích gốc đầy đủ

1 bình luận

 
Ý kiến trên Lobste.rs
  • Câu trả lời cho câu hỏi được trích dẫn gần với tái triển khai hơn
    Giới thiệu về BusyBox có tại https://busybox.net/about.html, và mã nguồn ở https://github.com/vda-linux/busybox_mirror

  • Việc một chương trình giả vờ mang tên khác với thực tế khá gây khó chịu
    Trường hợp tệ nhất là trên macOS chạy gcc nhưng thực ra lại ra clang, và PowerShell cũng hoạt động tương tự. Giá mà cứ dùng tên khác thì tốt hơn

    • Cũng có nguyên nhân lịch sử: nhiều nhà phát triển không biết đến clang, không có máy Mac để thử nghiệm, hoặc không có lựa chọn thay thế thực tế, nên vấn đề này mới xuất hiện
      Nixpkgs phải áp dụng rất nhiều bản vá như https://github.com/NixOS/nixpkgs/…, và ngay cả dự án nổi tiếng như sqlite cũng không ngoại lệ. Trong khi đó, macOS về cơ bản đã chọn con đường đánh lừa bằng đường dẫn
    • Có những người viết makefile cho nhiều phần mềm nhưng lại không biết cc
    • Có thể hiểu sự bực bội đó, nhưng nếu nó đủ tương thích để chạy được phần lớn script build thì có lẽ nó đã phổ biến vì cho phép tái sử dụng phần nào cùng một script trên Linux và nhiều hệ Unix khác nhau
    • Đôi khi điều đó thực tế là bất khả thi
      Sẽ có rất nhiều makefile đã hardcode gcc, và ở ngữ cảnh khác cũng tương tự. Ví dụ, vì nhiều chương trình hiện có kiểm tra xterm-* của biến môi trường TERM thay vì cơ sở dữ liệu terminfo vốn là cách đúng, nên cách chọn tên khác sẽ không hiệu quả
  • Khi chẩn đoán các vấn đề kỳ lạ trong container, tôi thường dùng podman cp /usr/bin/busybox-static somecontainer:/bin

  • toybox cũng có cấu trúc tương tự
    Một số công cụ có vẻ được mang từ nơi khác về hoặc được port sang, nhưng khá nhiều cái đã được triển khai mới cho BusyBox, với mục tiêu giữ cho nó nhỏ gọn và chỉ dùng các tính năng libc hạn chế để khi liên kết tĩnh vẫn biên dịch ra kích thước nhỏ. Một ưu điểm nữa là trong shell script có thể dùng những công cụ này như lệnh tích hợp. Một số được chạy bằng fork+jump thay vì fork+exec, và một vài cái thậm chí có thể chạy như lời gọi hàm thông thường mà không cần fork

    • Theo tôi biết thì toybox ban đầu được tạo ra như một phương án thay thế giấy phép BSD cho BusyBox vốn dùng GPL
      Nói thêm, theo Toybox on Wikipedia, “Toybox được Rob Landley bắt đầu vào đầu năm 2006 sau khi ông từ bỏ vai trò bảo trì BusyBox do tranh chấp với Bruce Perens, người tạo ra BusyBox ban đầu”
  • Trên thực tế đây là một bản tái triển khai. Tuy vậy, nếu giấy phép cho phép thì cũng không có gì đáng ngạc nhiên nếu một phần mã được mượn từ triển khai lớn ban đầu
    Theo 'pedia:, BusyBox được Bruce Perens viết lần đầu vào năm 1995, và đến năm 1996 đã được tuyên bố là hoàn thành cho mục đích sử dụng ban đầu. Mục tiêu ban đầu là nhét một hệ thống có thể khởi động hoàn chỉnh, đóng vai trò đĩa cứu hộ và trình cài đặt cho bản phân phối Debian, vào đúng một đĩa mềm. Sau đó nó mở rộng thành bộ công cụ user space cốt lõi tiêu chuẩn trên thực tế cho các thiết bị Linux nhúng và trình cài đặt bản phân phối Linux, và vì mỗi tệp thực thi Linux đều cần thêm vài KB overhead nên việc gộp hơn 200 chương trình thành một sẽ tiết kiệm đáng kể dung lượng đĩa và bộ nhớ
    Liên quan đến đó, Toybox cũng có cấu trúc và triết lý tương tự nhưng dùng giấy phép BSD. Tôi nhớ rằng nhà phát triển chính Rob Landley từng cho rằng nếu có một giấy phép dễ được chấp nhận hơn về mặt thương mại thì nó có thể được đưa vào thiết bị Android, và khi đó mọi điện thoại và máy tính bảng Android đều có tiềm năng trở thành môi trường phát triển kiểu Unix. Có vẻ Toybox hiện vẫn là một phần của Android, nhưng nếu không có các công cụ hỗ trợ ở tầng trên như Termux thì Android vẫn không dễ dùng theo kiểu Unix

    • Khó có ví dụ nào hoàn hảo hơn cho thấy việc làm lao động không công cho các công ty như Google có thể quay lại gây hại cho chính mình
      Càng như vậy hơn khi xét đến việc Google trong nhiều năm đã đe dọa sẽ chặn Termux
  • Cũng có bản port cho Windows: https://github.com/rmyorston/busybox-w32
    Kích thước nhỏ của BusyBox khiến những bản port như vậy trở nên thực tế hơn. Tuy nhiên giờ đây có thể просто chạy Linux bên trong Windows nên có vẻ mức độ liên quan của nó đã giảm bớt