2012-02-06 10 views
17

पर प्रक्रियाओं और धागे के बीच भेद this answer पर पढ़ने के बाद और रॉबर्ट लव द्वारा "लिनक्स कर्नेल डेवलपमेंट" और बाद में, clone() सिस्टम कॉल पर, मैंने पाया कि लिनक्स में प्रक्रियाएं और धागे कर्नेल के लिए लगभग अलग-अलग हैं । उनके बीच कुछ बदलाव हैं (उद्धृत एसओ प्रश्न में "अधिक साझाकरण" या "कम साझाकरण" होने के रूप में चर्चा की गई), लेकिन मेरे पास अभी तक कुछ प्रश्नों का उत्तर देने के लिए अभी भी कुछ प्रश्न हैं।लिनक्स

मैंने हाल ही में कुछ पॉज़िक्स धागे से जुड़े कार्यक्रम पर काम किया और इस आधार पर प्रयोग करने का फैसला किया। एक प्रक्रिया पर जो दो धागे बनाता है, पाठ्यक्रम के सभी धागे pthread_self(), द्वारा द्वारा getpid() द्वारा अनन्य मूल्य प्राप्त करते हैं।

एक नमूना कार्यक्रम मेरे द्वारा बनाए गए इस प्रकार है:

#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
#include <unistd.h> 
#include <pthread.h> 

void* threadMethod(void* arg) 
{ 
    int intArg = (int) *((int*) arg); 

    int32_t pid = getpid(); 
    uint64_t pti = pthread_self(); 

    printf("[Thread %d] getpid() = %d\n", intArg, pid); 
    printf("[Thread %d] pthread_self() = %lu\n", intArg, pti); 
} 

int main() 
{ 
    pthread_t threads[2]; 

    int thread1 = 1; 

    if ((pthread_create(&threads[0], NULL, threadMethod, (void*) &thread1)) 
     != 0) 
    { 
     fprintf(stderr, "pthread_create: error\n"); 
     exit(EXIT_FAILURE); 
    } 

    int thread2 = 2; 

    if ((pthread_create(&threads[1], NULL, threadMethod, (void*) &thread2)) 
     != 0) 
    { 
     fprintf(stderr, "pthread_create: error\n"); 
     exit(EXIT_FAILURE); 
    } 

    int32_t pid = getpid(); 
    uint64_t pti = pthread_self(); 

    printf("[Process] getpid() = %d\n", pid); 
    printf("[Process] pthread_self() = %lu\n", pti); 

    if ((pthread_join(threads[0], NULL)) != 0) 
    { 
     fprintf(stderr, "Could not join thread 1\n"); 
     exit(EXIT_FAILURE); 
    } 

    if ((pthread_join(threads[1], NULL)) != 0) 
    { 
     fprintf(stderr, "Could not join thread 2\n"); 
     exit(EXIT_FAILURE); 
    } 

    return 0; 
} 

(यह [gcc -pthread -o thread_test thread_test.c] 64-बिट फेडोरा को संकलित किया गया; pthread_t<bits/pthreadtypes.h> से प्राप्त के लिए इस्तेमाल किया 64-बिट प्रकार की वजह से, कोड नाबालिग की आवश्यकता होगी परिवर्तन 32-बिट संस्करणों पर संकलित करने के लिए)

उत्पादन मैं इस प्रकार है:।

[[email protected] ~]$ ./thread_test 
[Process] getpid() = 28549 
[Process] pthread_self() = 140050170017568 
[Thread 2] getpid() = 28549 
[Thread 2] pthread_self() = 140050161620736 
[Thread 1] getpid() = 28549 
[Thread 1] pthread_self() = 140050170013440 
[[email protected] ~]$ 

schedu का उपयोग करके ler gdb में ताला लगा, मैं कार्यक्रम और उसके धागे जिंदा रख सकते हैं तो मैं पर कब्जा कर सकते हैं क्या top कहते हैं, जो, सिर्फ दिखा प्रक्रियाओं, है:

PID USER  PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
28602 bean  20 0 15272 1112 820 R 0.4 0.0 0:00.63 top 
2036 bean  20 0 108m 1868 1412 S 0.0 0.0 0:00.11 bash 
28547 bean  20 0 231m 16m 7676 S 0.0 0.4 0:01.56 gdb 
28549 bean  20 0 22688 340 248 t 0.0 0.0 0:00.26 thread_test 
28561 bean  20 0 107m 1712 1356 S 0.0 0.0 0:00.07 bash 

और जब धागे दिखा रहा है, का कहना है:

PID USER  PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
28617 bean  20 0 15272 1116 820 R 47.2 0.0 0:00.08 top 
2036 bean  20 0 108m 1868 1412 S 0.0 0.0 0:00.11 bash 
28547 bean  20 0 231m 16m 7676 S 0.0 0.4 0:01.56 gdb 
28549 bean  20 0 22688 340 248 t 0.0 0.0 0:00.26 thread_test 
28552 bean  20 0 22688 340 248 t 0.0 0.0 0:00.00 thread_test 
28553 bean  20 0 22688 340 248 t 0.0 0.0 0:00.00 thread_test 
28561 bean  20 0 107m 1860 1432 S 0.0 0.0 0:00.08 bash 

