2012-12-20 60 views
7

मैं एक सी लाइब्रेरी लिख रहा हूं जिसे प्रारंभिक समय के दौरान फोर्क() की आवश्यकता है। इसलिए, मैं जोर देना चाहता हूं() कि एप्लिकेशन कोड (जो मेरे नियंत्रण के बाहर है) मेरे पुस्तकालय प्रारंभिक कोड को एक थ्रेडेड संदर्भ से कॉल करता है (अच्छी तरह से ज्ञात "threads and fork don't mix" समस्या से बचने के लिए)। एक बार मेरी लाइब्रेरी शुरू हो जाने के बाद, यह थ्रेड सुरक्षित है (और उम्मीद है कि एप्लिकेशन स्तर कोड थ्रेड बना सकता है)। मैं केवल pthreads का समर्थन करने के लिए चिंतित हूँ।pthreads: कोड को कैसे जोर देना एक थ्रेड किए गए संदर्भ में चलाया जाता है

pthreads का उपयोग कर वर्तमान प्रक्रिया स्थान में धागे की संख्या को गिनना असंभव लगता है। दरअसल, यहां तक ​​कि googletest केवल मैक ओएस और क्यूएनएक्स पर GetThreadCount() लागू करता है।

, यह देखते हुए कि मैं धागे गिनती नहीं कर सकते हैं यह किसी भी तरह कि मैं बजाय एक ही लड़ी संदर्भ पर जोर कर सकते हैं हो सकता है?

स्पष्टीकरण: यदि संभव हो, तो मैं "/ proc" (गैर-पोर्टेबल), अतिरिक्त लाइब्रेरी निर्भरता (libproc) और LD_PRELOAD-style pthread_create wrappers का उपयोग करने से बचना चाहूंगा।

स्पष्टीकरण # 2: मेरे मामले कई प्रक्रियाओं का उपयोग कर में आवश्यक है के रूप में मेरे पुस्तकालय में श्रमिकों अपेक्षाकृत भारी वजन (वेबकिट प्रयोग करके) और क्रैश हो सकता है कर रहे हैं। हालांकि, मैं मूल प्रक्रिया कार्यकर्ता दुर्घटनाओं से बचने के लिए चाहता हूं।

+0

