कुछ गोले `रीड` बिलिन फ़ाइल की पूरी लाइन को` / प्रोक` में पढ़ने में विफल क्यों हैं?


19

कुछ बॉर्न-जैसे गोले में, readबिलिन फाइल में से पूरी लाइन नहीं पढ़ सकता है /proc(नीचे दी गई कमांड को चलाया जाना चाहिए zsh, अन्य शेल के साथ प्रतिस्थापित $=shellकरें $shell):

$ for shell in bash dash ksh mksh yash zsh schily-sh heirloom-sh "busybox sh"; do
  printf '[%s]\n' "$shell"
  $=shell -c 'IFS= read x </proc/sys/fs/file-max; echo "$x"'       
done
[bash]
602160
[dash]
6
[ksh]
602160
[mksh]
6
[yash]
6
[zsh]
6
[schily-sh]
602160
[heirloom-sh]
602160
[busybox sh]
6

readमानक के लिए मानक इनपुट की आवश्यकता होती है एक पाठ फ़ाइल होना चाहिए , क्या यह आवश्यकता विभिन्न व्यवहारों का कारण बनती है?


पाठ फ़ाइल की POSIX परिभाषा पढ़ें , मैं कुछ सत्यापन करता हूं:

$ od -t a </proc/sys/fs/file-max 
0000000   6   0   2   1   6   0  nl
0000007

$ find /proc/sys/fs -type f -name 'file-max'
/proc/sys/fs/file-max

की NULसामग्री में कोई चरित्र नहीं है /proc/sys/fs/file-max, और findइसे एक नियमित फ़ाइल के रूप में भी रिपोर्ट किया है (क्या यह एक बग है find?)।

मुझे लगता है कि खोल ने हुड के नीचे कुछ किया था, जैसे file:

$ file /proc/sys/fs/file-max
/proc/sys/fs/file-max: empty

जवाबों:


31

समस्या यह है कि /procलिनक्स पर वे फ़ाइलें जहाँ तक पाठ फ़ाइलें दिखाई देती हैंstat()/fstat() संबंधित है, लेकिन ऐसा व्यवहार न करें।

