Mengurai GNU Coreutils (2018)
(maizure.org)- Proyek penjelasan jangka panjang yang membaca struktur source GNU coreutils 8.3 per utilitas, ditujukan untuk programmer pemula yang ingin belajar desain alat baris perintah
- Ini bukan dokumentasi penggunaan, melainkan materi pendamping untuk membaca source code, yang juga membahas namespace tiap perintah, alur eksekusi, keputusan desain, algoritme, serta silsilah UNIX dan Coreutils awal
- Karena coreutils berisi banyak program C kecil dengan satu tujuan, pola kode yang kasar dari sudut pandang modern seperti variabel global, makro,
goto, dan fungsi panjang perlu dibaca dalam konteks sejarahnya - Alur umumnya berlanjut dari inisialisasi, parsing opsi, pemrosesan input, eksekusi system call, hingga penanganan kegagalan, dengan
getopt_long(),fts, dan helper gnulib yang berulang kali muncul - Sebelum berkontribusi atau mengubah kode, perlu lebih dulu memastikan apakah hal itu bisa direproduksi dengan alat yang sudah ada, apakah akan merusak kompatibilitas mundur, dan seberapa jauh menyimpang dari POSIX
Cakupan proyek penjelasan GNU coreutils
- Ini adalah proyek jangka panjang yang menjelaskan semua utilitas di GNU coreutils 8.3
- Pembaca yang dituju adalah programmer pemula yang ingin mempelajari desain utilitas baris perintah
- Materi ini paling cocok dibaca sebagai penjelasan latar saat menelaah source code utilitas yang diminati
- Materi ini bukan manual pengguna; untuk penggunaan yang sebenarnya, lihat man pages
- Proyek ini terdiri dari beberapa tahap
- Phase 1: halaman namespace dan ringkasan eksekusi untuk tiap utilitas telah selesai
- Phase 2: keputusan desain utama dan algoritme, silsilah UNIX dan Coreutils awal, walkthrough source yang lebih berguna, serta visualisasi evolusi source code telah selesai
- Phase indefinite: walkthrough kode baris per baris untuk tiap utilitas masih menjadi pekerjaan jangka panjang
- Tersedia GitHub repo untuk mengumpulkan catatan per baris
Halaman penjelasan per utilitas
- Nama setiap perintah terhubung ke halaman detail yang menguraikan utilitas tersebut
- Setiap halaman mencakup pembahasan, source code, dan walkthrough
- Utilitas yang ditandai tebal adalah entri yang diperluas pada Phase 2
- Daftar ini mencakup perintah GNU coreutils seperti
arch,base64,cat,chmod,cp,date,dd,df,ls,rm,sort,tail,tr,wc,yes, dan lainnya
Hal-hal yang perlu diketahui sebelum membaca kode
- Banyak utilitas coreutils memiliki sejarah hampir 30 tahun, dan merupakan kode yang telah dimodifikasi banyak orang selama waktu yang panjang
- Sebagian besar adalah program kecil yang dirancang untuk satu tujuan
- Umumnya berupa program dengan satu file source
- Bukan kode yang sejak awal dirancang untuk bertahan lama atau berkembang jauh melampaui peran awalnya
- Terlihat pola seperti variabel global, makro,
goto, dan fungsi panjang denganswitch/loop bertingkat
- Pengetahuan POSIX penting untuk memahami kode
- Sebaiknya mulai dari Utility Syntax Guidelines
- POSIX mendefinisikan interoperabilitas input dan output, tetapi cara kerja aktual diserahkan pada implementasi
- GNU coreutils mungkin tidak mengikuti POSIX secara ketat, tetapi konsep seperti bit izin, uid/gid, variabel lingkungan, dan status keluar sangat tertanam di dalamnya
- Karena masalah portabilitas, coreutils sangat bergantung pada gnulib
- Hampir semua utilitas menyertakan fungsi gnulib yang dirancang untuk menangani masalah umum di berbagai sistem
- coreutils berjalan dengan asumsi adanya dukungan shell seperti
bash,zsh, danksh- Shell menjalankan utilitas lewat fork/clone, meneruskan argumen, menyiapkan environment, mengalihkan I/O dengan pipe, dan mempertahankan nilai keluar
- GNU coreutils awalnya berasal dari tiga paket: utilitas shell, teks, dan file, sehingga utilitas sejenis banyak berbagi pola desain
Pola desain dasar
- Sebagian besar utilitas CLI pada umumnya memiliki tiga tahap
- Tahap konfigurasi untuk menyiapkan flag, opsi, lokalisasi, dan sebagainya
- Tahap parsing argumen untuk membaca input dan menentukan parameter eksekusi
- Tahap pemrosesan/eksekusi untuk menyiapkan input dan meneruskannya ke satu atau lebih system call
- Selama eksekusi, pembatasan dapat diperiksa dan kegagalan bisa terjadi di berbagai titik
- Status keluar yang berbeda dapat memberi petunjuk tentang lokasi masalah
EXIT_FAILUREsering dipakai sebagai status kegagalan umum
- Setelah gagal, utilitas memberi umpan balik kepada pengguna
- Variasi utilitas terbagi menjadi tiga kelompok
- Trivial utilities: mendefinisikan beberapa baris makro lalu meng-include source utilitas lain, dan memaksa kontrol alur tertentu lewat makro. Contohnya
arch,dir,vdir - Wrapper utilities: setelah konfigurasi dan parsing opsi, opsi baris perintah diteruskan langsung sebagai argumen system call. Hasil system call menjadi hasil utilitas, dengan pemrosesan internal yang minim. Contohnya
link,whoami,hostid,logname - Full utilities: memiliki konfigurasi, parsing opsi/argumen, pemrosesan data input, dan eksekusi beberapa system call; mayoritas utilitas termasuk kategori ini
- Trivial utilities: mendefinisikan beberapa baris makro lalu meng-include source utilitas lain, dan memaksa kontrol alur tertentu lewat makro. Contohnya
Inisialisasi umum dan parsing opsi
- Semua utilitas memiliki prosedur inisialisasi singkat di awal
main()initialize_main (&argc, &argv);set_program_name (argv[0]);setlocale (LC_ALL, "");bindtextdomain (PACKAGE, LOCALEDIR);textdomain (PACKAGE);atexit (close_stdout);
- Kode pra-pemrosesan ini menangani pekerjaan administratif seperti internasionalisasi dan pendaftaran perilaku saat program berakhir, tanpa memengaruhi perilaku inti masing-masing utilitas
- Keluarga alat Getopt berperan penting dalam parsing opsi baris perintah
- Opsi pendek memakai prefiks
-, opsi panjang memakai prefiks-- - Opsi pendek didefinisikan sebagai string, sementara opsi panjang menggunakan struct
- Dalam string opsi pendek, karakter tanpa argumen ditulis begitu saja, argumen wajib diberi
:, dan argumen opsional diberi:: - String opsi pendek
Lln:s:tpadakillberartiL,l, danttidak memiliki argumen, sedangkanndansmemerlukan argumen getopt_long()digunakan di semua utilitas untuk mengembalikan opsi berikutnyaoptindmenunjuk posisi argumen berikutnya dalam arrayargv[]optargadalah pointer karakter yang menunjuk nilai argumen opsi
- Opsi pendek memakai prefiks
Penelusuran file system dan helper system call
- Pada sistem keluarga Unix, pustaka fts sering didukung untuk memudahkan penelusuran file system
fts_open()atauxfts_open()membuat strukturFTSdari sebuah path- Node file atau direktori pada pohon direpresentasikan oleh struktur
FTSENT - Memanggil
fts_read()akan menghasilkanFTSENT, dan proses ini adalah penelusuran pohon - Field
FTSENT->fts_infosering dipakai untuk menjelaskan entri dan menentukan cara memprosesnya
- coreutils menggunakan banyak wrapper system call dan helper selain yang disediakan libc
- Dalam pemrosesan tulis, selain wrapper system call
write()dan fungsi libc sepertifwrite(), ada pula fungsi nonstandar sepertifull_write()- full_write() terus mencoba menulis ulang kecuali terjadi kegagalan keras
- safe_write() mencoba ulang system call
write()meski terjadi interupsi - Ada juga helper tulis yang hanya digunakan di satu utilitas, seperti
iwrite()padadddancwrite()padasplit
Fungsi umum dan idiom kode
- Semua utilitas setidaknya menggunakan tiga fungsi:
main(),usage(), dan_() usage()menampilkan bantuan yang mencakup parameter input, arti, dan sintaks yang benar_()adalah makro yang didefinisikan disystem.h, yang menghubungkan string biasa ke fitur Native Language Support dari GNU gettext.h- Jika sebuah string ditampilkan kepada pengguna, biasanya string itu dibungkus dengan makro ini
- Sebagian besar utilitas yang tidak sepele mengulang baris kode umum
#include "system.h"mendefinisikan makro bergantung sistem, variabel, dan fungsi utilitas nonstandarPROGRAM_NAMEmendefinisikan nama resmi utilitas dan dipakai untuk pemeriksaan versiAUTHORSmendefinisikan penulis utilitas dan dipakai untuk pemeriksaan versiemit_try_help()menampilkan saran bantuan setelah output kegagalanemit_ancillary_info (PROGRAM_NAME)menampilkan informasi bantuan tambahan umum setelah output perintahinitialize_main(&argc, &argv)menangani ekspansi wildcard bawaan di VMS, dan dihapus pada sebagian besar sistem operasi lainatexit(close_stdout)mendaftarkan fungsi yang akan melakukan flush dan menutup stream buffer saat program berakhir
- Ada juga idiom C yang mungkin terasa asing bagi pemula
!!adalah operasi unary NOT ganda untuk memaksa nilai menjadi booleando { ... } while (0)adalah pola non-loop untuk membungkus makro multi-pernyataan agar tokenisasi tetap aman setelah substitusi preprocessor
Cara pemeliharaan dan pemeriksaan sebelum berkontribusi
- Proyek aktif seperti coreutils umumnya berevolusi melalui tiga alur pemeliharaan
- Perubahan seluruh proyek adalah perubahan besar yang memengaruhi struktur dan dependensi semua utilitas
- Pada 1995, Native Language Support ditambahkan melalui GNU gettext project, dan makro
_()diperkenalkan di sebagian besar baris output teks - Pada 1996, dukungan internasionalisasi diperluas dan beberapa kode inisialisasi ditambahkan ke
main() - Pada 1995, deskripsi singkat tujuan utilitas ditambahkan ke output penggunaan
- Pada 2003, dukungan wildcard VMS ditambahkan, yang terlihat pada fungsi
initialize_main() - Pada 2016, untuk menghindari peringatan compiler di jalur kegagalan, makro
die()menggantikan sebagian besar fungsiexit()danerror()
- Pada 1995, Native Language Support ditambahkan melalui GNU gettext project, dan makro
- Perubahan per utilitas dibagi menjadi perbaikan bug, fitur baru, dan optimisasi
join,sort, danuniqrentan terhadap serangan overflow hingga patch pada 2016dfmendapat opsi--outputyang ditambahkan pada 2013yesmengalami peningkatan performa lewat buffering yang lebih baik
- Pemeliharaan tahunan setidaknya mencakup pembaruan tahun hak cipta semua utilitas
- Ada juga perubahan administratif seperti pembaruan alamat FSF, yang tidak memengaruhi eksekusi
- Kontributor sebaiknya lebih dulu memeriksa GNU project page, contribution guidelines, rejected features, dan mailing list archives
- Ada tiga pertanyaan yang perlu diperiksa sebelum menulis kode
- Apakah fungsi yang sama bisa direproduksi dengan alat yang sudah ada
- Apakah kontribusi tersebut merusak kompatibilitas mundur
- Apakah perilaku yang diusulkan menyimpang jauh dari POSIX
- Jika belum yakin, prosedur yang disarankan adalah bertanya kepada komunitas di mailing list
Detail menarik
- Utilitas terpendek adalah
false, hanya 2 baris, setara denganarch,dir, danvdir - Utilitas mandiri terpendek adalah
truedengan 80 baris, dan versi pertamanya nyaris seperti program C minimal - Utilitas terpanjang adalah
lsdengan 5308 baris - Banyak utilitas dapat ditelusuri kembali hingga Research UNIX pada 1970-an, dan sebagian bahkan sampai ke Multics
- Leluhur konseptual tertua adalah perintah CTSS
LISTFsekitar tahun 1963, yang kemudian dipendekkan menjadilsLISTFadalah salah satu dari 18 utilitas asli dalam makalah desain 1962
- Sintaks unik
ddmengingatkan pada OS/360 job control language dari awal 1960-an sortadalah satu-satunya utilitas yang memanfaatkan multithreadingfmtmenunjukkan optimisasi baris dan paragraf dengan memakai biaya fungsiyesmenerapkan output berkinerja tinggi dengan buffer memori yang disejajarkan ke halamandflebih cepat karena memakai metadata perangkat, sedangkanduharus memeriksa semua filecksummemiliki dua titik masuk: eksekusi normal dan pembuatan tabel CRC-32echotidak memiliki kondisi kegagalan- Desain
testdanexprsangat berbeda dari struktur utilitas pada umumnya suawalnya dipelihara di coreutils/shellutils
Implementasi yang layak dilihat
- shuf dan shred menggunakan ISAAC cipher) untuk bilangan acak
- shuf menggunakan Reservoir sampling
- sum menggunakan checksum lawas System V dan BSD
- expr menggunakan evaluasi ekspresi asosiatif kiri
- shred menggunakan Secure overwrite
- cksum menghitung CRC-32 checksum
- sort menggunakan modifikasi Merge sort
- factor adalah utilitas yang layak dilihat untuk unsur implementasi matematis
Informasi dukungan
- Tidak ada pengaturan untuk menerima dukungan pribadi
- Jika ingin menyumbangkan waktu atau uang, Anda dapat berkontribusi ke Free Software Foundation
1 komentar
Opini Hacker News
Jika dikatakan bahwa “banyak dari utilitas ini sudah berusia hampir 30 tahun, dan selama itu telah dimodifikasi oleh banyak orang”, tetapi juga “tidak dirancang untuk diperluas melampaui umur panjang atau perannya”, saya ingin melihat contoh program yang dirancang dengan umur panjang dalam pikiran menurut penulis
Saya penasaran, baik itu program yang sudah bertahan 30 tahun, maupun program yang menurutnya akan bertahan 30 tahun ke depan
Saya membuat framework pengujian dengan bash dan coreutils untuk menguji bahasa pemrograman kecil buatan saya sendiri. Awalnya saya merasa kurang nyaman karena tidak memakai bahasa yang “semestinya”, tetapi pada praktiknya berjalan sangat baik dan juga bisa dieksekusi paralel
Satu-satunya hal yang tidak bisa saya uji adalah
argv[0]program, dan setelah mencoba berbagai kombinasi tetap tidak bisa mendapatkan perilaku yang persis saya inginkan, jadi saya mengirim permintaan fitur dan patch untukenvke coreutils: https://lists.gnu.org/archive/html/coreutils/2023-08/msg0006...Sepertinya akan diterima, yang berarti fitur baru masuk ke program lama
Praktik-praktik yang disebutkan penulis biasanya dikritik dalam program besar karena semakin besar skalanya, semakin merusak maintainability
Materi yang layak dijadikan rujukan:
Cara pengujian GNU coreutils: https://www.pixelbeat.org/docs/coreutils-testing.html
Eksplorasi tiap perintah coreutils: https://ratfactor.com/slackware/pkgblog/coreutils
Pemrosesan teks command-line dengan GNU Coreutils: https://learnbyexample.github.io/cli_text_processing_coreuti... — ebook saya yang membahas lebih dari 20 alat pemrosesan teks
Artikel terkait:
Decoded: GNU Coreutils (2018) - https://news.ycombinator.com/item?id=29871037 - Januari 2022, 7 komentar
Decoded: GNU coreutils (2019) - https://news.ycombinator.com/item?id=26411908 - Maret 2021, 38 komentar
Decoded: GNU Coreutils - https://news.ycombinator.com/item?id=20328650 - Juli 2019, 55 komentar
Fakta menarik: jika Anda memasang coreutils lewat Homebrew di macOS, karena macOS sudah memiliki
od(1),odmilik coreutils dipasang sebagaigod(1)Jika penulis melihat ini, setidaknya ada satu kesalahan. Deskripsi singkat di halaman shred[0] sebenarnya adalah deskripsi
csplit[1], dan seharusnya kira-kira menjadi “menimpa isi file untuk menyembunyikannya dan secara opsional menghapusnya”[0]: https://maizure.org/projects/decoded-gnu-coreutils/shred.htm...
[1]: https://maizure.org/projects/decoded-gnu-coreutils/csplit.ht...
Saya tidak tahu ada yang seperti ini, keren. Bahkan sesuatu yang sederhana seperti
yespun cukup menarik untuk melihat bagaimana kode dasar sebuah utilitas yang menulis ke standard output dibuathttps://maizure.org/projects/decoded-gnu-coreutils/yes.html
Misalnya, bagus bahwa ada contoh minimal untuk mempelajari cara menulis output dengan sangat cepat. Karena programnya sangat pendek, jumlah baris tidak terlalu penting, dan jika penulisnya tahu caranya, menurut saya lebih baik membuatnya secepat mungkin agar tidak menjadi hambatan
Cukup menarik sebagai daftar utilitas dasar. Saya sudah sangat lama memakai UNIX, tapi belum pernah mendengar yang seperti
shred,shuf, ataufactorJadi ingin mencoba menjalankan
lalu melihat berapa lama ia bertahan sebelum membunuh dirinya sendiri. Tentu saja di mesin virtual atau perangkat yang mudah di-flash ulang
dd, dan ternyata selesai sampai akhir, cukup mengecewakanSaya berharap terjadi crash atau setidaknya koneksi terputus, tetapi kernel serta
sshddanbashmasih berada di memori, jadi saya kembali ke prompt yang sudah tidak bisa melakukan apa-apa dengan benarETXTBUSY), jadishredtidak bisa merusak dirinya sendiri ataufindSaya suka bahwa
/bin/trueternyata bisa gagal dan mengembalikan false. Secara teknis, pemanggilan “Not/bin/false” jadi lebih kokoh: https://github.com/coreutils/coreutils/blob/master/src/true....Tentu saya tahu ini kasus yang hampir tidak pernah terjadi, tapi tetap saja lucu
trueadalah ia menjadi jauh lebih rumit daripada yang diperlukanLatihan dulu:
truesecara harfiah bermula seperti ini. Cukup seperti koan ZenPerubahan pertama terjadi karena urusan hukum. Semua kode harus memiliki pemberitahuan hak cipta, dan itu juga diperlukan untuk file kosong. Jadi file tanpa isi berubah menjadi file yang hanya berisi pemberitahuan hak cipta, sehingga muncul pertanyaan, “bisakah seseorang mengklaim hak cipta atas ketiadaan?” AT&T mencobanya
Lalu seseorang mengatakan program harus terdefinisi dengan baik dan tidak boleh bergantung pada perilaku kebetulan Unix, yang pada saat itu cukup masuk akal. Maka
trueakhirnya memiliki kode, yaituexit 0Lalu ada lagi yang mengatakan utilitas sistem sebaiknya ditulis dalam C, bukan shell, agar lebih cepat, dan OpenBSD masih punya contoh bagus dari bentuk itu: http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/usr....
Pada suatu titik birokrasi GNU ikut campur dan mengatakan semua program harus mendukung flag
-h, lalu setelah itu semua program harus mendukung locale, sehingga GNUtruesekarang, yang mengejutkan, menjadi 80 baris: https://github.com/coreutils/coreutils/blob/master/src/true....Bisa saja dianggap baik-baik saja, tetapi untuk program yang menurut definisi “tidak melakukan apa-apa dan berhasil”, itu banyak sekali kode
http://trillian.mit.edu/~jc/humor/ATT_Copyright_true.html
truesaja sepertinya tidak terpengaruh, kecuali komentarnya yang membingungkanKalau seorang programmer pemula ingin mengembangkan intuisi tentang cara menerapkan struktur data dan algoritma secara berguna, adakah hal tertentu di sini yang sebaiknya diperhatikan?
Misalnya memakai
copy_file_range()alih-alihread()/write()agar data tidak perlu disalin ke ruang pengguna. Ini lebih dekat ke rekayasa perangkat lunak daripada ilmu komputerMungkin saya melewatkan inti situs ini, tetapi bukankah masing-masing sudah punya halaman man atau halaman info?