Not: Yanıt, bu mekanizmalar hakkındaki güncel bilgileri, bu site ve unix.stackexchange.com adresindeki akranlar tarafından araştırma ve okumalar hakkında toplanan bilgilerimi yansıtıyor ve zaman geçtikçe güncellenecek. Soru sormaktan veya yorumlarda iyileştirmeler yapmaktan çekinmeyin. Ayrıca, sistemlerin kabuk içinde strace
komutla nasıl çalıştığını görmeyi denemenizi öneririm . Ayrıca, lütfen dahili ya da sistem görüşlerini göz ardı etmeyin; kabuğun işlerin nasıl yürüdüğünü anlamak için bunları bilmek ya da kullanmak zorunda değilsiniz, ancak kesinlikle anlamanıza yardımcı olurlar.
TL; DR
|
Borular, diskteki bir girişle ilişkilendirilmez, bu nedenle inode disk sayısı yoktur (ancak çekirdek alanındaki pipefs sanal dosya sisteminde inode vardır ), ancak yönlendirmeler genellikle disk girişleri olan ve bu nedenle karşılık gelen dosyaları içerir. inode.
- Borular
lseek()
mümkün değildir, bu nedenle komutlar bazı verileri okuyamaz ve sonra geri sarabilir, ancak yönlendirme yaptığınızda >
veya <
genellikle lseek()
nesnenin nesnesi olan bir dosya olduğunda , komutlar istedikleri şekilde gezinebilir.
- yönlendirmeler, dosya tanımlayıcıları üzerinde yapılan ve birçok olabilir; Borular yalnızca iki dosya tanımlayıcısına sahiptir - biri sol komut için diğeri sağ komut için
- Standart akışlarda ve borularda yeniden yönlendirme hem tamponlanır.
- Borular neredeyse her zaman çatallaşmayı içerir ve bu nedenle işlem çiftleri söz konusudur; yönlendirmeleri - her zaman değil, sonuçta her iki durumda da sonuçta ortaya çıkan dosya tanımlayıcıları alt işlemler tarafından miras alınır.
- Borular her zaman dosya tanımlayıcılarını (bir çift) bağlar, yönlendirmeler - bir yol adı veya dosya tanımlayıcıları kullanır.
- Borular İşlemler Arası İletişim yöntemidir, yönlendirmeler yalnızca açık dosyalar veya dosyaya benzer nesneler üzerinde yapılan işlemlerdir
- her ikisi de
dup2()
, gerçek veri akışının gerçekleştiği dosya tanımlayıcılarının kopyalarını sağlamak için kaputun altında sistemler kullanır .
- yönlendirmeler ile "global" uygulanabilir
exec
yerleşik komutu (bkz bu ve bu ), bunu yaparsanız bu yüzden exec > output.txt
her komut için yazacak output.txt
o andan itibaren. |
Borular yalnızca geçerli komut için uygulanır (bu, basit komut veya alt kabuk benzeri seq 5 | (head -n1; head -n2)
veya bileşik komutlar anlamına gelir) .
Dosyalara yönlendirme yapıldığında, gibi şeyler echo "TEST" > file
ve echo "TEST" >> file
her ikisi de open()
bu dosya üzerinde syscall kullanır ( ayrıca bakınız ) ve dosya tanımlayıcısını iletmek için bu dosyadan dup2()
. Borular |
sadece pipe()
ve dup2()
çağrı kullanır .
Komutlar uygulandığında, yöneltmeler ve yeniden yönlendirme, dosya tanımlayıcılarından - kendilerine körce yazabilecekleri veya içsel olarak manipüle edebilecekleri (beklenmeyen davranışlar üretebilecek; apt
örneğin stdout'a bile yazamayacakları) dosya tanıtıcılarından başka bir şey değildir. eğer yönlendirme olduğunu biliyorsa).
Giriş
Bu iki mekanizmanın nasıl farklılaştığını anlamak için, temel özelliklerini, ikisinin ardındaki geçmişi ve C programlama dilindeki köklerini anlamak gerekir. Aslında, hangi dosya tanımlayıcılarının ne olduğunu dup2()
ve pipe()
sistemin nasıl işlediğini ve nasıl çalıştığını bilmek de önemlidir lseek()
. Shell, bu mekanizmaları kullanıcıya soyut ettirmenin bir yolu olarak ifade edilir, ancak soyutlamanın daha derine inmesi, kabuğun davranışının gerçek doğasını anlamaya yardımcı olur.
Yönlendirme ve Boruların Kökenleri
Dennis Ritche'nin Prophetic Petroglyphs adlı makalesine göre , borular , Multics işletim sistemi üzerinde çalıştıkları sırada Malcolm Douglas McIlroy tarafından 1964 tarihli bir nottan kaynaklanıyordu . Alıntı:
En güçlü endişelerimi kısaca özetlemek için:
- Verilere başka bir şekilde masaj yapılması gerektiğinde, bahçe hortumu - vidası gibi programları bağlamada başka yollar kullanmalıyız. Bu da IO'nun yoludur.
Açıkça görüldüğü gibi, programların o zamanlar diske yazabildikleri, ancak çıktının büyük olması durumunda verimsiz olduğu söylenebilir. Brian Kernighan'ın Unix Pipeline videosundaki açıklamasını alıntı yapmak için :
Öncelikle, büyük bir büyük program yazmak zorunda değilsiniz - zaten işin bir kısmını yapabilecek daha küçük programlarınız var ... Bir başkası, işlediğiniz veri miktarının uygun olmaması durumunda bir dosyada sakladınız ... çünkü hatırlayın, bu şeylerle ilgili disklerin olduğu günlerde geri döndük, eğer şanslıysanız, bir Megabayt ya da iki veri ... .
Böylece kavramsal fark belirgindir: borular, programların birbirleriyle konuşmasını sağlayan bir mekanizmadır. Yönlendirmeler - temel düzeyde dosyaya yazma yoludur. Her iki durumda da, kabuk bu iki şeyi kolaylaştırır, ancak kaputun altında çok şey oluyor.
Daha derine inmek: Sistemler ve kabuğun iç işleyişi
Dosya tanımlayıcı kavramıyla başlıyoruz . Dosya tanımlayıcıları temel olarak bir tam sayı ile temsil edilen açık bir dosyayı (diskte veya bellekte veya anonim dosyada olup olmadığını) tanımlar. İki standart veri akışı (stdin, stdout, stderr) sırasıyla dosya tanımlayıcıları 0,1 ve 2'dir. Onlar nereden geliyor ? Eh, kabuk komutlarında, dosya tanımlayıcıları üst kabuklarından miras alınır. Ve genel olarak tüm işlemler için doğrudur - alt süreç ebeveynin dosya tanımlayıcılarını devralır. İçin cinleri hepsi kalıtsal dosya tanımlayıcıları kapatmak ve / veya başka yerlere yönlendirmek için ortaktır.
Yeniden yönlendirmeye dön. Gerçekte ne Kabuğa komut için dosya tanımlayıcıları hazırlamalarını söyleyen bir mekanizmadır (çünkü komutlar çalıştırılmadan önce kabuk tarafından yapılır) ve kullanıcının önerdiği yere işaret eder. Standart tanımlı çıkış yönlendirme ait
[n]>word
Bu [n]
dosya tanıtıcı numarası vardır. Bunu yaptığınızda echo "Something" > /dev/null
1 sayısı orada ima edilir ve echo 2> /dev/null
.
Kaputun altında, bu dosya tanımlayıcısını dup2()
sistem çağrısı yoluyla kopyalayarak yapılır . Hadi alalım df > /dev/null
. Kabuk bir çocuk süreç yaratacak df
çalışır, ancak ondan önce de açılacaktır /dev/null
dosya tanıtıcı 3. olarak ve dup2(3,1)
dosya tanımlayıcı 3 bir kopyasını yapar verilecektir ve kopya Sen iki dosya var biliyorum 1. olacak file1.txt
ve file2.txt
ve ne zaman cp file1.txt file2.txt
iki tane aynı dosyaya sahip olacaksın, ama onları bağımsız olarak değiştirebilirsin. Burada da aynı şeyler oluyor. Genellikle, çalıştırmadan önce, daha sonra geri yüklemek amacıyla (ve bu kopya fd # 10 olacaktır) bir kopya dosyası tanımlayıcı # 1 bash
yapmak dup(1,10)
için yapacağını görebilirsiniz stdout
. Önemli olan, yerleşik komutları göz önüne aldığınızda(kabuğun kendisinin bir parçasıdır ve içinde /bin
veya başka bir yerde dosya yoktur ) veya etkileşimli olmayan kabuklarda basit komutlar , kabuk bir alt süreç oluşturmaz.
Ve sonra [n]>&[m]
ve gibi şeyler var [n]&<[m]
. Bu, dup2()
sadece şu anda olduğu gibi aynı mekanizmanın kullanıcı için uygun olan kabuk sözdizimindeki mekanizmasını kopyalayan dosya tanımlayıcılarıdır .
Yeniden yönlendirme hakkında dikkat edilmesi gereken önemli noktalardan biri, sıralarının sabit olmadığı, ancak kabukta kullanıcının ne istediğini yorumlaması açısından önemlidir. Aşağıdakileri karşılaştırın:
# Make copy of where fd 2 points , then redirect fd 2
$ ls -l /proc/self/fd/ 3>&2 2> /dev/null
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
lrwx------ 1 runner user 64 Sep 13 00:08 3 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/29/fd
# redirect fd #2 first, then clone it
$ ls -l /proc/self/fd/ 2> /dev/null 3>&2
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
l-wx------ 1 user user 64 Sep 13 00:08 3 -> /dev/null
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/31/fd
Bunların kabuk komut dosyalarında pratik kullanımı çok yönlü olabilir:
ve diğerleri.
Sıhhi tesisat pipe()
vedup2()
Peki borular nasıl oluşturulur? Via pipe()
syscall girdi olarak adlandırılan bir dizi (aka liste) alacak pipefd
türden iki maddeden int
(tamsayı). Bu iki tamsayı, dosya tanımlayıcılarıdır. pipefd[0]
Borunun okunan sonu olacak ve pipefd[1]
yazma sonu olacak. Yani içinde df | grep 'foo'
, grep
bir kopyasını alacaktır pipefd[0]
ve df
bir kopyasını alacaktır pipefd[1]
. Ama nasıl ? Tabii ki, dup2()
çağrı büyüsü ile . For df
örneğimizde, diyelim ki pipefd[1]
# 4 vardır, bu nedenle kabuk, Çocuk yapmaya yapacak dup2(4,1)
(benim hatırlamak cp
örnek?) Ve daha sonra yapılacak execve()
aslında çalıştırmak için df
. Doğal olarak,df
# 1 numaralı dosya tanımlayıcısını devralacak, ancak terminalde artık işaret etmediğinin farkında değil, aslında fd # 4, ki bu borunun aslında yazma ucu. Doğal olarak, grep 'foo'
farklı dosya tanımlayıcıları dışında da aynı şey gerçekleşecek .
Şimdi, ilginç soru: fd # 2'ye yön veren borular yapabilir miyiz, sadece fd # 1'i değil mi? Evet, aslında |&
bash böyle yapar. POSIX standardı, df 2>&1 | grep 'foo'
bu amaç için sözdizimini desteklemek için kabuk komut dili gerektirir , ancak aynı zamanda bash
bunu |&
da yapar .
Unutulmaması gereken nokta, boruların her zaman dosya tanımlayıcıları ile ilgilenmesidir. Diskte bir dosya adı olan ve dosya olarak kullanmanıza izin veren, ancak bir boru gibi davranan, adlandırılmış bir boru var FIFO
ya da var . Ancak, boru türleri isimsiz boru olarak bilinir - dosya adı yoktur, çünkü bunlar birbirine bağlanan sadece iki nesnedir. Dosyalarla uğraşmadığımız gerçeği de önemli bir sonuçtur: borular mümkün değildir . Bellekteki veya diskteki dosyalar statiktir - programlar bayt 120'ye, ardından bayt 10'a atlamak ve sonra sonuna kadar ileri gitmek için sistem çağrısı kullanabilir . Borular statik değildir - sıralıdır ve bu nedenle onlardan aldığınız verileri geri alamazsınız.|
lseek()
lseek()
lseek()
. Bu, bazı programları dosyadan mı yoksa borudan mı okuduklarının farkında olmalarını sağlar ve böylece verimli performans için gerekli ayarlamaları yapabilir; Başka bir deyişle, prog
eğer yaparsam cat file.txt | prog
veya algılayabilir prog < input.txt
. Bunun gerçek iş örneği kuyruktur .
Boruların diğer iki ilginç özelliği , Linux'ta 4096 byte olan bir arabelleklerinin olması ve aslında Linux kaynak kodunda tanımlandığı şekilde bir dosya sistemine sahip olmaları ! Onlar sadece veri iletmek için bir nesne değiller, kendileri bir veri yapıları! Aslında, hem boruları hem de FIFO'ları yöneten pipefs dosya sistemi bulunduğundan, borular kendi dosya sistemlerinde inode numarasına sahiptir:
# Stdout of ls is wired to pipe
$ ls -l /proc/self/fd/ | cat
lrwx------ 1 user user 64 Sep 13 00:02 0 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:02 1 -> pipe:[15655630]
lrwx------ 1 user user 64 Sep 13 00:02 2 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:02 3 -> /proc/22/fd
# stdin of ls is wired to pipe
$ true | ls -l /proc/self/fd/0
lr-x------ 1 user user 64 Sep 13 03:58 /proc/self/fd/0 -> 'pipe:[54741]'
Linux'ta borular tek yönlüdür, tıpkı yönlendirme gibi. Unix benzeri bazı uygulamalarda - çift yönlü borular vardır. Her ne kadar kabuk komut dosyası sihri ile , Linux'ta da çift yönlü borular yapabilirsiniz .
Ayrıca bakınız:
thing1 > temp_file && thing2 < temp_file
Borularla daha kolay yapmaktan bahsettin . Fakat neden>
operatörü bunu yapmak için tekrar kullanmıyorsunuz , örneğinthing1 > thing2
komutlarthing1
vething2
? Neden ekstra bir operatör|
?