क्योंकि यह गतिशील डेटा है, आप केवल एक read()सिस्टम कॉल उन पर कर सकते हैं (उनमें से कुछ के लिए कम से कम)। एक से अधिक काम करने से आपको दो अलग-अलग सामग्री के दो टुकड़े मिल सकते हैं, इसलिए इसके बजाय यह read()उन पर एक सेकंड लगता है बस कुछ भी नहीं लौटाता है (मतलब अंत-फ़ाइल) (जब तक आप नहींlseek() शुरुआत में वापस (केवल शुरुआत में)।

readउपयोगिता अतीत न्यू लाइन चरित्र को पढ़ने के लिए यकीन नहीं होने के लिए एक समय में एक बाइट फ़ाइलों की सामग्री को पढ़ने के लिए की जरूरत है। यही dashहै:

 $ strace -fe read dash -c 'read a < /proc/sys/fs/file-max'
 read(0, "1", 1)                         = 1
 read(0, "", 1)                          = 0

कुछ शेल्स की तरह bashएक अनुकूलन है ताकि कई read()सिस्टम कॉल करने से बचें । वे पहले जांचते हैं कि क्या फ़ाइल खोजने योग्य है, और यदि ऐसा है, तो विखंडू में पढ़ें क्योंकि उन्हें पता है कि वे कर्सर को नई पंक्ति के ठीक बाद वापस रख सकते हैं यदि उन्होंने इसे पढ़ा है:

$ strace -e lseek,read bash -c 'read a' < /proc/sys/fs/file-max
lseek(0, 0, SEEK_CUR)                   = 0
read(0, "1628689\n", 128)               = 8

इसके साथ bash, आपको अभी भी उन फ़ाइलों के लिए समस्याएँ होंगी जो 128 बाइट्स से अधिक बड़ी हैं और केवल एक रीड सिस्टम कॉल में ही पढ़ी जा सकती हैं।

bash-dविकल्प का उपयोग करने पर उस अनुकूलन को अक्षम करने के लिए भी लगता है।

ksh93अनुकूलन को इतना आगे ले जाता है कि फर्जी बन जाता है। ksh93 readवापस नहीं चाहता है, लेकिन यह अगले के लिए पढ़े गए अतिरिक्त डेटा को याद रखता है read, इसलिए अगला read(या इसके किसी भी अन्य निर्माण जो डेटा को पढ़ता है जैसे catया head) डेटा की कोशिश भी नहीं करता है read(भले ही उस डेटा को संशोधित किया गया हो बीच में अन्य कमांड):

$ seq 10 > a; ksh -c 'read a; echo test > a; read b; echo "$a $b"' < a
1 2
$ seq 10 > a; sh -c 'read a; echo test > a; read b; echo "$a $b"' < a
1 st

आह हाँ, एक- straceआधारित व्याख्या को समझना बहुत आसान है!
स्टीफन किट

धन्यवाद, गतिशील डेटा समझ में आता है। तो शेल कैसे पता लगाता है कि यह गतिशील डेटा है? अगर मैं करता हूं cat /proc/sys/fs/file-max | ..., तो समस्या चली गई।
कोउंगलम

3
शेल इसका पता नहीं लगाता है। यह तथ्य कि यह गतिशील डेटा है, इसका मतलब है कि एक ही फ़ाइल में procfsकई क्रमिक read(2)कॉल को संभाल नहीं सकता है ; व्यवहार शेल पर निर्भर नहीं करता है। का उपयोग करना catऔर पाइपिंग काम करता है क्योंकि catफ़ाइल को बड़े पैमाने पर पर्याप्त मात्रा में पढ़ा जाता है; शेल का readबिल्ट-इन तब पाइप से एक बार में एक कैरेक्टर को पढ़ता है।
स्टीफन किट

1
इसमें थोड़ा गंदा वर्कअराउंड है mkshread -N 10 a < /proc/sys/fs/file-max
इपोर सिरकार

1
@IporSircer। वास्तव में। चारों ओर एक समान काम के साथ काम करने लगता है zsh: read -u0 -k10(या उपयोग नहीं करता sysread; $mapfile[/proc/sys/fs/file-max]उन फ़ाइलों के रूप में काम नहीं किया जा सकता है mmap)। किसी भी मामले में, किसी भी शेल के साथ, कोई भी हमेशा कर सकता है a=$(cat /proc/sys/fs/file-max)। कुछ सहित mksh, zshऔर ksh93, a=$(</proc/sys/fs/file-max)यह भी काम करता है और रीडिंग करने के लिए एक प्रक्रिया को कांटा नहीं करता है।
स्टीफन चेजलस

9

यदि आप यह जानने में रुचि रखते हैं कि क्यों? ऐसा है, आप यहाँ कर्नेल स्रोतों में उत्तर देख सकते हैं :

    if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
            *lenp = 0;
            return 0;
    }

मूल रूप से, मांग ( *ppos0 नहीं) !writeसंख्याओं वाले sysctl मानों के रीड्स ( ) के लिए लागू नहीं की जाती है। जब भी कोई रीड किया जाता है /proc/sys/fs/file-max, तो कॉन्फ़िगरेशन तालिका के__do_proc_doulongvec_minmax() लिए प्रविष्टि से प्रश्न को दिनचर्या कहा जाता हैfile-max उसी फ़ाइल में में ।

अन्य प्रविष्टियाँ, जैसे कि /proc/sys/kernel/poweroff_cmdकार्यान्वित की जाती हैं , proc_dostring()जिसके माध्यम से अनुमति देता है, ताकि आप कर सकेंdd bs=1 पर और बिना किसी समस्या के अपने शेल से पढ़ ।

ध्यान दें कि कर्नेल के बाद से 2.6 सबसे अधिक /procपढ़े गए नए API के माध्यम से seq_file नाम से लागू किया गया था और यह समर्थन का समर्थन करता है, इसलिए जैसे पढ़ना /proc/statसमस्याओं का कारण नहीं होना चाहिए। /proc/sys/कार्यान्वयन, जैसा कि हम देख सकते हैं, इस API का उपयोग नहीं करता है।


