2012-07-20 5 views
7

विफल रहता है जब मैं C++ में किसी बच्चे POSIX थ्रेड से/proc/net/tcp खोलने का प्रयास करता हूं तो यह "ऐसी कोई फ़ाइल या निर्देशिका नहीं" त्रुटि। अगर मैं इसे पैरेंट थ्रेड से खोलने की कोशिश करता हूं तो यह हर बार सफल होता है, और इसे पैरेंट थ्रेड में खोलने/बंद करने की प्रक्रिया तब बच्चे के धागे में भी एक तिहाई समय में सफल होती है। मैं बिना किसी मुद्दे के बच्चे के थ्रेड में 100% समय/खोल/अपटाइम खोल सकता हूं। यहाँ कुछ उदाहरण कोड है जो के साथ "जी ++ -Wall test.cc -ओ परीक्षण -pthread" संकलित किया जा सकता है: (POSIX थ्रेड से C++ में खोलना/proc/net/tcp अधिकांश समय

#include <iostream> 
#include <fstream> 
#include <cstring> 
#include <cerrno> 
#include <pthread.h> 

using namespace std; 

void * open_test (void *) 
{ 
    ifstream in; 
    in.open("/proc/net/tcp"); 
    if (in.fail()) 
     cout << "Failed - " << strerror(errno) << endl; 
    else 
     cout << "Succeeded" << endl; 
    in.close(); 

    return 0; 
} 

int main (int argc, char * argv[]) 
{ 
    open_test(NULL); 

    pthread_t thread; 
    pthread_create(&thread, NULL, open_test, NULL); 
    pthread_exit(0); 
} 

मैं एक इंटेल i5-2520M के साथ एक Ubuntu 12.04 बॉक्स पर यह चला रहा हूँ 2 कोर * 2 आभासी कोर) लिनक्स कर्नेल 3.2.0 पर।

[email protected]:/tmp$ ./test 
Succeeded 
Failed - No such file or directory 
[email protected]:/tmp$ ./test 
Succeeded 
Succeeded 
[email protected]:/tmp$ ./test 
Succeeded 
Failed - No such file or directory 
[email protected]:/tmp$ ./test 
Succeeded 
Failed - No such file or directory 
[email protected]:/tmp$ ./test 
Succeeded 
Succeeded 
[email protected]:/tmp$ ./test 
Succeeded 
Failed - No such file or directory 
[email protected]:/tmp$ 

यह शायद ध्यान देने योग्य बात है कि मैं इस समस्या अगर मैं POSIX धागे के बजाय कांटा का उपयोग नहीं है लायक है: यहाँ एक पंक्ति में उपरोक्त कोड चल 6 बार मुझे का उत्पादन होता है। अगर मैं कांटा का उपयोग करता हूं, तो बच्चे की प्रक्रिया में कोई समस्या नहीं होती है/proc/net/tcp

बस कुछ डेटा पॉइंट फेंकने के लिए .... ऐसा लगता है कि यह लिनक्स में 2.6.35 के रूप में एक प्रतिगमन है समय के 100% काम करने के लिए। 3.2.0 ज्यादातर समय मेरे धीमे पुराने पेंटियम एम आधारित लैपटॉप पर भी।

+0

क्या आपने 3.4.4 कर्नेल के साथ प्रयास किया था? –

+0

@BasileStarynkevitch मैं पुन: पेश करने पर 3.4.4 –

+1

@MikeCardwell में सक्षम हूँ, आप लिनक्स कर्नेल के खिलाफ एक बग के रूप में इस पर विचार किया है दाखिल? –

उत्तर

2

यह व्यवहार /proc वर्चुअल फाइल सिस्टम में एक प्रकार का बग प्रतीत होता है। तुम सिर्फ फ़ाइल खोलने से पहले इस कोड को जोड़ते हैं:

system("ls -l /proc/net /proc/self/net/tcp"); 

आपको लगता है कि /proc/net/proc/self/net करने के लिए एक प्रतीकात्मक कड़ी है देखेंगे, और /proc/sec/net/tcp ठीक से open_test के दोनों कॉल के लिए सूचीबद्ध किया गया है, तब भी जब पैदा की धागा कॉल के विफल ।

संपादित करें: मैं सिर्फ एहसास हुआ ऊपर परीक्षण, फर्जी है, क्योंकि स्वयं प्रणाली अधिकार, नहीं इस प्रक्रिया के खोल प्रक्रिया को उल्लेख करता है।

