पाइप बफर कितना बड़ा है?


146

एक टिप्पणी के रूप में मैं उलझन में हूँ कि क्यों "| ट्रू" को मेकफाइल में "" सत्य " उपयोगकर्ता " के रूप में एक ही प्रभाव है :

बचने का दूसरा कारण | यह सच है कि यदि कमांड पाइप बफर को भरने के लिए पर्याप्त आउटपुट का उत्पादन करता है, तो यह इसे पढ़ने के लिए सही प्रतीक्षा कर रहा है।

क्या हमारे पास यह पता लगाने का कोई तरीका है कि पाइप बफर का आकार क्या है?

जवाबों:


142

एक पाइप बफर की क्षमता पूरे सिस्टम में भिन्न होती है (और एक ही सिस्टम पर भिन्न भी हो सकती है)। मुझे यकीन नहीं है कि एक पाइप की क्षमता को देखने के लिए एक त्वरित, आसान और क्रॉस प्लेटफॉर्म तरीका है।

उदाहरण के लिए, Mac OS X, डिफ़ॉल्ट रूप से 16384 बाइट्स की क्षमता का उपयोग करता है, लेकिन यदि 65% पाइप में बड़े लेखन हो जाते हैं, तो 65336 बाइट कैपेसिटी पर स्विच कर सकते हैं, या यदि बहुत कर्नेल मेमोरी पहले से ही है तो सिंगल सिस्टम पेज की क्षमता पर स्विच करेंगे। पाइप बफ़र्स द्वारा उपयोग किया जा रहा है (देखें xnu/bsd/sys/pipe.h, और xnu/bsd/kern/sys_pipe.c; चूंकि ये FreeBSD से हैं, वही व्यवहार वहां भी हो सकता है)।

एक लिनक्स पाइप (7) मैन पेज का कहना है कि लिनक्स 2.6.11 के बाद से पाइप की क्षमता 65536 है और इससे पहले एक एकल सिस्टम पेज (जैसे 4096 बाइट्स पर (32-बिट) x86 सिस्टम)। कोड ( include/linux/pipe_fs_i.hऔर fs/pipe.c) 16 सिस्टम पेजों का उपयोग करने लगता है (यानी 64 KiB अगर एक सिस्टम पेज 4 KiB है), लेकिन प्रत्येक पाइप के लिए बफर को पाइप पर एक fcntl (अधिकतम क्षमता तक) जो 1048576 तक चूकता है, के माध्यम से समायोजित किया जा सकता है बाइट्स, लेकिन के माध्यम से बदला जा सकता है /proc/sys/fs/pipe-max-size))।


यहाँ थोड़ा बैश / पर्ल संयोजन है जिसका उपयोग मैंने अपने सिस्टम पर पाइप क्षमता का परीक्षण करने के लिए किया था:

#!/bin/bash
test $# -ge 1 || { echo "usage: $0 write-size [wait-time]"; exit 1; }
test $# -ge 2 || set -- "$@" 1
bytes_written=$(
{
    exec 3>&1
    {
        perl -e '
            $size = $ARGV[0];
            $block = q(a) x $size;
            $num_written = 0;
            sub report { print STDERR $num_written * $size, qq(\n); }
            report; while (defined syswrite STDOUT, $block) {
                $num_written++; report;
            }
        ' "$1" 2>&3
    } | (sleep "$2"; exec 0<&-);
} | tail -1
)
printf "write size: %10d; bytes successfully before error: %d\n" \
    "$1" "$bytes_written"

यहाँ मुझे मैक ओएस एक्स 10.6.7 सिस्टम पर विभिन्न लेखन आकारों के साथ इसे चलाते हुए पाया गया है (16KBB से बड़ा लिखने के लिए परिवर्तन पर ध्यान दें):

% /bin/bash -c 'for p in {0..18}; do /tmp/ts.sh $((2 ** $p)) 0.5; done'
write size:          1; bytes successfully before error: 16384
write size:          2; bytes successfully before error: 16384
write size:          4; bytes successfully before error: 16384
write size:          8; bytes successfully before error: 16384
write size:         16; bytes successfully before error: 16384
write size:         32; bytes successfully before error: 16384
write size:         64; bytes successfully before error: 16384
write size:        128; bytes successfully before error: 16384
write size:        256; bytes successfully before error: 16384
write size:        512; bytes successfully before error: 16384
write size:       1024; bytes successfully before error: 16384
write size:       2048; bytes successfully before error: 16384
write size:       4096; bytes successfully before error: 16384
write size:       8192; bytes successfully before error: 16384
write size:      16384; bytes successfully before error: 16384
write size:      32768; bytes successfully before error: 65536
write size:      65536; bytes successfully before error: 65536
write size:     131072; bytes successfully before error: 0
write size:     262144; bytes successfully before error: 0

लिनक्स 3.19 पर एक ही स्क्रिप्ट:

