Serpiştirirler! Sadece açık olmayan kısa çıkış patlamaları denediniz, ancak pratikte herhangi bir çıktının açık kalmamasını sağlamak zor.
Çıktı tamponlama
Programların çıktılarını nasıl arabelleğe aldıklarına bağlıdır . Stdio kütüphanesi onlar ediyoruz yazma kullandığı tamponlar çıktı daha verimli kılmak için zaman en programları kullanan. Program bir dosyaya yazmak için bir kütüphane işlevini çağırır çağırmaz veri çıktısı almak yerine, işlev bu verileri bir arabellekte saklar ve yalnızca arabellek dolduğunda veri çıktısı verir. Bu, çıktının gruplar halinde yapıldığı anlamına gelir. Daha doğrusu, üç çıkış modu vardır:
- Tamponsuz: veriler tampon kullanılmadan hemen yazılır. Program çıktısını küçük parçalar halinde yazarsa, yavaş yavaş olabilir, örneğin karakter karakter. Bu standart hata için varsayılan moddur.
- Tamamen arabelleğe alındı: veriler yalnızca tampon dolduğunda yazılır. Bir boruya veya normal bir dosyaya yazarken, stderr dışında bu varsayılan moddur.
- Satır arabelleği: veriler her yeni satırdan sonra veya arabellek dolduğunda yazılır. Bu, stderr dışında bir terminale yazarken varsayılan moddur.
Programlar her dosyayı farklı davranacak şekilde yeniden programlayabilir ve arabelleği açıkça temizleyebilir. Bir program dosyayı kapattığında veya normal olarak çıktığında arabellek otomatik olarak temizlenir.
Aynı boruya yazılan tüm programlar satır arabelleğe alınmış mod kullanıyorsa veya arabelleksiz mod kullanıyorsa ve her satırı bir çıkış işlevine tek bir çağrı ile yazıyorsa ve satırlar tek bir yığın halinde yazacak kadar kısaysa, o zaman çıktı tüm satırların serpiştirilmesi olacaktır. Ancak programlardan biri tamamen arabelleğe alınmış mod kullanıyorsa veya çizgiler çok uzunsa, karışık çizgiler görürsünüz.
İşte iki programdan çıktıyı araya eklediğim bir örnek. Linux'ta GNU coreutils kullandım; bu yardımcı programların farklı sürümleri farklı davranabilir.
yes aaaa
aaaa
sonsuza kadar satır ara belleğe alınmış modda eşdeğer bir şekilde yazar . Yardımcı yes
program aslında bir seferde birden fazla satır yazar, ancak her çıktı verdiğinde, çıktı bir dizi satır olur.
echo bbbb; done | grep b
bbbb
tamamen arabellek modunda sonsuza kadar yazar . 8192 arabellek boyutu kullanır ve her satır 5 bayt uzunluğundadır. 5, 8192'yi bölmediğinden, yazılar arasındaki sınırlar genel olarak bir çizgi sınırında değildir.
Onları bir araya getirelim.
$ { yes aaaa & while true; do echo bbbb; done | grep b & } | head -n 999999 | grep -e ab -e ba
bbaaaa
bbbbaaaa
baaaa
bbbaaaa
bbaaaa
bbbaaaa
ab
bbbbaaa
Gördüğünüz gibi, evet bazen grep'i kesintiye uğrattı ve tam tersi. Hatların sadece% 0.001'i kesildi, ancak oldu. Çıktı randomize edilir, böylece kesinti sayısı değişir, ancak her seferinde en az birkaç kesinti gördüm. Hatlar daha uzun olsaydı, kesikli hatların daha yüksek bir kısmı olurdu, çünkü tampon başına hat sayısı azaldıkça bir kesilme olasılığı artar.
Çıktı tamponlamasını ayarlamanın birkaç yolu vardır . Ana olanlar:
stdbuf -o0
GNU coreutils ve FreeBSD gibi diğer bazı sistemlerde bulunan programla varsayılan ayarlarını değiştirmeden stdio kütüphanesini kullanan programlarda arabelleğe almayı kapatın . Alternatif olarak ile satır arabelleğe almayı seçebilirsiniz stdbuf -oL
.
- Program çıktısını sadece bu amaçla oluşturulan bir terminal üzerinden yönlendirerek satır arabelleğe alma işlemine geçin
unbuffer
. Bazı programlar başka şekillerde farklı davranabilir, örneğin grep
çıktısı bir terminalse varsayılan olarak renkleri kullanır.
- Programı yapılandırın, örneğin
--line-buffered
GNU grep'e geçerek .
Yukarıdaki pasajı tekrar görelim, bu sefer her iki tarafta satır arabelleğe alma ile.
{ stdbuf -oL yes aaaa & while true; do echo bbbb; done | grep --line-buffered b & } | head -n 999999 | grep -e ab -e ba
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
Yani bu sefer evet asla grep'i kesintiye uğratmadı, ama grep bazen evet'i kesintiye uğrattı. Neden sonra geleceğim.
Boru serpiştirme
Her program bir seferde bir satır çıkardığı ve satırlar yeterince kısa olduğu sürece, çıkış hatları düzgün bir şekilde ayrılacaktır. Ancak, bunun çalışması için çizgilerin ne kadar sürebileceğinin bir sınırı var. Borunun kendisinde bir transfer tamponu bulunur. Bir program bir boruya çıktı verdiğinde, veriler yazar programından borunun aktarım arabelleğine ve daha sonra borunun aktarım arabelleğinden okuyucu programına kopyalanır. (En azından kavramsal olarak - çekirdek bazen bunu tek bir kopyaya göre optimize edebilir.)
Borunun aktarım arabelleğine sığacakdan daha fazla kopyalanacak veri varsa, çekirdek her seferinde bir arabelleğe kopyalar. Aynı boruya birden çok program yazıyorsa ve çekirdeğin aldığı ilk program birden fazla arabellek yazmak istiyorsa, çekirdeğin aynı programı ikinci kez tekrar seçeceğine dair bir garanti yoktur. Örneğin, P arabellek boyutu ise, foo
2 * P bayt bar
yazmak istiyor ve 3 bayt yazmak istiyorsa, o zaman bir araya ekleme olası P bayt foo
, sonra 3 bayt bar
ve P bayt'tır foo
.
Yukarıdaki yes + grep örneğine geri dönersek, sistemimde, yes aaaa
bir seferde 8192 baytlık bir ara belleğe sığabilecek kadar çok satır yazıyor. Yazmak için 5 bayt (4 yazdırılabilir karakter ve yeni satır) olduğundan, her seferinde 8190 bayt yazar. Boru tamponu boyutu 4096 bayttır. Bu nedenle evetten 4096 bayt, daha sonra grep'ten bir miktar çıktı almak ve daha sonra evet'ten yazmanın geri kalanını almak mümkündür (8190 - 4096 = 4094 bayt). 4096 bayt, 819 satır aaaa
ve yalnız bir alan bırakıyor a
. Bu a
yüzden bu yalnız ile bir çizgi ve ardından grep'ten bir yazma ile bir çizgi verir abbbb
.
Olanların ayrıntılarını görmek getconf PIPE_BUF .
istiyorsanız, sisteminizdeki boru arabellek boyutunu söyleyecek ve her program tarafından yapılan sistem çağrılarının tam bir listesini görebilirsiniz.
strace -s9999 -f -o line_buffered.strace sh -c '{ stdbuf -oL yes aaaa & while true; do echo bbbb; done | grep --line-buffered b & }' | head -n 999999 | grep -e ab -e ba
Temiz hat serpiştirme nasıl garanti edilir
Hat uzunlukları boru tampon boyutundan daha küçükse, hat tamponlaması çıktıda karışık bir çizgi olmayacağını garanti eder.
Çizgi uzunlukları daha büyük olabilirse, aynı boruya birden fazla program yazarken rastgele karıştırmayı önlemenin bir yolu yoktur. Ayrımı sağlamak için, her bir programı farklı bir boruya yazmanız ve satırları birleştirmek için bir program kullanmanız gerekir. Örneğin, GNU Parallel bunu varsayılan olarak yapar.