void ls_command() { 
    ostringstream cmd; 
    cmd << "ls -l /proc/net " 
     << "/proc/" << getpid() 
     << "/net/tcp " 
     << "/proc/" << syscall(SYS_gettid) 
     << "/net/tcp"; 
    system(cmd.str().c_str()); 
} 

आप देखेंगे कि पैदा की धागा कभी कभी नहीं माता-पिता की /net/tcp फ़ाइल को देखने के लिए सक्षम हो जाएगा: निम्नलिखित समारोह का उपयोग करने के बजाय भी बग का पता चलता है। असल में यह गायब हो गया है, क्योंकि यह स्पॉल्ड शेल की प्रक्रिया है जो ls कमांड चला रही है।

नीचे दिए गए कामकाज से बच्चे के धागे को विश्वसनीय रूप से पहुंचने की अनुमति मिलती है जो /proc/net/tcp होगी।

मेरा सिद्धांत यह है कि यह किसी प्रकार की रेस कंडीशन बग है जो थ्रेड के लिए /proc/self प्रविष्टि को सही ढंग से स्थापित करने के साथ मूल स्थिति और थ्रेड विशिष्ट स्थिति के उचित मिश्रण के रूप में सेट करता है। एक परीक्षण और कार्य के रूप में, मैंने open_test कोड को मूल प्रक्रिया तक पहुंचने की कोशिश करने के बजाय थ्रेड से जुड़े "प्रक्रिया पहचानकर्ता" का उपयोग करने के लिए संशोधित किया है (क्योंकि /proc/self पैरेंट प्रक्रिया आईडी को संदर्भित करता है, धागे की नहीं)।

संपादित करें: सबूत संकेत के रूप में, बग माता पिता प्रक्रिया अपने /proc/self/... राज्य को साफ करने से पहले बच्चे धागा इसे पढ़ने के लिए का अवसर मिल सके से कोई लेना देना नहीं है। मैं अभी भी इसे एक बग बनाने के लिए बनाए रखता हूं, क्योंकि बाल धागा अभी भी तकनीकी रूप से प्रक्रिया का हिस्सा है। यह getpid() मुख्य थ्रेड कॉल करने से पहले और बाद में pthread_exit() है। माता-पिता प्रक्रिया के लिए /proc प्रविष्टि वैध रहनी चाहिए जब तक कि सभी बाल धागे पूरा नहीं हो जाते।हालांकि

EDIT2: जोनास का तर्क है कि यह एक बग नहीं हो सकता। इस बात का सबूत हैं, वहाँ man proc से यह है:

  /proc/[pid]/fd 
       ... 
       In a multithreaded process, the contents of this directory are 
       not available if the main thread has already terminated (typi- 
       ally by calling pthread_exit(3)).

लेकिन फिर एक ही man पेज प्रविष्टि में /proc/self के लिए इस प्रविष्टि पर विचार करें:

  /proc/self 
       This directory refers to the process accessing the /proc file 
       system, and is identical to the /proc directory named by the 
       process ID of the same process.

यदि एक विश्वास करने के लिए इस वजह से एक बग नहीं है धागे और प्रक्रियाओं को लिनक्स में समान माना जाता है, तो धागे की अपेक्षा की जानी चाहिए कि /proc/self काम करेगा। बग आसानी से /proc/self संशोधित /proc/[getpid] संस्करण अब उपलब्ध नहीं है जब, बस के रूप में वैकल्पिक हल के नीचे कर रही है /proc/[gettid] मान का उपयोग करने को बदलने के लिए द्वारा निर्धारित किया जा सकता है।

void * open_test (void *) 
{ 
    ifstream in; 
    string file = "/proc/net/tcp"; 
    in.open(file.c_str()); 
    if (in.fail()) { 
     ostringstream ss; 
     ss << "/proc/" << syscall(SYS_gettid) << "/net/tcp"; 
     cout << "Can't access " << file 
      << ", using " << ss.str() << " instead" << endl; 
     file = ss.str(); 
     in.open(file.c_str()); 
    } 
    if (in.fail()) 
     cout << "Failed - " << strerror(errno) << endl; 
    else 
     cout << "Succeeded" << endl; 
    in.close(); 

    return 0; 
} 
+0

आपका उदाहरण मेरे लिए काम करता है, धन्यवाद। मुझे लगता है कि मैं आपकी व्याख्या समझता हूं। तो क्या यह मूल रूप से procfs में एक लिनक्स कर्नेल बग है? –