/bin/bash -c 'for p in {0..18}; do /tmp/ts.sh $((2 ** $p)) 0.5; done'
write size:          1; bytes successfully before error: 65536
write size:          2; bytes successfully before error: 65536
write size:          4; bytes successfully before error: 65536
write size:          8; bytes successfully before error: 65536
write size:         16; bytes successfully before error: 65536
write size:         32; bytes successfully before error: 65536
write size:         64; bytes successfully before error: 65536
write size:        128; bytes successfully before error: 65536
write size:        256; bytes successfully before error: 65536
write size:        512; bytes successfully before error: 65536
write size:       1024; bytes successfully before error: 65536
write size:       2048; bytes successfully before error: 65536
write size:       4096; bytes successfully before error: 65536
write size:       8192; bytes successfully before error: 65536
write size:      16384; bytes successfully before error: 65536
write size:      32768; bytes successfully before error: 65536
write size:      65536; bytes successfully before error: 65536
write size:     131072; bytes successfully before error: 0
write size:     262144; bytes successfully before error: 0

नोट: PIPE_BUFसी हेडर फ़ाइलों में परिभाषित मूल्य (और के लिए pathconf मूल्य _PC_PIPE_BUF), पाइप की क्षमता को निर्दिष्ट नहीं करता है, लेकिन बाइट्स की अधिकतम संख्या जिसे परमाणु लिखा जा सकता है (देखें POSIX लिखना (2) )।

उद्धरण include/linux/pipe_fs_i.h:

/* Differs from PIPE_BUF in that PIPE_SIZE is the length of the actual
   memory allocation, whereas PIPE_BUF makes atomicity guarantees.  */

14
बहुत बढ़िया जवाब। विशेष रूप से POSIX लिखने के लिंक के लिए (2), जो कहता है: एक पाइप या FIFO का प्रभावी आकार (अधिकतम राशि जो बिना किसी अवरोध के एक ऑपरेशन में लिखी जा सकती है) कार्यान्वयन के आधार पर गतिशील रूप से भिन्न हो सकती है, इसलिए यह संभव नहीं है इसके लिए एक निश्चित मान निर्दिष्ट करने के लिए।
मिकेल

5
fcntl()लिनक्स पर उल्लेख करने के लिए धन्यवाद ; मैंने कुछ समय यूज़र्सस्पेस बफरिंग प्रोग्राम की तलाश में बिताया था क्योंकि मुझे लगा कि बिल्ट-इन पाइप्स में पर्याप्त बड़ा बफर नहीं था। अब मैं देखता हूं कि वे करते हैं, अगर मेरे पास CAP_SYS_RESOURCE या रूट अधिकतम पाइप आकार का विस्तार करने के लिए तैयार है। जैसा कि मैं चाहता हूं कि केवल एक विशिष्ट लिनक्स कंप्यूटर (मेरा) पर चलाया जाएगा, यह एक समस्या नहीं होनी चाहिए।
डैनियल एच

1
क्या आप कृपया अपनी लिपि के मूल विचार की व्याख्या कर सकते हैं? मैं इसे घूर रहा हूं और मैं यह पता नहीं लगा सकता कि यह कैसे काम करता है? विशेष रूप से यहाँ VAR = $ ({}) घुंघराले कोष्ठक का उपयोग करने का क्या उद्देश्य है? धन्यवाद।
वकन तन्खा

@WakanTanka: यह एक बिट एक टिप्पणी में वर्णन करने के लिए बहुत है, लेकिन यह विशेष रूप से निर्माण एक है पैरामीटर काम ( var=…एक के उत्पादन के) आदेश प्रतिस्थापन ( $(…)) भी शामिल समूहीकृत आदेशों ( {…}और (…))। यह कई (कम आम) पुनर्निर्देशन (यानी 0<&-और 3>&1) का भी उपयोग करता है ।
क्रिस जॉन्सन

2
@WakanTanka: पर्ल प्रोग्राम किसी दिए गए आकार के ब्लॉक में अपने stdout (एक शेल-निर्मित पाइप- जिसे परीक्षण किया जा रहा है) को लिखता है और अपने स्टडर को रिपोर्ट करता है कि उसने कितना लिखा है (जब तक कि उसे एक त्रुटि नहीं मिलती है - आमतौर पर क्योंकि पाइप का बफर भरा हुआ है या संभवतः इसलिए क्योंकि पाइप का रीडिंग एंड थोड़े समय ( exec 0<&-)) के बाद बंद हो गया है । अंतिम रिपोर्ट एकत्र की जाती है ( tail -1) और लेखन आकार के साथ मुद्रित की जाती है।
क्रिस जॉन्सन

33

यह शेल-लाइन पाइप बफर आकार भी दिखा सकता है:

M=0; while true; do dd if=/dev/zero bs=1k count=1 2>/dev/null; \
       M=$(($M+1)); echo -en "\r$M KB" 1>&2; done | sleep 999

(बफर पूर्ण होने तक अवरुद्ध पाइप को 1k चंक्स भेजना) ... कुछ परीक्षण आउटपुट:

64K (intel-debian), 32K (aix-ppc), 64K (jslinux bellard.org)      ...Ctrl+C.

प्रिंटफ का उपयोग करते हुए सबसे छोटा बैश-वन-लाइनर:

M=0; while printf A; do >&2 printf "\r$((++M)) B"; done | sleep 999