मैं अपने पोर्टेबल नहीं पता है, लेकिन इसकी एक शुरुआत: लिनक्स पर, वहाँ है [/ proc/self/स्टेट] (http: //www.kernel.org/doc/man-pages/online/pages/man5/proc.5.html)। – ArjunShankar

+0

कैसे "अगर (pthread_self()! = NULL) ..."? क्षमा करें, यह उपयोगी नहीं है अगर आप यह देखना चाहते हैं कि पहले के मुकाबले अन्य धागे नहीं हैं। माफ़ कीजिये। सोचते रहेंगे ... –

+1

आगे की जांच पर, मुझे यकीन नहीं है कि इस समस्या को हल करने का कोई आसान तरीका है। शायद बस "इसे स्पष्ट रूप से दस्तावेज करें और उपयोगकर्ता को परेशान होने दें यदि वे दस्तावेज़ नहीं पढ़ रहे हैं?" पोर्टेबिलिटी कारण के लिए –

उत्तर

2

आप को चिह्नित कर सकता है अपने पुस्तकालय आरंभीकरण समारोह आवेदन main() से पहले चलाने के लिए। उदाहरण के लिए, जीसीसी का उपयोग कर,

static void my_lib_init(void) __attribute__((constructor)); 

static void my_lib_init(void) 
{ 
    /* ... */ 
} 

एक अन्य विकल्प posix_spawn() उपयोग करने के लिए कांटा और अलग, गुलाम binaries के रूप में कार्यकर्ता प्रक्रियाओं पर अमल करने के लिए है।

संपादित जोड़ने के लिए:

मुझे ऐसा लगता है कि अगर आप यह निर्धारित करने की प्रक्रिया पहले से ही (वास्तविक कर्नेल पर आधारित) धागे पैदा कर दी है कि अगर चाहें तो आप ओएस विशिष्ट कोड पर भरोसा करना होगा।

लिनक्स मामले में, दृढ़ संकल्प सरल है, और अन्य ओएसई पर भी चलाने के लिए सुरक्षित है। यह मौजूदा प्रक्रिया के द्वारा प्रयोग किया धागे की संख्या का निर्धारण नहीं कर सकते, समारोह -1 वापस आ जाएगी:

#include <unistd.h> 
#include <sys/types.h> 
#include <dirent.h> 
#include <errno.h> 

int count_threads_linux(void) 
{ 
    DIR   *dir; 
    struct dirent *ent; 
    int   count = 0; 

    dir = opendir("/proc/self/task/"); 
    if (!dir) 
     return -1; 

    while (1) { 

     errno = 0; 
     ent = readdir(dir); 
     if (!ent) 
      break; 

     if (ent->d_name[0] != '.') 
      count++; 
    } 

    if (errno) { 
     const int saved_errno = errno; 
     closedir(dir); 
     errno = saved_errno; 
     return -1; 
    } 

    if (closedir(dir)) 
     return -1; 

    return count; 
} 

कुछ मामलों में जब कि चेक लिनक्स में भी असफल हो जायेगी (/proc/ बिना chroot) की तरह होते हैं, इसलिए -1 वापसी मान हमेशा की तरह अज्ञात बजाय त्रुटि व्यवहार किया जाना चाहिए (हालांकि errno विफलता के लिए वास्तविक कारण का संकेत होगा)।

फ्रीबीएसडी मैन पेजों को देखते हुए, मुझे आश्चर्य है कि संबंधित जानकारी बिल्कुल उपलब्ध है या नहीं।

अंत:

बजाय समस्या पैदा करने वाले मामले का पता लगाने की कोशिश, मैं गंभीरता से आप fork() और exec() (या posix_spawn()) गुलाम प्रक्रियाओं की सलाह देते हैं, का उपयोग करते हुए केवल async-संकेत सुरक्षित कार्य (man 7 signal देखें) बच्चे की प्रक्रिया में (exec() से पहले), इस प्रकार कांटा() - थ्रेड जटिलताओं से परहेज करते हैं। आप अभी भी फोर्किंग() से पहले किसी भी साझा मेमोरी सेगमेंट, सॉकेट जोड़े, और cetera बना सकते हैं। एकमात्र कमी यह है कि मैं देख सकता हूं कि आपको दास श्रमिकों के लिए अलग-अलग बाइनरी का उपयोग करना है। जो, उनके बारे में आपका विवरण दिया गया है, मुझे कोई कमी की तरह नहीं लगता है।

+0

init/constructor trick @ahochhaus की समस्या को रोकने के लिए नहीं जा रहा है यदि लाइब्रेरी को 'dlopen() ' – ydroneaud

+0

@ydroneaud से लोड किया गया है: मैंने सोचा था कि" मुख्य() से पहले चलाएं "यह स्पष्ट होगा। ऐसा नहीं है कि कोड समय पर वापस जा सकता है .. –

+0

पढ़ना "/ proc" आदर्श नहीं है, लेकिन मैं मानता हूं कि थ्रेड गिनने का एकमात्र तरीका ओएस विशिष्ट है, इसलिए इस प्रकार का समाधान सबसे अच्छा है जो हम प्राप्त कर सकते हैं। धन्यवाद @ नाममात्र पशु। – ahochhaus

1

आप इस प्रक्रिया को 'tty को नियंत्रित करने के लिए एक SIGINFO संकेत भेजते हैं, तो प्रक्रिया धागे की स्थिति का वर्णन करना चाहिए। विवरण से यह साबित करना संभव है कि कोई धागा बनाया गया है या नहीं।

आपको अपनी लाइब्रेरी में आउटपुट को वापस पढ़ने के लिए पॉपन के माध्यम से एक छोटी उपयोगिता विकसित करना पड़ सकता है।

जोड़ा गया नमूना कोड शुक्र दिसंबर 21 14:45

एक साधारण प्रोग्राम है जो पांच सूत्र बनाता चलाएँ। धागे मूल रूप से सोते हैं। कार्यक्रम से बाहर निकलने से पहले, थ्रेड की स्थिति प्राप्त करने के लिए एक SIGINFO सिग्नल भेजें।

openbsd> cat a.c 
#include <unistd.h> 
#include <pthread.h> 

#define THREADS 5 

void foo(void); 

int 
main() 
{  
    pthread_t thr[THREADS]; 
    int j; 

    for (j = 0; j < THREADS; j++) { 
     pthread_create(&thr[j], NULL, (void *)foo, NULL); 
    } 

    sleep(200);        

    return(0); 
}    

void 
foo() 
{ 
    sleep(100); 
}    
openbsd> gcc a.c -pthread 
openbsd> a.out & 
[1] 1234 
openbsd> kill -SIGINFO 1234 
0x8bb0e000 sleep_wait 15 -c---W---f 0000   
0x8bb0e800 sleep_wait 15 -c---W---f 0000   
0x8bb0e400 sleep_wait 15 -c---W---f 0000   
0x7cd3d800 sleep_wait 15 -c---W---f 0000   
0x7cd3d400 sleep_wait 15 -c---W---f 0000   
0x7cd3d000 sleep_wait 15 -c---W---f 0000 main 
+1

क्या आपके पास इसके बारे में कोई संदर्भ है? – ydroneaud

+0

मेरे सिस्टम (ओपनबीएसडी) पर 'मैन पर्थ्रेड' है। यह POSIX 1003.1c थ्रेड इंटरफेस अनुपालन माना जाता है। –

+0

कोई उदाहरण है? – ydroneaud

-1

आप pthread_once() का उपयोग कर सकते हैं ताकि यह सुनिश्चित किया जा सके कि कोई अन्य थ्रेड एक ही काम नहीं कर रहा है: इस तरह आपको यह सुनिश्चित करने की ज़रूरत नहीं है कि एकाधिक थ्रेड आपके प्रारंभिक फ़ंक्शन को कॉल कर रहे हैं, केवल एक ही वास्तव में निष्पादित किया जाएगा।

अपना सार्वजनिक प्रारंभिक कार्य pthread_once() के माध्यम से एक निजी प्रारंभिकरण चलाएं।

static pthread_once_t my_initialisation_once = PTHREAD_ONCE_INIT; 

static void my_initialisation(void) 
{ 
    /* do something */ 
} 

int lib_initialisation(void) 
{ 
    pthread_once(&my_initialisation_conce, my_initialisation); 

    return 0; 
} 

एक और उदाहरण here पाया जा सकता है।

लिंक

+1

ydroneaud, धन्यवाद - यह एक दिलचस्प विचार है। हालांकि, जैसा कि मैं सबसे अच्छा कह सकता हूं, यह सुनिश्चित नहीं करता है कि लाइब्रेरी प्रारंभ करने से पहले एप्लिकेशन कोड पहले से ही एक अलग थ्रेड नहीं बना सकता है (जो कभी भी मेरे लाइब्रेरी कोड में प्रवेश नहीं करेगा लेकिन फिर भी म्यूटेक्स डेडलॉक का कारण बन सकता है)। – ahochhaus

+0

@ हैचहॉस यदि आप किसी अन्य उपयोगकर्ता से पहले इनिट फ़ंक्शन को कॉल न करने वाले उपयोगकर्ता से भी रक्षा करना चाहते हैं, * बस * अन्य सभी कार्यों में इनिट फ़ंक्शन को कॉल करें: 'pthread_once (& my_initialisation_conce, my_initialisation); ' – ydroneaud

+0

@ हैचहॉस और आप अभी भी उपयोग कर सकते हैं एक परमाणु वैश्विक परिवर्तनीय सेट केवल तभी प्रारंभ होता है जब प्रारंभिकता पूरी तरह से हो जाती है – ydroneaud