ऐसा लगता है कि प्रोग्राम, या शायद कर्नेल, प्रक्रियाओं के विपरीत धागे को परिभाषित करने का एक अलग तरीका है। top के अनुसार प्रत्येक धागे का अपना पीआईडी ​​होता है - क्यों?

+1

'क्लोन() 'यह है कि लिनक्स दोनों धागे और' कांटा()' को कैसे लागू करता है। यह सब मायने रखता है कि पीआईडी ​​से बात करने से सिग्नल पास हो जाएगा जो हर किसी को जानना चाहिए। यदि कर्नेल धागे को अतिरिक्त आईडी निर्दिष्ट करता है, तो यह आपका कोई भी व्यवसाय नहीं है और इससे प्रभावित नहीं होता है कि आप अपनी प्रक्रियाओं से कैसे बात करते हैं। –

+0

अच्छा [लिंक] (http://opensourceforgeeks.blogspot.in/2014/03/processes-and-threads-in-linux.html) के माध्यम से जाने के लिए। –

+0

"* लिनक्स में प्रक्रियाओं और धागे कर्नेल के लिए अलग-अलग हैं (लगभग) * उम्म, वास्तव में सच नहीं है। लिनक्स कर्नेल कैसे काम करता है, यह प्रक्रियाओं और धागे दोनों के बारे में सच है कि आप लगभग कुछ भी नहीं कह सकते हैं। वीएम के विचार का मालिक है? केवल प्रक्रियाओं। निर्धारित किया जा सकता है? केवल धागे एक फाइल डिस्क्रिप्टर टेबल है? केवल प्रक्रियाओं। प्राथमिकता है? केवल धागे और इसलिए लाइन पर नीचे। –

उत्तर

29

ये भ्रम इस तथ्य से सभी स्टेम हैं कि कर्नेल डेवलपर्स मूल रूप से एक तर्कहीन और गलत विचार रखते हैं कि थ्रेड को पूरी तरह से कर्नेल प्रक्रियाओं का उपयोग करके उपयोगकर्ता के रूप में कार्यान्वित किया जा सकता है, जब तक कर्नेल ने उन्हें साझा करने का एक तरीका दिया स्मृति और फ़ाइल वर्णनकर्ताओं। यह पॉज़िक्स धागे के कुख्यात रूप से खराब लिनक्स थ्रेड्स कार्यान्वयन का कारण बनता है, जो कि एक गलत नाम था क्योंकि यह POSIX थ्रेड सेमेन्टिक्स के समान दूरस्थ रूप से कुछ भी नहीं देता था। आखिर में लिनक्स थ्रेड को प्रतिस्थापित किया गया था (एनपीटीएल द्वारा), लेकिन बहुत भ्रमित शब्दावली और गलतफहमी बरकरार रहेगी।

एहसास करने वाली पहली और सबसे महत्वपूर्ण बात यह है कि "पीआईडी" का अर्थ कर्नेल स्पेस और उपयोगकर्ता स्पेस में अलग-अलग चीजें हैं। कर्नेल कॉल पीआईडी ​​वास्तव में कर्नेल-स्तरीय थ्रेड आईडी (जिसे अक्सर टीआईडी ​​कहा जाता है) हैं, pthread_t के साथ भ्रमित नहीं होना चाहिए जो एक अलग पहचानकर्ता है। सिस्टम पर प्रत्येक थ्रेड, चाहे एक ही प्रक्रिया या एक अलग में, एक अद्वितीय टीआईडी ​​(या कर्नेल की शब्दावली में "पीआईडी" है)।

दूसरी ओर, "प्रक्रिया" की पॉज़िक्स भावना में पीआईडी ​​माना जाता है, को कर्नेल में "थ्रेड ग्रुप आईडी" या "टीजीआईडी" कहा जाता है।प्रत्येक प्रक्रिया में एक या अधिक धागे (कर्नेल प्रक्रिया) होते हैं जिनमें से प्रत्येक अपने स्वयं के टीआईडी ​​(कर्नेल पीआईडी) के साथ होता है, लेकिन सभी एक ही टीजीआईडी ​​साझा करते हैं, जो प्रारंभिक धागे के टीआईडी ​​(कर्नेल पीआईडी) के बराबर होता है जिसमें main रन होते हैं।

जब top आपको थ्रेड दिखाता है, तो यह टीआईडी ​​(कर्नेल पीआईडी) दिखा रहा है, पीआईडी ​​(कर्नेल टीजीआईडी) नहीं, और यही कारण है कि प्रत्येक थ्रेड में एक अलग होता है।

NPTL के आगमन के साथ

, सबसे सिस्टम कॉल है कि एक पीआईडी ​​तर्क या बुला प्रक्रिया पर कार्रवाई ले एक TGID के रूप में पीआईडी ​​का इलाज और पूरी "धागा समूह" (इसे POSIX प्रक्रिया) पर कार्रवाई करने के लिए बदल रहे थे।

+0

आपके उत्तर के लिए धन्यवाद, और मैं कुछ समय के लिए जो कुछ कहता हूं, उस पर शोध कर रहा हूं, इसमें कोई संदेह नहीं है। शायद आज मेरे पद से सबसे बड़ा सवाल (जहां तक ​​मैं कह सकता हूं) है, 'दूसरों ने इसके बारे में क्यों नहीं पूछा?' (कम से कम, आसानी से उपलब्ध संसाधन के माध्यम से।) निश्चित रूप से, यह उन लोगों के लिए एक बड़ा विषय होना चाहिए जो मेरे जैसे, बहुप्रचारित अनुप्रयोगों में शामिल हैं? – Doddy

+0

ठीक है आजकल (अब हम लिनक्स थ्रेड्स फियास्को से पीछे हैं) एप्लिकेशन प्रोग्रामर वास्तव में पॉज़िक्स थ्रेड * का उपयोग करके व्यवसाय में जा सकते हैं * जैसा कि पॉज़िक्स द्वारा निर्दिष्ट किया गया है, बिना हुड के नीचे क्या चल रहा है, इस बारे में बहुत ज्यादा चिंता किए बिना, क्योंकि सबकुछ ठीक से काम करता है। मुझे संदेह है कि यही कारण है कि कार्यान्वयन के विवरण अब और अधिक ध्यान नहीं देते हैं। बीटीडब्ल्यू 'मैन 7 पर्थ्रेड' में कुछ बुनियादी स्पष्टीकरण है कि यह कैसे काम करता है। –

+0

@bean - प्रश्न को कई अलग-अलग कोणों से कई अलग-अलग तरीकों से पूछा गया है। आर ने विशेष रूप से अच्छा जवाब दिया है कि यह लिनक्स ऐतिहासिक और तकनीकी दृष्टिकोण दोनों से भ्रम के कई स्तरों पर छूता है। – Duck

0

लिनक्स पर, प्रत्येक थ्रेड को थ्रेड आईडी मिलता है। मुख्य धागे की थ्रेड आईडी प्रक्रिया आईडी के रूप में डबल ड्यूटी परोसती है (और उपयोगकर्ता इंटरफ़ेस में भी जाना जाता है)। थ्रेड आईडी लिनक्स का एक कार्यान्वयन विवरण है, और POSIX आईडी से असंबंधित है। अधिक जानकारी के लिए, gettid सिस्टम कॉल देखें (शुद्ध पायथन से उपलब्ध नहीं है क्योंकि यह सिस्टम-विशिष्ट है)।

+0

मुझे यकीन नहीं है कि 'मुख्य धागे की थ्रेड आईडी डबल ड्यूटी परोसती है क्योंकि प्रक्रिया आईडी' सत्य है। जब मैंने उपरोक्त कोड चलाया, तो मुख्य धागे की थ्रेड आईडी पीआईडी ​​के समान नहीं थी। – codeforester

1

किसी प्रकार की "मेटा-इकाई" की कल्पना करें। यदि इकाई अपने माता-पिता के किसी भी संसाधन (पता स्थान, फ़ाइल वर्णनकर्ता, आदि) को साझा नहीं करती है तो यह एक प्रक्रिया है, और यदि इकाई अपने माता-पिता के सभी संसाधनों को साझा करती है तो यह एक धागा है। आप प्रक्रिया और धागे के बीच कुछ आधा रास्ता भी प्राप्त कर सकते हैं (उदाहरण के लिए कुछ संसाधन साझा किए गए हैं और कुछ साझा नहीं किए गए हैं)। "क्लोन()" सिस्टम कॉल (उदा। http://linux.die.net/man/2/clone) पर एक नज़र डालें और आप देखेंगे कि लिनक्स आंतरिक रूप से चीजें कैसे करता है।

अब कुछ प्रकार के अमूर्तता के पीछे छिपाएं जो सब कुछ एक प्रक्रिया या थ्रेड की तरह दिखता है। अगर अमूर्त दोषहीन है तो आप "इकाइयों" और "प्रक्रियाओं और धागे" के बीच का अंतर कभी नहीं जानते। हालांकि अमूर्तता काफी निर्दोष नहीं है - जो पीआईडी ​​आप देख रहे हैं वह वास्तव में एक "इकाई आईडी" है।

+0

क्षमा करें, आपका उत्तर मेरे प्रश्न पर कोई प्रकाश नहीं डाला है। मैंने पहले ही क्लोन() 'के मैन पेज को देखा है। तथ्य यह है कि प्रक्रियाओं और धागे के बीच अमूर्तता थी, जिसने मुझे पहले स्थान पर सवाल पूछा था। यह कहना बहुत आसान है क्योंकि मैंने 50% से कम संसाधनों के साथ 'क्लोन()' को कॉल करना चुना है, इसलिए मुझे एक प्रक्रिया के विपरीत * थ्रेड * दिया जाना चाहिए, और इसी तरह। – Doddy