Neden / dev / null bir dosyadır? Neden işlevi basit bir program olarak uygulanmıyor?


114

Linux'taki özel dosyalar kavramını anlamaya çalışıyorum. Bununla birlikte, özel bir dosyaya sahip /devolmak, işlevi C'deki bir dizi satır tarafından bilgime göre uygulanabileceği zaman aptalca görünüyor.

Üstelik hemen hemen aynı şekilde kullanabilirsiniz, yani nullyönlendirmek yerine boruya kullanabilirsiniz /dev/null. Dosya olarak sahip olmanın belirli bir nedeni var mı? Dosya yapmak, aynı dosyaya erişen çok fazla program gibi birçok soruna neden olmaz mı?


20
Bu arada, bu ek yükün çoğu, neden cat foo | bar(ölçek olarak) olduğundan daha kötüdür bar <foo. catönemsiz bir programdır, ancak önemsiz bir program bile maliyetler yaratır (bazıları FIFO anlambilimine özgüdür - çünkü programlar seek()FIFO'ların içinde olamaz) , örneğin, arama ile verimli bir şekilde uygulanabilecek bir program çok daha pahalı operasyonlar gerçekleştirebilir. Bir boru hattı verildiğinde, /dev/nullonun gibi bir karakter cihazıyla bu işlemleri sahtekarlaştırabilir veya gerçek bir dosya ile bunları uygulayabilir, ancak bir FIFO içeriğe duyarlı bir şekilde işlemeye izin vermez.
Charles Duffy

13
grep blablubb file.txt 2>/dev/null && dosomethingboş bir program veya işlev olmak ile çalışamadı.
rexkogitans

16
Her şeyin bir dosya olduğu vizyonunun nereye gittiğini görmek için Plan 9 işletim sistemi hakkında bilgi okumanın aydınlatıcı (veya en azından zihin genişletici) olduğunu görebilirsiniz; Kavramı tamamen kucaklayan bir sistem gördüğünüzde (modern Linux / Unix'in yaptığı gibi / çoğunlukla değil).
mtraceur

25
Çekirdek alanında çalışan bir aygıt sürücüsünün "bir avuç C satırı" olan bir program olduğuna işaret eden hiç kimsenin yanı sıra , şu ana kadarki cevapların hiçbiri "aynı dosyaya erişen çok fazla program" varsayımını ele almadı. soruda.
JdeBP

12
Bunu inanmazsınız ama: Re "işlevi C hatlarının bir avuç uygulanabilecek" olduğu C hatlarının bir avuç tarafından uygulanan! Örneğin, readişlevinin gövdesi /dev/nullbir "return 0" dan oluşur (yani hiçbir şey yapmaz ve sanırım bir EOF ile sonuçlanır): (Statik github.com/torvalds/linux/blob/master/ drivers / char / mem.c ) ssize_t read_null(struct file *file, char __user *buf, size_t count, loff_t *ppos) { return 0; }(Oh, Sadece @JdeBP'nin o noktaya değiniyor olduğunu gördüm. Neyse, işte örnek :-).
Peter A. Schneider

Yanıtlar:


153

Karaktere özel bir cihaz kullanmanın performans yararlarına ek olarak, birincil yarar modülerliktir . / dev / null, sadece kabuk boru hatlarında değil, bir dosyanın beklendiği hemen her bağlamda kullanılabilir. Dosyaları komut satırı parametreleri olarak kabul eden programları göz önünde bulundurun.

# We don't care about log output.
$ frobify --log-file=/dev/null

# We are not interested in the compiled binary, just seeing if there are errors.
$ gcc foo.c -o /dev/null  || echo "foo.c does not compile!".

# Easy way to force an empty list of exceptions.
$ start_firewall --exception_list=/dev/null

Tüm bunlar, bir programın kaynak veya lavabo olarak kullanılmasının çok hantal olacağı durumlardır. Kabuk boru hattı durumunda bile, stdout ve stderr dosyalara bağımsız olarak yönlendirilebilir, bu da çalıştırılabilirlerle batarya olarak yapılması zor bir şeydir:

# Suppress errors, but print output.
$ grep foo * 2>/dev/null

14
Ayrıca /dev/nullsadece kabuğun komutlarında kullanmazsınız. Bir yazılıma sağlanan diğer parametrelerde --- örneğin yapılandırma dosyalarında kullanabilirsiniz. --- Yazılımın Fort bu çok uygun. /dev/nullDüzenli bir dosya arasında bir fark yaratmak gerekmez .
pabouk

Çalıştırılabilirleri zorlaştırmak için ayrı yönlendirmeler ile ilgili kısmı anladığımdan emin değilim. C, sen sadece yapmak pipe, forkve execvediğer herhangi bir süreç boru gibi sadece yapılan değişikliklerle dup2, doğru bağlantıları kurmak aramalar? Çoğu kabuğun bunu yapmanın en güzel yollarını sunmadığı doğrudur, ancak büyük olasılıkla dosya olarak çok fazla bir cihaz düzenine sahip olmasak /devve /prociçerideki ve çalıştırılabilir olarak ele alınan işlemlerin çoğu , kabukları yollarla tasarlanmış olacaktı. şimdi yönlendirdiğimiz kadar kolay yapmak için.
aschepler

6
@ aschepler Çalıştırılabilirleri batırmaya yönlendirilmemesi zor. Boş dosya bir dosya olmasaydı , her iki dosyadan da okuyabilen / okuyabilen yazma uygulamaları ve boş havuz daha karmaşık olurdu. Her şey bir dosya olmak yerine, her şeyin çalıştırılabilir olduğu bir dünya hakkında konuşmuyorsanız? Bu, * nix işletim sistemindeki modelinizden çok farklı bir model olurdu.
Kübik

1
@aschepler Unuttun wait4! Doğru, stdout ve stderr komutlarını POSIX apis kullanarak farklı programlara aktarmanız kesinlikle mümkün olabilir ve stdout ve stderr komutunu farklı komutlara yönlendirmek için akıllı bir kabuk sözdizimi oluşturmak mümkün olabilir. Ancak şu anda böyle bir kabuğun farkında değilim ve en büyük nokta, / dev / null öğesinin mevcut takımlara (büyük ölçüde dosyalarla çalışan) düzgün bir şekilde uyması ve / bin / null'un olmamasıdır. Bir program için gcc'nin (güvenli!) Çıktısını bir dosyaya dönüştürmesini kolaylaştıran bazı IO API'leri de hayal edebiliriz, ancak içinde bulunduğumuz durum bu değil.
ioctl

2
@ kabuklarla ilgili ioctl; Hem zsh hem de bash en azından gibi şeyler yapmanıza izin verecek grep localhost /dev/ /etc/hosts 2> >(sed 's/^/STDERR:/' > errfile ) > >(sed 's/^/STDOUT:/' > outfile), sonuç ayrı ayrı işlenir errfileveoutfile
Matija Nalis

62

Adalet içinde, normal bir dosya değildir; Bir var bir karakter özel bir cihaz :

$ file /dev/null
/dev/null: character special (3/2)

Bir dosya veya programdan ziyade bir cihaz olarak işlev görür, standart girdi / çıktı / hata da dahil olmak üzere herhangi bir dosya tanımlayıcısına eklenebileceği için girişi doğrudan ona yönlendirmek veya çıktı almak anlamına gelir.


24
cat file | nullİlk Ayrıca, vb bir boru kurma bir işlem üretmekten, yeni süreçte "sıfır" ifa ederken ülkenin, yükü bir sürü olurdu nullkendisi daha sonra bir tampon içine bir döngü okuma bayt CPU biraz kullanırsınız Sadece atıldı ... /dev/nullÇekirdekte uygulanması bu şekilde sadece daha verimlidir. Ayrıca, /dev/nullyeniden yönlendirme yerine, argüman olarak geçmek istiyorsanız ne olur ? ( <(...)Bash olarak kullanabilirsiniz , ancak bu daha da ağır şekilde ilerledi!)
filbranden

4
Adlı bir programa boruya olsaydı nullYerine göre yönlendirmeyi kullanarak /dev/nullsadece kendi stderr'yi null gönderirken, bir programı çalıştırmak için kabuk anlatmak için basit, açık bir yol olurdu?
Mark Plotnick

5
Bu genel bir gösteri için gerçekten pahalı bir kurulum. Bunun /dev/zeroyerine kullanmanızı öneririm .
chrylis

20
Bu örnekler yanlış. dd of=-adlı bir dosyaya yazar -, of=stdout'a yazmayı ddvarsayılan olarak yazdığı şekilde atlamanız yeterlidir . Boru tesisatı stdinini okumamış falsegibi falseçalışmaz, bu yüzden ddbir SIGPIPE ile öldürülür. Girişini iptal eden bir komut için kullanabilirsiniz cat > /dev/null. Ayrıca şişe boynu ile muhtemelen alakasız olan karşılaştırma burada muhtemelen rastgele sayı üretimi olacaktır.
Stéphane Chazelas

8
AST ddvb. Sürümleri , hedef / dev / null olduğunu tespit ettiklerinde yazma sınaması bile yapmazlar.
Mark Plotnick

54

Sanıyorum neden Unix (ve dolayısıyla Linux) şeklindeki vizyon / tasarım ile ne çok şey vardır ve avantajları ondan kaynaklanan.

Hiç şüphe yok ki, fazladan bir süreci döndürmemek için ihmal edilemez bir performans avantajı var, ama bence daha fazlası var: Early Unix, bakarsanız bariz olmayan ama zarif bir avantaja sahip olan "her şey bir dosyadır" metaforuna sahipti. kabuk komut dosyası perspektifi yerine sistem açısından.

nullKomut satırı programınız ve /dev/nullcihaz düğümünüz olduğunu söyleyin . Kabuk-komut dosyası bakış açısına göre, foo | nullprogram aslında gerçekten yararlı ve kullanışlı ve yazmak foo >/dev/nullbiraz daha uzun sürüyor ve garip görünebilir.

Ama işte iki alıştırma:

  1. Programını uygulamak edelim nullmevcut Unix araçları ve kullanma /dev/null- kolay: cat >/dev/null. Bitti.

  2. /dev/nullAçısından uygulayabilir misiniz null?

Girdiyi atmak için C kodunun önemsiz olduğu konusunda kesinlikle haklısınız, bu nedenle görev için uygun bir sanal dosya bulundurmanın neden yararlı olduğu henüz belli olmayabilir .

Şunu düşünün: hemen hemen her programlama dili zaten dosyalarla, dosya tanımlayıcılarıyla ve dosya yollarıyla çalışmak zorunda, çünkü bunlar Unix'in “her şey bir dosyadır” paradigmasının bir parçasıydı.

Eğer sahip olduğunuz tek şey stdout'a yazılan programlarsa, program onları tüm yazarları yutan sanal bir dosyaya ya da tüm yazarları yutan bir programa yöneltecek şekilde umursamaz.

Şimdi, veri okumak veya yazmak için dosya yollarına sahip programlarınız varsa (çoğu programın yaptığı) - ve bu programlara "boş girdi" veya "bu çıktıyı at" işlevini eklemek istiyorsanız, bununla birlikte /dev/nullücretsiz gelir.

Bunun zerafetinin, ilgili tüm programların kod karmaşıklığını azalttığına dikkat edin - sisteminizin gerçek bir "dosya adı" ile "dosya" olarak sağlayabileceği her genel ancak özel kullanım için, kodunuz özel komut eklemekten kaçınabilir -line seçenekleri ve işlemek için özel kod yolları.

İyi yazılım mühendisliği genellikle, bir problemin bazı öğelerini düşünmek daha kolay ancak esnek bir şekilde soyutlamak için iyi veya "doğal" metaforlar bulmaya dayanır , böylece temelde aynı yüksek seviye problemleri çözmeden çözebilirsiniz. zaman ve zihinsel enerjiyi sürekli olarak aynı alt seviye problemlere yeniden uygulama çözümlerine harcarlar.

"Her şey bir dosyadır" kaynaklara erişmek için böyle bir metafor gibi görünmektedir: openHeirarchical bir ad alanında belirli bir yolu çağırıyorsunuz , nesneye referans (dosya tanımlayıcısı) alıyorsunuz readve writedosya tanımlayıcılarına vb. Stdin / stdout / stderr, ayrıca sizin için önceden açılmış olan dosya tanımlayıcılarıdır. Borularınız sadece dosya ve dosya tanımlayıcılarıdır ve dosya yeniden yönlendirmesi tüm bu parçaları birbirine yapıştırmanıza izin verir.

Unix, bu soyutlamaların birlikte çalışmasının iyi olmasından dolayı, kısmen olduğu kadar başarılı oldu ve /dev/nullen iyisi, bütünün bir parçası olarak anlaşıldı.


Not: "Her şey bir dosyadır" un Unix sürümüne ve /dev/nullizleyen birçok sistemde uygulanan metaforun daha esnek ve güçlü bir genellemesine doğru atılan ilk adımlar gibi şeylere bakmaya değer .

Örneğin, Unix'te, özel dosya benzeri nesnelerin /dev/nullçekirdeğin kendisinde uygulanması gerekiyordu, ancak dosya / klasör biçiminde işlevselliği ortaya koymanın, o zamandan bu yana programlar için bir yol sağlayan çok sayıda sistem yapılmış olması yeterince yararlı olduğu ortaya çıktı. bunu yapmak için.

Bunlardan birincisi, Unix'i yapan aynı kişiler tarafından yapılan Plan 9 işletim sistemi idi. Daha sonra GNU Hurd, "çevirmenleri" ile benzer bir şey yaptı. Bu arada, Linux (şimdiye dek diğer ana sistemlere yayılmış olan) FUSE yazılımını almaya başladı.


8
@Peter Cevabın, tasarımı anlamayan bir pozisyondan başladığına işaret eder. Herkes tasarımı zaten anladıysa, bu soru olmazdı.
OrangeDog

1
@mtraceur: Kök izni olmadan bir görüntü dosyası bağla ? FUSE'nin kök gerektirmeyebileceğine dair bazı kanıtlar gösteriyor, ancak emin değilim.
Peter Cordes

1
@PeterCordes RE: "garip görünüyor": tasarım için bir özür değil, onun altındaki sistem uygulamasını düşünmüyorsanız ve sistem hakkında henüz eureka anı yaşamadığınızı gösteren bir açıklama dünya çapında tasarım avantajları. Bu cümleyi "bir kabuk betik bakış açısıyla" açarak ve bunun önüne geçen birkaç cümle ile sistem perspektifi arasındaki zıtlığı ortaya koyarak bunu netleştirmeye çalıştım. Daha fazla düşünce "garip görünebilir" daha iyidir, bu yüzden onu ince ayarlayacağım. Çok ayrıntılı hale getirmeden daha net hale getirmek için daha fazla ifade önerisi bekliyorum.
mtraceur

2
Unix'le ilgili olarak genç bir mühendis olarak söylenen ilk şey "Her Şey Bir Dosya" ydı ve başkentleri duyacağınıza yemin ederim. Ve bu fikri erken ele geçirmek, Unix / Linux'un anlaşılması daha kolay görünmesini sağlar. Linux bu tasarım felsefesinin çoğunu devraldı. Birinin bundan bahsetmesine sevindim.
StephenG

2
@PeterCordes, DOS "sihirli dosya adının NULher dizinde görünmesini sağlayarak yazım problemini" çözdü " , yani yazmanız gereken tek şey > NUL.
Cristian Ciupitu

15

Performans nedeniyle bir program yerine /dev/nullbir karakter aygıtı (sıradan bir dosya gibi davranır) olduğunu düşünüyorum .

Eğer bir program olsaydı, yükleme, çalıştırma, programlama, çalıştırma ve sonrasında programı durdurma ve boşaltmayı gerektirir. Açıkladığınız basit C programı elbette çok fazla kaynak tüketmez, ancak süreç yönetimi işlemleri çok büyük ölçüde maliyetli olduğu için çok sayıda (milyonlarca) yönlendirme / borulama işlemi dikkate alındığında önemli bir fark yarattığını düşünüyorum . bağlam anahtarları içerirler.

Bir başka varsayım: Bir programa bağlanmak , alıcının program tarafından tahsis edilmesini gerektirir (daha sonra doğrudan atılsa bile). Bu nedenle, alete bağlarsanız, bir kez gönderme programında ve bir kez de alıcı programda çift bellek tüketimine sahip olursunuz.


10
Bu sadece kurulum maliyeti değil, bir boruya yapılan her yazının hafıza kopyalaması ve okuma programına bir bağlam geçişi gerektirmesidir. (Veya en azından, boru tamponu dolduğunda bir içerik düğmesidir. Okuyucunun readveri olduğunda başka bir kopya yapması gerekir ). Unix'in tasarlandığı tek çekirdekli bir PDP-11'de bu göz ardı edilemez! Bellek bant genişliği / kopyalama işlemi bugün olduğundan çok daha ucuz. A writebir fd için sistem çağrısı üzerine açmak /dev/nullbile tampon herhangi bir veri okumadan hemen dönebilir.
Peter Cordes,

@PeterCordes, notum teğetsel, ancak paradoksal olarak, günümüzde yazılan hafızanın her zamankinden daha pahalı olması mümkün. 8 çekirdekli bir işlemci, potansiyel olarak bir saat içinde 16 tamsayı işlemi gerçekleştirirken, uçtan uca bir bellek yazma örneğin 16 saatte (4GHz CPU, 250 MHz RAM) tamamlanır. Bu 256 faktörüdür. Modern CPU’nun RAM’i PDP-11 CPU’nun RL02’si gibidir, neredeyse bir çevre birimi depolama birimi gibi! :) Tabii ki basit değil, doğal olarak, ama önbelleğe çarpan her şey yazılacak ve işe yaramaz yazılar yazılan önemli önbellek alanının diğer hesaplamalarından mahrum olacaktır.
kkm

@kkm: Evet, çekirdekteki bir boru arabelleğine yaklaşık 2 x 128kiB L3 önbellek ayak izi harcamak ve nullprogramdaki bir okuma arabelleği berbat olur, ancak çoğu çok çekirdekli CPU her zaman tüm çekirdeklerle meşgul olmaz, nullProgramı çalıştırmak için CPU zamanı çoğunlukla ücretsizdir. Çekirdekleri pimlenen bir sistemde, işe yaramaz borular daha büyük bir sorun olur. Ancak hayır, "sıcak" bir tampon RAM'e basılmadan defalarca yeniden yazılabilir, bu yüzden çoğunlukla önbellek için değil L3 bant genişliği için yarışıyoruz. Özellikle de aynı fiziksel yapıdaki diğer mantıksal çekirdek (ler) in rekabet ettiği bir SMT (hiper iş parçacığı) sistemi için pek iyi değil ...
Peter Cordes

.... Fakat hafıza hesaplamanız çok hatalı. Modern CPU'ların çok fazla bellek paralelliği vardır, bu nedenle DRAM'e gecikme süresi 200-400 çekirdek saat çevrimi ve L3> 40 gibi bir şey olsa da , bant genişliği ~ 8 bayt / saattir. (Şaşırtıcı bir şekilde, L3 veya DRAM'a göre tek iş parçacıklı bant genişliği, dört çekirdekli bir masaüstü ile dört çekirdekli bir masaüstüne karşı dört kanallı belleğe sahip birçok çekirdekli bir Xeon'da daha kötüdür , çünkü bir çekirdeğin uçuşta tutabileceği maksimum istek eşzamanlılığı ile sınırlıdır. max_concurrency / latency: Skylake neden tek iş parçacıklı bellek verimi için Broadwell-E'den daha iyi? )
Peter Cordes

... Ayrıca bakınız dört çekirdekli ve 18 çekirdekli Haswell numaraları için 7-cpu.com/cpu/Haswell.html . Her neyse, evet modern CPU'lar, hafıza bekletilmemesi durumunda saat başına yapılan saçma bir iş miktarını elde edebilirler. Sayılarınız, saatte yalnızca 2 ALU işlemi gibi görünüyor, belki 1993’teki bir Pentium gibi, ya da modern, düşük seviye çift sorunlu bir ARM gibi. Bir Ryzen veya Haswell potansiyel olarak saat başına çekirdek başına 4 skaler tamsayı ALU op + 2 bellek op veya SIMD ile çok daha fazlasını gerçekleştirir. örneğin, Skylake-AVX512'de (çekirdek başına) saat başı 2 çıkış açıklığı vpaddd zmm: talimat başına 16 32 bit eleman.
Peter Cordes

7

"Her şey bir dosyadır" ve dolayısıyla diğer çoğu yanıtın dayandığı her yerde kullanım kolaylığı yanında, @ user5626466'nın belirttiği gibi performans sorunu da vardır.

Uygulamada göstermek için, basit bir program yaratacağız nullread.c:

#include <unistd.h>
char buf[1024*1024];
int main() {
        while (read(0, buf, sizeof(buf)) > 0);
}

ve ile derleyin gcc -O2 -Wall -W nullread.c -o nullread

(Not: lseek'i (2) borularda kullanamayız, bu nedenle boruyu boşaltmanın tek yolu boşalıncaya kadar okumaktır).

% time dd if=/dev/zero bs=1M count=5000 |  ./nullread
5242880000 bytes (5,2 GB, 4,9 GiB) copied, 9,33127 s, 562 MB/s
dd if=/dev/zero bs=1M count=5000  0,06s user 5,66s system 61% cpu 9,340 total
./nullread  0,02s user 3,90s system 41% cpu 9,337 total

standart /dev/nulldosya yönlendirme ile çok daha iyi hızlar elde ediyoruz (belirtilen gerçekler nedeniyle: daha az bağlamda anahtarlama, çekirdek sadece kopyalamak yerine verileri yok sayma vb.):

% time dd if=/dev/zero bs=1M count=5000 > /dev/null
5242880000 bytes (5,2 GB, 4,9 GiB) copied, 1,08947 s, 4,8 GB/s
dd if=/dev/zero bs=1M count=5000 > /dev/null  0,01s user 1,08s system 99% cpu 1,094 total

(Bu orada bir yorum olmalı, ama bunun için çok büyük ve tamamen okunamaz olur)


Hangi donanım üzerinde test yaptınız? 4.8GB / sn, Skylake i7-6700k (DDR4-2666 ile karşılaştığım 23GB / sn ile karşılaştırıldığında oldukça düşük, ancak tamponun L3 önbelleğinde sıcak kalması gerekiyor. Bu nedenle, maliyetin büyük bir kısmı Spectre ile pahalı olmakta. + Erime azaltma etkinleştirilmiş. Bu, boru tamponları 1M'den daha küçük olduğu için borulara iki kat daha fazla zarar veriyor, bu nedenle daha fazla yazma / okuma sistemi çağırıyor. Yaklaşık 10x perf fark beklediğimden daha kötü. Skylake sistemimde 23GB / sn vs. 3.3GB / sn yani 6.8 bir faktör yani, Linux 4.15.8-1-ARCH x86-64 çalışan, Vay, sistem çağrıları vardır. pahalıdır şimdi)
Peter Cordes

1
@PeterCordes 64k boru tamponları ile 3GB / sn, saniyede 2x 103124 sistem çağrısı önerir ... ve bu bağlam bağlamında anahtar sayısı, heh. Bir sunucu cpu üzerinde, saniyede 200000 sistem çağrısı ile, çok az çalışma kümesi olduğundan, PTI'dan% 8 daha fazla harcama bekleyebilirsiniz. (Referans gösterdiğim grafik PCID optimizasyonunu içermiyor, ancak küçük çalışma grupları için bu çok önemli değil). Yani PTI'nin orada büyük bir etkisi olduğundan emin değilim? brendangregg.com/blog/2018-02-09/...
sourcejedi

1
Çok ilginç, bu yüzden 2 MB L2 önbellekli bir Silvermont , bu nedenle ddtampon + alma tamponunuz uymuyor; Muhtemelen son seviye önbellek bant genişliği yerine hafıza bant genişliği ile uğraşıyorsunuzdur. 512k tamponlarla veya hatta 64k tamponlarla daha iyi bant genişliği elde edebilirsiniz. (Göre stracemasaüstümde, writeve read1048576 iade yüzden biz sadece ödeme yapan kullanıcı demektir düşünüyorum <->., 64k başına değil, bir kere MiB başına TLB geçersiz + dal-tahmin çekirdek maliyeti floş @sourcejedi Bu Spectre var Sanırım en fazla maliyeti olan azaltma)
Peter Cordes

1
@sourcejedi: Spectre azaltma etkin durumdayken, syscallhemen geri dönen bir ürünün bedeli Spectre azaltma etkinleştirilmiş Skylake'de ENOSYS~ 1800 devirdir, bunun birçoğu @ BeeOnRope testine göre BPU'yuwrmsr geçersiz kılar . Azaltmanın devre dışı bırakılmasıyla kullanıcı-> çekirdek-> kullanıcı gidiş dönüş süresi ~ 160 döngüdür. Eğer Ama eğer olan bellek dokunmadan sürü Meltdown hafifletme de önemlidir. Hugepages yardım etmeli (daha az TLB girişinin yeniden yüklenmesi gerekiyor).
Peter Cordes

1
@PeterCordes Tek çekirdekli bir unix sisteminde, kesinlikle 64K başına 1 içerik anahtarı ya da boru tamponunuz ne olursa olsun ve bunun zarar vereceğini görürüz, aslında… [2 cpu çekirdeğine sahip aynı sayıda bağlam anahtarı] ( unix.stackexchange.com/questions/439260/… ); ayrıca bağlam anahtarı olarak her 64k için bir uyku / uyanıklık döngüsünü sayıyor olmalı (nominal bir "boşta işleme"). Boru hattı işlemlerini aynı CPU'da tutmak aslında iki kattan daha hızlı çalıştı.
kaynakjedi

6

Sorunuz, basit bir şekilde bir dosya yerine boş bir program kullanılarak basitçe bir şey kazanılmış gibi düşünülüyor. Belki de "sihirli dosyalar" kavramından kurtulabiliriz ve bunun yerine sadece "sıradan borular" olabilir.

Ancak, bir borunun da bir dosya olduğunu düşünün . Normalde adlandırılmazlar ve bu nedenle yalnızca dosya tanımlayıcıları aracılığıyla manipüle edilebilirler.

Bunu biraz tartışmalı bir örnek olarak düşünün:

$ echo -e 'foo\nbar\nbaz' | grep foo
foo

Bash'in işlem ikamesini kullanarak aynı şeyi daha dolambaçlı bir yolla gerçekleştirebiliriz:

$ grep foo <(echo -e 'foo\nbar\nbaz')
foo

Değiştir grepiçin echove biz kapakların altında görebilirsiniz:

$ echo foo <(echo -e 'foo\nbar\nbaz')
foo /dev/fd/63

<(...)Yapı sadece bir dosya adı ile değiştirildi ve grep herhangi eski bir dosya açıyor, sadece ismini vermek olur düşündüğü edilir /dev/fd/63. Burada, /dev/fddosyaya erişen her dosya tanımlayıcısı için adlandırılmış yöneltmeler yapan sihirli bir dizindir.

Sıradan bir dosya gibi, içinde görünen herşeyi mkfifogösteren bir boru oluşturmayı daha az sihirle yapabiliriz ls:

$ mkfifo foofifo
$ ls -l foofifo 
prw-rw-r-- 1 indigo indigo 0 Apr 19 22:01 foofifo
$ grep foo foofifo

Ayrıca:

$ echo -e 'foo\nbar\nbaz' > foofifo

ve işte, grep çıkacaktır foo.

Borular ve düzenli dosyalar ile / dev / null gibi özel dosyaların farkına varınca, sadece dosya olduklarını düşünüyorum, boş bir programın uygulanması daha karmaşıktır. Çekirdek bir dosyaya yazmayı her iki şekilde de işlemek zorundadır, ancak / dev / null olması durumunda, yazmaları zemine bırakabilir, bununla birlikte bir boru ile baytları gerçekte baytları başka bir programa aktarabilir. aslında onları okudum.


@Lyle Evet? O zaman neden yankı yazdırıyor /dev/fd/63?
Phil Frost

Hm. İyi bir nokta. Şey, bu kabuklar tarafından gerçekleştirilir, bu yüzden belki de kabuğunuz büyüdüğüm eski Bourne kabuğundan farklıdır.
Lyle

Bir fark, echo stdin'den okumaz, grep okur ama kabuğun onları çalıştırmadan önce nasıl bileceğini düşünemiyorum.
Lyle

1
Ve strace bunu daha açık hale getiriyor: benim için. kesinlikle haklısın, bash ile. '<(...)' yapısı, <dosya adından oldukça farklı bir şey yapıyor. Hm. Bir şey öğrendim.
Lyle

0

Tarihsel paradigmaların ve performansın ötesinde bir güvenlik sorunu olduğunu savunuyorum. Ayrıcalıklı yürütme kimlik bilgilerine sahip program sayısını sınırlamak, ne kadar basit olursa olsun, sistem güvenliğinin temel prensibidir. /dev/nullSistem servislerinin kullanması nedeniyle bu tür ayrıcalıklara sahip olmak için kesinlikle bir değişiklik yapmanız gerekecektir. Modern güvenlik çerçeveleri, istismarları önleyen mükemmel bir iş çıkarmaktadır, ancak kusursuz değildirler. Dosya olarak erişilen bir çekirdek çekirdek aygıttan yararlanmak çok daha zordur.


Bu saçmalık gibi geliyor. Sorunsuz bir çekirdek sürücüsü yazmak, stdin'i okuyan + geçersiz kılan hatasız bir program yazmaktan daha kolay değildir. Hem öylesine, setuid veya bir şey olmak zorunda değildir /dev/nullya da teklif edilen giriş-atmadan programı, saldırı vektörü aynı olacaktır: garip bir şey kök olarak çalışan bir komut dosyası veya programı almak (gibi çalışın lseekbölgesi /dev/nullveya açık aynı işlemden defalarca, ya da IDK ne, ya da /bin/nullgarip bir ortam ya da her neyse.
Peter Cordes

0

Diğerleri zaten belirttiği gibi, /dev/null bir satır kod bir avuç yapılmış bir programdır. Sadece bu kod satırları çekirdeğin bir parçası.

Daha açık hale getirmek için, işte Linux uygulaması: bir karakter cihazı okunduğunda veya yazıldığında işlevleri çağırır. Yazma /dev/nullçağrılar write_null okuma çağrılar yaparken, read_null , kayıtlı buraya .

Kelimenin tam anlamıyla bir avuç kod satırları: bu işlevler hiçbir şey yapmaz. Sadece okuma ve yazma dışındaki işlevleri sayıyorsanız, elinizde parmaklarınızdan daha fazla kod satırına ihtiyacınız olacaktır.


Belki daha kesin ifade etmeliydim. Neden bir program yerine bir char cihazı olarak uygulamıştım. Her iki şekilde de birkaç satır olacaktır ancak programın uygulanması kesinlikle daha kolay olacaktır. Diğer cevapların da belirttiği gibi, bunun için birkaç fayda var; verimlilik ve taşınabilirlik aralarında şef.
Ankur S

Elbette. Bu cevabı yeni ekledim çünkü asıl uygulamanın eğlenceli olduğunu gördüm (yakın zamanda kendim keşfettim), fakat asıl sebep, başkalarının da belirttiği şeydi.
Matthieu Moy

Ben de! Geçenlerde linux cihazlarını öğrenmeye başladım ve cevaplar oldukça bilgilendiriciydi
Ankur S

-2

Umarım / dev / chargen / dev / zero ve / dev / null dahil onlar gibi diğerlerinin de farkındasınızdır.

LINUX / UNIX bunlardan birkaçına sahiptir - insanlar WELL WRITTEN CODE FrAGMEnTS'den daha iyi faydalanabilirler.

Chargen belirli ve yinelenen bir karakter örüntüsü oluşturmak için tasarlanmıştır - oldukça hızlıdır ve seri cihazların sınırlarını zorlar ve bazı testlerde veya diğer testlerde yazılan ve başarısız olan seri protokollerde hata ayıklamaya yardımcı olur.

Sıfır, mevcut bir dosyayı doldurmak veya çok fazla sıfır çıktısı almak için tasarlanmıştır.

/ dev / null, akılda aynı fikri olan başka bir araçtır.

Araç takımınızdaki bu araçların tümü, mevcut bir programın (özel gereksinimlerinize) aygıt veya dosya değişikliği olarak bakılmaksızın benzersiz bir şey yapmasını sağlama şansınız olduğunu gösterir.

LINUX sürümünüzde sadece birkaç karakter cihazı göz önüne alındığında en heyecan verici sonucu kimin üretebileceğini görmek için bir yarışma düzenleyelim.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.