3

पहले प्रयास में, यह गोले में एक बग की तरह दिखता है जो वास्तविक बॉर्न शेल या उसके डेरिवेटिव रिटर्न (श, बोश, केश, हिरलूम) से कम होता है।

मूल बॉर्न शेल एक ब्लॉक (64 बाइट्स) को पढ़ने की कोशिश करता है नए बॉर्न शेल वेरिएंट 128 बाइट्स पढ़ते हैं, लेकिन नई लाइन वर्ण नहीं होने पर वे फिर से पढ़ना शुरू करते हैं।

पृष्ठभूमि: / procfs और समान कार्यान्वयन (जैसे माउंटेड /etc/mtabवर्चुअल फ़ाइल) में डायनामिक सामग्री होती है और एक stat()कॉल पहले डायनामिक सामग्री के फिर से निर्माण का कारण नहीं बनता है । इस कारण से, ऐसी फ़ाइल का आकार (EOF तक पढ़ने से) क्या stat()रिटर्न से भिन्न हो सकता है।

यह देखते हुए कि पोसिक्स मानक को किसी भी समय कम रीड की अपेक्षा करने के लिए उपयोगिताओं की आवश्यकता होती है , सॉफ्टवेयर जो मानता है कि बाइट्स read()की ऑर्डर की गई राशि से कम रिटर्न एक ईओएफ संकेत टूट गया है। एक सही तरीके से लागू की गई उपयोगिता read()दूसरी बार कॉल करती है, जब तक कि वह अपेक्षा से कम न हो - जब तक कि 0 वापस न आए। के मामले में readनिर्मित, यह निश्चित रूप से जब तक पढ़ने के लिए पर्याप्त होगा EOF या जब तक एक NLदेखा जाता है।

यदि आप चलते हैं trussया ट्रस क्लोन करते हैं, तो आपको उन शेल के लिए गलत व्यवहार को सत्यापित करने में सक्षम होना चाहिए जो केवल 6आपके प्रयोग में वापस आते हैं ।

इस विशेष मामले में, यह एक लिनक्स कर्नेल बग लगता है, देखें:

$ sdd -debug bs=1 if= /proc/sys/fs/file-max 
Simple copy ...
readbuf  (3, 12AC000, 1) = 1
writebuf (1, 12AC000, 1)
8readbuf  (3, 12AC000, 1) = 0

sdd: Read  1 records + 0 bytes (total of 1 bytes = 0.00k).
sdd: Wrote 1 records + 0 bytes (total of 1 bytes = 0.00k).

लिनक्स कर्नेल 0 दूसरे के साथ लौटता हैread और यह निश्चित रूप से गलत है।

निष्कर्ष: गोले जो पहले डेटा का एक बड़ा हिस्सा पढ़ने की कोशिश करते हैं, इस लिनक्स कर्नेल बग को ट्रिगर नहीं करते हैं।


ठीक है, लिनक्स कर्नेल बग के लिए एक नया सत्यापन के साथ बाहर निकलने का उत्तर।
शास्त्री

यह एक बग नहीं है, यह एक सुविधा है!
गुंटराम ब्लोहम

यह एक बहुत ही अजीब दावा है।
शास्त्री

यह एक विशेषता होगी यदि इसे प्रलेखित किया गया था। पढ़ना kernel.org/doc/Documentation/filesystems/proc.txt , मुझे व्यवहार के लिए कोई दस्तावेज़ नहीं दिख रहा है। उस ने कहा, यह स्पष्ट रूप से काम कर रहा है, इसलिए यह एक बग माना जाता है, इसलिए यह बग में डिजाइन है, कार्यान्वयन नहीं है।
चार्ल्स डफी

0

फ़ाइल के अंतर्गत / कभी-कभी फ़ाइल के अंदर अलग-अलग फ़ील्ड के लिए NULL वर्ण का उपयोग किया जाता है। ऐसा लगता है कि पढ़ा यह संभाल नहीं पा रहा है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.