+0

@ माइककार्डवेल: मुझे संदेह है कि यह मामला है। – jxh

+0

@ user315052 आप http://stackoverflow.com/questions/807506/threads-vs-processes-in-linux को देखने के लिए देखने के लिए क्यों यह एक बग नहीं हो सकता है कर सकते हैं। प्रक्रियाओं और धागे वास्तव में लिनक्स में काफी समान हैं। दूसरी तरफ, धागा/proc/$ pid/कार्यों/में भी सूचीबद्ध है .... यह बिल्कुल अस्पष्ट है और शायद इन चीजों को कर्नेल मेलिंग सूची पर चर्चा करनी चाहिए। –

2

आप एक pthread_join (धागा, शून्य) से पहले pthread_exit() कॉल कॉल जोड़ते हैं, तो अपने कार्यक्रम सही ढंग से काम करेंगे।

+0

हम्म, आपको सही लगता है। क्या आप व्यख्या कर सकते हैं? मेरी समझ यह थी कि मुख्य समारोह में pthread_exit को कॉल करना बस इसे अन्य सभी धागे समाप्त होने तक प्रतीक्षा करेगा।साथ ही, यह समस्या pthread_join के बिना भी/proc/uptime के साथ प्रकट नहीं होती है? –

+0

@ माइककार्डवेल: ऐसा लगता है कि 'थॉट' का अर्थ है 'pthread_exit' कॉल'/proc/self/net/tcp' फ़ाइल एंट्री को हटाने के लिए ज़िम्मेदार है, इससे पहले कि बच्चे के धागे को इसे ढूंढने का मौका मिले। मैं अभी भी इसे एक बग के रूप में विशेषता दूंगा, हालांकि। चूंकि प्रक्रिया अभी तक बाहर नहीं निकली है, इसलिए बाल धागा अभी भी उस प्रविष्टि को ढूंढने में सक्षम होना चाहिए। – jxh

+0

दरअसल यह मुझे समझ में आता है। चूंकि अभिभावक प्रक्रिया बच्चे को समाप्त करने की प्रतीक्षा नहीं कर रही है, इसलिए बच्चे माता-पिता की '/ proc' प्रविष्टि से पढ़ने की कोशिश कर सकता है जो पहले ही हटा दिया गया है। यह किसी भी अन्य स्पष्टीकरण की तुलना में बहुत अधिक समझ में आता है क्योंकि मैंने हमेशा सोर्स को सिस्को के समान ही माना था। –

4

के रूप में स्कॉट उसके जवाब में बताते हैं, एक pthread_join(thread, NULL) फिक्स लक्षण जोड़ने। पर क्यों?

  1. (gdb) run […] 
    Succeeded 
    [New Thread 0x7ffff7fd1700 (LWP 18937)]   // <- child thread 
    [Thread 0x7ffff7fd3740 (LWP 18934) exited]  // <- parent thread 
    [Switching to Thread 0x7ffff7fd1700 (LWP 18937)] 
    Breakpoint 1, open_test() at test.cc:14 
    
  2. :

    (gdb) break test.cc:14 
    Breakpoint 1 at 0x400c98: file test.cc, line 14. 
    

    फिर हम व्यवहार के दो अलग अलग प्रकार का निरीक्षण कर सकते हैं:

    के gdb में कार्यक्रम रखा है और बिंदु पर एक ब्रेकपाइंट जहां खुले में नाकाम रही है सेट करें

  3. (gdb) run 
    Succeeded 
    [New Thread 0x7ffff7fd1700 (LWP 19427)]   // <- child thread 
    Succeeded 
    [Thread 0x7ffff7fd1700 (LWP 19427) exited] 
    [Inferior 1 (process 19424) exited normally] 
    

पहला एक सुझाव देता है कि माता-पिता की प्रक्रिया बच्चे के सामने निकलती है। लिनक्स के रूप में, प्रक्रियाओं और धागे काफी समान हैं, इसका तात्पर्य है कि मुख्य प्रक्रिया से जुड़े पीआईडी ​​को साफ कर दिया जाता है। हालांकि बच्चे के धागे को चलाने से कुछ भी बाधा नहीं डालता है। यह और उसकी पिड अभी भी पूरी तरह मान्य हैं। मुख्य प्रक्रिया के पीआईडी ​​को बस /proc/self अंक, जो उस बिंदु पर हटा दिया गया है।

संबंधित मुद्दे