GNU Coreutils’i Çözümleme (2018)
(maizure.org)- GNU coreutils 8.3 kaynak yapısını yardımcı program bazında baştan sona okuyan uzun soluklu bir açıklama projesi; komut satırı aracı tasarımını öğrenen acemi programcılara yönelik
- Bir kullanım belgesi değil, kaynak kod okuma yardımcı materyali; her komutun namespace’ini, yürütme akışını, tasarım kararlarını, algoritmalarını, UNIX ve erken Coreutils soy ağacını birlikte ele alıyor
- coreutils küçük ve tek amaçlı çok sayıda C programından oluştuğu için, global değişkenler, makrolar,
goto, uzun fonksiyonlar gibi modern bakışla kaba görünen kod kalıplarını da tarihsel bağlam içinde okumak gerekiyor - Ortak akış; başlatma, seçenek ayrıştırma, girdi işleme, sistem çağrısı yürütme ve hata işleme şeklinde ilerliyor;
getopt_long(),ftsve gnulib yardımcıları tekrar tekrar karşımıza çıkıyor - Katkı veya düzeltme yapmadan önce, mevcut araçlarla yeniden üretilebilir mi, geriye dönük uyumluluğu bozuyor mu, POSIX’ten ne kadar sapıyor diye önce kontrol etmek gerekiyor
GNU coreutils açıklama projesinin kapsamı
- GNU coreutils 8.3 içindeki tüm yardımcı programları açıklayan uzun soluklu bir proje
- Hedef okur kitlesi, komut satırı yardımcı programlarının tasarımını inceleyen acemi programcılar
- İlgilendiğiniz yardımcı programın source code dosyalarını okurken arka plan açıklaması olarak birlikte kullanmaya en uygun kaynak
- Bu materyal kullanıcı kılavuzu değildir; gerçek kullanım için man pages başvurulmalıdır
- Proje birkaç aşamadan oluşuyor
- Phase 1: Her yardımcı program için namespace ve yürütme özeti sayfalarının tamamlanması
- Phase 2: Başlıca tasarım kararları ve algoritmalar, UNIX ve erken Coreutils soy ağacı, daha yararlı kaynak kod walkthrough’ları, kaynak kod evrimi görselleştirmelerinin tamamlanması
- Phase indefinite: Her yardımcı programın satır satır kod walkthrough’u uzun vadeli hedef olarak kalıyor
- Satır satır notların toplanması için bir GitHub repo sunuluyor
Yardımcı program bazında açıklama sayfaları
- Her komut adı, ilgili yardımcı programın çözümlendiği ayrıntılı sayfaya bağlanıyor
- Her sayfada tartışma, kaynak kod ve walkthrough yer alıyor
- Kalın gösterilen yardımcı programlar Phase 2’de genişletilmiş öğeler
- Listede
arch,base64,cat,chmod,cp,date,dd,df,ls,rm,sort,tail,tr,wc,yesgibi GNU coreutils komutları yer alıyor
Kodu okumadan önce bilinmesi gereken varsayımlar
- coreutils yardımcı programlarının çoğu neredeyse 30 yıla yaklaşan bir geçmişe sahip; uzun süre boyunca birçok kişi tarafından değiştirilmiş kodlar
- Çoğu küçük ve tek bir amaca odaklanmış küçük programlar
- Genellikle tek kaynak dosyalı programlar
- Uzun süre yaşayacak ya da rolünün ötesinde genişleyecek şekilde tasarlanmış kodlar değiller
- Global değişkenler, makrolar,
goto, iç içeswitch/döngüler içeren uzun fonksiyonlar gibi kalıplar görülüyor
- POSIX bilgisi kodu anlamak için önemli
- Utility Syntax Guidelines ile başlamak iyi olur
- POSIX girdi ve çıktının birlikte çalışabilirliğini tanımlar, ancak gerçek çalışma biçimini uygulamaya bırakır
- GNU coreutils POSIX’i katı biçimde izlemeyebilir; ancak izin bitleri, uid/gid, ortam değişkenleri, çıkış durumu gibi kavramlar derinlemesine yerleşmiştir
- Taşınabilirlik sorunları nedeniyle coreutils büyük ölçüde gnulib üzerine dayanır
- Neredeyse tüm yardımcı programlar, farklı sistemlerde ortak sorunları ele almak için tasarlanmış gnulib fonksiyonlarını içerir
- coreutils,
bash,zsh,kshgibi shell desteği varsayımıyla çalışır- Shell; fork/clone ile yardımcı programı çalıştırır, argümanları iletir, ortamı ayarlar, pipe ile girdi/çıktıyı yönlendirir ve çıkış değerini korur
- GNU coreutils başlangıçta shell, metin ve dosya yardımcı programları olarak üç paketten doğdu; aynı türden yardımcı programlar birçok tasarım kalıbını paylaşır
Temel tasarım kalıpları
- Çoğu CLI yardımcı programında ortak olarak üç aşama bulunur
- Bayrakları, seçenekleri, yerelleştirmeyi vb. hazırlayan kurulum aşaması
- Girdiyi okuyup yürütme parametrelerini belirleyen argüman ayrıştırma aşaması
- Girdiyi hazırlayıp bir veya daha fazla sistem çağrısına aktaran işleme·yürütme aşaması
- Yürütme sırasında çeşitli noktalarda kısıtlar kontrol edilir ve işlem başarısız olabilir
- Farklı çıkış durumları, sorunun yerini ima edebilir
EXIT_FAILUREgenel başarısızlık durumu olarak sık kullanılır
- Başarısızlıktan sonra kullanıcıya geri bildirim sağlanır
- Yardımcı program varyasyonları üç gruba ayrılır
- Trivial utilities: Birkaç satır makro tanımladıktan sonra başka bir yardımcı programın kaynağını include eder ve makrolarla belirli akış kontrolünü zorlar. Örnekler:
arch,dir,vdir - Wrapper utilities: Kurulum ve seçenek ayrıştırmadan sonra komut satırı seçeneklerini doğrudan sistem çağrısı argümanlarına aktarır. Sistem çağrısının sonucu yardımcı programın sonucu olur; kendi işleme mantığı azdır. Örnekler:
link,whoami,hostid,logname - Full utilities: Kurulum, seçenek·argüman ayrıştırma, girdi verisi işleme ve birden çok sistem çağrısı yürütmenin tamamını içerir; yardımcı programların çoğu bu gruptadır
- Trivial utilities: Birkaç satır makro tanımladıktan sonra başka bir yardımcı programın kaynağını include eder ve makrolarla belirli akış kontrolünü zorlar. Örnekler:
Ortak başlatma ve seçenek ayrıştırma
- Tüm yardımcı programlarda
main()başında kısa bir başlatma prosedürü bulunurinitialize_main (&argc, &argv);set_program_name (argv[0]);setlocale (LC_ALL, "");bindtextdomain (PACKAGE, LOCALEDIR);textdomain (PACKAGE);atexit (close_stdout);
- Bu ön işleme kodu, uluslararasılaştırma ve çıkışta davranış kaydı gibi yönetim işlerini ele alır; tek tek yardımcı programların temel davranışını etkilemez
- Komut satırı seçeneklerini ayrıştırmada Getopt araç ailesi merkezi rol oynar
- Kısa seçenekler
-, uzun seçenekler--önekini kullanır - Kısa seçenekler string olarak tanımlanır, uzun seçenekler struct kullanır
- Kısa seçenek string’inde argüman yoksa yalnızca karakter yazılır; zorunlu argüman için
:, isteğe bağlı argüman için::eklenir killkomutunun kısa seçenek string’iLln:s:t,L,l,tiçin argüman olmadığını;n,siçin argüman gerektiğini ifade eder- Tüm yardımcı programlarda
getopt_long()sıradaki seçeneği döndürmek için kullanılır optind,argv[]dizisinde sıradaki argümanın konumunu gösteriroptarg, seçenek argümanı değerini gösteren karakter işaretçisidir
- Kısa seçenekler
Dosya sistemi dolaşımı ve sistem çağrısı yardımcıları
- Unix benzeri sistemlerde dosya sistemi dolaşımını kolay ele almak için çoğu zaman fts kütüphanesi desteklenir
fts_open()veyaxfts_open()ile bir yoldanFTSyapısı oluşturulur- Ağaçtaki dosya veya dizin düğümü
FTSENTyapısı ile temsil edilir fts_read()çağrıldığındaFTSENTüretilir; bu süreç ağaç dolaşımıdırFTSENT->fts_infoalanı öğe açıklamasında sık kullanılır ve öğenin nasıl işleneceğini belirlemekte kullanılır
- coreutils, libc’nin sağladıklarının dışında da birçok sistem çağrısı sarmalayıcısı ve yardımcı kullanır
- Yazma işlemlerinde
write()sistem çağrısı sarmalayıcısı vefwrite()gibi libc fonksiyonlarının yanındafull_write()gibi standart dışı fonksiyonlar da görülür- full_write(), sert bir hata oluşmadığı sürece yazmayı yeniden denemeye devam eder
- safe_write(), kesme oluşsa bile
write()sistem çağrısını yeniden dener ddiçindekiiwrite(),splitiçindekicwrite()gibi yalnızca tek bir yardımcı programda kullanılan yazma yardımcıları da vardır
Ortak fonksiyonlar ve kod deyimleri
- Tüm yardımcı programlar en azından
main(),usage(),_()olmak üzere üç fonksiyon kullanır usage(), girdi parametrelerini, anlamlarını ve doğru söz dizimini içeren yardım metnini gösterir_(),system.hiçinde tanımlı bir makrodur ve düz string’leri GNU gettext.h Native Language Support özelliğine bağlar- Kullanıcıya gösterilen bir string ise genellikle bu makro ile sarılır
- Önemsiz olmayan yardımcı programların çoğunda ortak kod satırları tekrar eder
#include "system.h"sistem bağımlı makroları, değişkenleri ve standart dışı yardımcı fonksiyonları tanımlarPROGRAM_NAMEyardımcı programın resmi adını tanımlar ve sürüm kontrolünde kullanılırAUTHORSyardımcı program yazarlarını tanımlar ve sürüm kontrolünde kullanılıremit_try_help()başarısız çıktıdan sonra yardım önerisini yazdırıremit_ancillary_info (PROGRAM_NAME)komuta özgü çıktının ardından ortak ek yardım bilgilerini yazdırırinitialize_main(&argc, &argv)VMS’nin yerleşik joker karakter genişletmesini ele alır; diğer işletim sistemlerinin çoğunda kaldırılıratexit(close_stdout)program çıkışında buffered stream’leri flush edip kapatan fonksiyonu kaydeder
- Acemilere yabancı gelebilecek C deyimleri de karşımıza çıkar
!!çiftli tekli NOT işlemidir; bir değeri boolean’a zorla dönüştürmek için kullanılırdo { ... } while (0), çok ifadeli makroları sarıp önişlemci ikamesinden sonra token’laştırmayı güvenli kılan döngü olmayan bir kalıptır
Bakım modeli ve katkı öncesi kontroller
- coreutils gibi aktif projeler genellikle üç bakım akışıyla evrilir
- Proje geneli değişiklikler, tüm yardımcı programların yapısı ve bağımlılıklarını kapsayan büyük değişikliklerdir
- 1995’te GNU gettext project ile Native Language Support eklendi ve metin çıktısı satırlarının çoğuna
_()makrosu getirildi - 1996’da uluslararasılaştırma desteği genişletildi ve
main()içine çeşitli başlatma kodları eklendi - 1995’te kullanım çıktısına yardımcı programın amacını anlatan kısa açıklama eklendi
- 2003’te VMS joker karakter desteği eklendi; bu
initialize_main()fonksiyonunda görülür - 2016’da başarısızlık yollarında derleyici uyarılarını önlemek için
die()makrosu,exit()veerror()fonksiyonlarının çoğunun yerini aldı
- 1995’te GNU gettext project ile Native Language Support eklendi ve metin çıktısı satırlarının çoğuna
- Yardımcı program bazlı değişiklikler hata düzeltmeleri, yeni özellikler ve optimizasyonlar olarak ayrılır
join,sort,uniq, 2016’daki patch öncesinde overflow saldırılarına açıktıdfkomutuna 2013’te--outputseçeneği eklendiyes, daha iyi buffering ile performans iyileştirmesi aldı
- Yıllık bakım, en azından tüm yardımcı programların telif hakkı yıllarının güncellenmesini içerir
- FSF adresinin güncellenmesi gibi idari değişiklikler de vardır; yürütmeye etkisi yoktur
- Katkı vermek isteyenlerin önce GNU project page, contribution guidelines, rejected features, mailing list archives sayfalarını kontrol etmesi iyi olur
- Kod yazmadan önce kontrol edilmesi gereken üç soru vardır
- Aynı işlev mevcut araçlarla yeniden üretilebilir mi?
- Katkı geriye dönük uyumluluğu bozar mı?
- Önerilen davranış POSIX’ten büyük ölçüde sapıyor mu?
- Emin değilseniz, mailing list üzerinden topluluğa danışma süreci önerilir
İlginç ayrıntılar
- En kısa yardımcı program
falseolup 2 satırdır;arch,dir,vdirile aynı uzunluktadır - En kısa bağımsız çalıştırılabilir yardımcı program
trueolup 80 satırdır; ilk sürümü neredeyse minimal bir C programına yakındır - En uzun yardımcı program
lsolup 5308 satırdır - Birçok yardımcı programın kökleri 1970’lerin Research UNIX’ine kadar uzanır; bazıları Multics’e kadar gider
- En eski düşünsel ata, yaklaşık 1963 tarihli CTSS
LISTFkomutudur; daha sonralsolarak kısalmıştırLISTF, 1962 design paper içindeki özgün 18 yardımcı programdan biridir
ddkomutunun kendine özgü söz dizimi, 1960’ların başındaki OS/360 job control language yapısını andırırsort, çoklu iş parçacığından yararlanan tek yardımcı programdırfmt, fonksiyon maliyetini kullanarak satır ve paragraf optimizasyonunu gösteriryes, yüksek performanslı çıktı için sayfa hizalı bellek tamponu kullanırdfaygıt metadata’sını kullanır,duise tüm dosyaları kontrol eder; bu yüzdendfdaha hızlıdırcksumiçinde normal yürütme ve CRC-32 tablosu üretimi için iki giriş noktası vardırechoiçin başarısızlık koşulu yokturtestveexprtasarımı, genel yardımcı program yapısından oldukça farklıdırsubaşlangıçta coreutils/shellutils içinde bakımı yapılan bir komuttu
Bakmaya değer uygulamalar
- shuf ve shred, rastgele sayı için ISAAC cipher) kullanır
- shuf, Reservoir sampling kullanır
- sum, eski System V ve BSD checksum’larını kullanır
- expr, sol birleşimli ifade değerlendirmesi kullanır
- shred, Secure overwrite kullanır
- cksum, CRC-32 checksum hesaplar
- sort, Merge sort varyasyonu kullanır
- factor, matematiksel uygulama unsurları açısından incelenmeye değer bir yardımcı programdır
Sponsorluk notu
- Kişisel sponsorluk alacak bir ayar yapılmamış
- Zaman veya para paylaşmak isterseniz Free Software Foundation’a katkıda bulunabilirsiniz
1 yorum
Hacker News yorumları
“Bu yardımcı programların çoğu neredeyse 30 yıllık ve bu süre içinde birçok kişi tarafından değiştirilmiş” denmesine rağmen “uzun ömürlü olacak ya da rolünün ötesine genişleyecek şekilde tasarlanmamış” deniyorsa, yazarın uzun ömür düşünülerek yazılmış program örneklerini görmek isterdim
30 yıl ayakta kalmış bir program mı, yoksa önümüzdeki 30 yıl daha gideceğini düşündüğü bir program mı, merak ediyorum
Kendi yaptığım küçük programlama dilini test etmek için bash ve coreutils ile bir test framework'ü kurdum; başta “düzgün” bir dil kullanmadığım için içime sinmemişti ama pratikte çok iyi çalıştı ve paralel çalıştırma da vardı
Test edemediğim tek şey programın
argv[0]değeriydi; ne kadar kombinasyon denesem de istediğim davranışı tam olarak elde edemedim, bu yüzden coreutils'eenviçin bir özellik isteği ve yama gönderdim: https://lists.gnu.org/archive/html/coreutils/2023-08/msg0006...Kabul edilecek gibi görünüyor; bu da eski bir programa yeni bir özelliğin eklenmesi demek
Yazarın sıraladığı pratikler, büyük programlarda genellikle ölçek büyüdükçe bakım yapılabilirliğe zarar verdiği için eleştirilen şeyler
Bakılabilecek kaynaklar:
GNU coreutils test yaklaşımı: https://www.pixelbeat.org/docs/coreutils-testing.html
coreutils'in her komutunu inceleme: https://ratfactor.com/slackware/pkgblog/coreutils
GNU Coreutils ile komut satırında metin işleme: https://learnbyexample.github.io/cli_text_processing_coreuti... — 20'den fazla metin işleme aracını ele alan e-kitabım
İlgili yazılar:
Decoded: GNU Coreutils (2018) - https://news.ycombinator.com/item?id=29871037 - Ocak 2022, 7 yorum
Decoded: GNU coreutils (2019) - https://news.ycombinator.com/item?id=26411908 - Mart 2021, 38 yorum
Decoded: GNU Coreutils - https://news.ycombinator.com/item?id=20328650 - Temmuz 2019, 55 yorum
İlginç bir bilgi: macOS'te Homebrew ile coreutils kurarsanız, macOS'te zaten
od(1)bulunduğu için coreutils'inodkomutugod(1)olarak kurulurYazar görüyorsa, en az bir hata var. shred sayfasının kısa açıklaması[0] aslında
csplitaçıklaması[1]; “dosya içeriğini gizlemek için üzerine yazar ve isteğe bağlı olarak siler” gibi bir şey olmalı[0]: https://maizure.org/projects/decoded-gnu-coreutils/shred.htm...
[1]: https://maizure.org/projects/decoded-gnu-coreutils/csplit.ht...
Böyle bir şeyin olduğunu bilmiyordum, güzelmiş.
yesgibi basit bir şey bile standart çıktıya yazan bir yardımcı programın temel kodunun nasıl yazıldığını görmek açısından oldukça ilginçhttps://maizure.org/projects/decoded-gnu-coreutils/yes.html
Örneğin çıktıyı çok hızlı yazmanın nasıl yapılacağını gösteren minimal bir örnek olması güzel. Program zaten çok kısa olduğu için satır sayısı pek önemli değil; yazarı yöntemi biliyorsa, engel oluşturmaması için mümkün olduğunca hızlı yapmak daha iyi bence
Temel yardımcı programlar listesi olarak oldukça eğlenceli. UNIX’i çok uzun süredir kullanıyorum ama
shred,shuf,factorgibi şeyleri hiç duymamıştımBir ara
çalıştırıp kendini öldürmeden önce ne kadar ilerleyeceğini görmek istiyorum. Elbette bir sanal makinede ya da kolayca yeniden flash’lanabilecek bir cihazda
ddile denedim; sonuna kadar tamamlandı ve epey heves kırıcıydıÇökmesini ya da en azından bağlantının kopmasını ummuştum; ama çekirdek ile
sshdvebashhâlâ bellekte olduğundan, hiçbir şeyi doğru düzgün yapamadığım bir prompt’a geri döndümETXTBUSY),shredkendisini ya dafindı bozamaz/bin/truenun aslında başarısız olup false döndürebilmesini seviyorum. Teknik olarak “Not/bin/false” çağrısı daha sağlam hale geliyor: https://github.com/coreutils/coreutils/blob/master/src/true....Elbette bunun neredeyse hiç gerçekleşmeyecek bir durum olduğunu biliyorum, ama yine de komik
truekomutuyla ilgili bir başka ilginç nokta da gereğinden çok daha karmaşık hale gelmiş olmasıÖnce bir alıştırma:
truekelimenin tam anlamıyla böyle başladı. Epey Zen koanı gibiİlk değişiklik hukuk yüzündendi. Her kodda telif hakkı bildirimi olması gerekiyordu; boş dosyalarda bile. Böylece hiçbir şey içermeyen dosya, yalnızca telif hakkı bildirimi içeren bir dosyaya dönüştü ve “hiçliğe telif hakkı konabilir mi?” sorusu akla geldi. AT&T bunu denedi
Sonra biri, programların iyi tanımlanmış olması ve Unix’in tesadüfi davranışlarına bel bağlamaması gerektiğini söyledi; o noktada bu oldukça makuldü. Böylece
truenihayet koda kavuştu ve o kodexit 0idiBir başkası da sistem yardımcı programlarının shell yerine C ile yazılırsa daha hızlı olacağını söyledi; OpenBSD’de bunun hâlâ güzel bir örneği var: http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/usr....
Bir noktada GNU bürokrasisi devreye girip tüm programların
-hbayrağını desteklemesi gerektiğini söyledi; ardından tüm programların locale desteği olması gerektiği söylendi ve bugünkü GNUtrueşaşırtıcı biçimde 80 satırlık hale geldi: https://github.com/coreutils/coreutils/blob/master/src/true....Bunu sorun etmeyebilirsiniz; ama tanımı gereği “hiçbir şey yapmayıp başarılı olan” bir program için epey fazla kod
http://trillian.mit.edu/~jc/humor/ATT_Copyright_true.html
trueçalıştırmak, yorum kafa karıştırmıyorsa, etkilenmiyor gibi görünüyorVeri yapıları ve algoritmaların nasıl faydalı uygulanacağı konusunda sezgi geliştirmek isteyen acemi bir programcı için burada özellikle bakılması gereken bir şey var mı?
Örneğin veriyi kullanıcı alanına kopyalamamak için
read()/write()yerinecopy_file_range()kullanmak gibi. Bu, bilgisayar biliminden çok yazılım mühendisliğine yakınBu sitenin ana fikrini kaçırıyor olabilirim ama bunların her biri için zaten man sayfası ya da info sayfası yok mu?