11
बहुत अच्छा! (dd if=/dev/zero bs=1 | sleep 999) &फिर एक दूसरा प्रतीक्षा करें और killall -SIGUSR1 ddदेता है 65536 bytes (66 kB) copied, 5.4987 s, 11.9 kB/s); - अपने समाधान के रूप में ही है, लेकिन 1 बाइट संकल्प पर
frostschutz

2
रिकॉर्ड के लिए, सोलारिस 10/11 SPARC / x86 ddपर 16 KiB पर कमांड ब्लॉक। फेडोरा 23/25 x86-64 पर, यह 64 KiB पर ब्लॉक करता है।
अधिकतमस्लेपजिग

1
@frostschutz: यह एक अच्छा सरलीकरण है। व्यावहारिक रूप से, आप सिर्फ dd if=/dev/zero bs=1 | sleep 999अग्रभूमि में दौड़ सकते हैं , एक सेकंड प्रतीक्षा करें, फिर दबाएं ^C। यदि आप लिनक्स और BSD / killalldd if=/dev/zero bs=1 | sleep 999 & sleep 1 && pkill -INT -P $$ -x dd
macOS

7

केवल शेल कमांड का उपयोग करके वास्तविक पाइप बफर क्षमता का पता लगाने के लिए कुछ और विकल्प दिए गए हैं:

# get pipe buffer size using Bash
yes produce_this_string_as_output | tee >(sleep 1) | wc -c

# portable version
( (sleep 1; exec yes produce_this_string_as_output) & echo $! ) | 
     (pid=$(head -1); sleep 2; kill "$pid"; wc -c </dev/stdin)

# get buffer size of named pipe
sh -c '
  rm -f fifo
  mkfifo fifo
  yes produce_this_string_as_output | tee fifo | wc -c &
  exec 3<&- 3<fifo
  sleep 1
  exec 3<&-
  rm -f fifo
'

# Mac OS X
#getconf PIPE_BUF /
#open -e /usr/include/limits.h /usr/include/sys/pipe.h
# PIPE_SIZE
# BIG_PIPE_SIZE
# SMALL_PIPE_SIZE
# PIPE_MINDIRECT

सोलारिस 10 पर, getconf PIPE_BUF /प्रिंट 5120जो ulimit -a | grep pipeआउटपुट से मेल खाता है, लेकिन कौन से dd .. | sleep ...ब्लॉक के बाद 16 KiB से मेल नहीं खाता है ।
मैक्सक्लेपजिग

फेडोरा 25 पर, आपकी पहली yesविधि 7372864 KiB के बजाय प्रिंट करती हैdd if=/dev/zero bs=4096 status=none | pv -bn | sleep 1
अधिकतम 30

6

यह Ubuntu 12.04, YMMV पर एक त्वरित और गंदा हैक है

cat >pipesize.c

#include <unistd.h>
#include <errno.h>
#include </usr/include/linux/fcntl.h>
#include <stdio.h>

void main( int argc, char *argv[] ){
  int fd ;
  long pipesize ;

  if( argc>1 ){
  // if command line arg, associate a file descriptor with it
    fprintf( stderr, "sizing %s ... ", argv[1] );
    fd = open( argv[1], O_RDONLY|O_NONBLOCK );
  }else{
  // else use STDIN as the file descriptor
    fprintf( stderr, "sizing STDIN ... " );
    fd = 0 ;
  }

  fprintf( stderr, "%ld bytes\n", (long)fcntl( fd, F_GETPIPE_SZ ));
  if( errno )fprintf( stderr, "Uh oh, errno is %d\n", errno );
  if( fd )close( fd );
}

gcc -o pipesize pipesize.c

mkfifo /tmp/foo

./pipesize /tmp/foo

>sizing /tmp/foo ... 65536 bytes

date | ./pipesize

>sizing STDIN ... 65536 bytes

0
$ ulimit -a | grep pipe
pipe size            (512 bytes, -p) 8

इसलिए मेरे लिनक्स बॉक्स पर डिफ़ॉल्ट रूप से मेरे पास 8 * 512 = 4096 बाइट पाइप हैं।

सोलारिस और कई अन्य प्रणालियों में एक समान ulimit फ़ंक्शन होता है।


2
यह (512 bytes, -p) 8फेडोरा 23/25 और 512 bytes, -p) 10सोलारिस 10 पर प्रिंट करता है - और वे मान बफर के आकारों से मेल नहीं खाते हैं जो एक ब्लॉकिंग के साथ प्रयोगात्मक रूप से प्राप्त हुए हैं dd
मैक्सक्लेपज़िग

0

यदि आपको Python> = 3.3 में मान की आवश्यकता है, तो यहां एक सरल विधि है (यह मानते हुए कि आप कॉल आउट चला सकते हैं dd):

from subprocess import Popen, PIPE, TimeoutExpired
p = Popen(["dd", "if=/dev/zero", "bs=1"], stdin=PIPE, stdout=PIPE)
try: 
    p.wait(timeout=1)
except TimeoutExpired: 
    p.kill()
    print(len(p.stdout.read()))
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.