कैसे लिनक्स एक प्रक्रिया "मार" करता है?


91

यह अक्सर मुझे चकित करता है, हालांकि मैं कई दशकों से कंप्यूटर के साथ पेशेवर रूप से काम कर रहा हूं और एक दशक से लिनक्स, मैं वास्तव में अधिकांश ओएस की कार्यक्षमता को ब्लैक बॉक्स के रूप में मानता हूं, जादू के विपरीत नहीं।

आज मैंने killकमांड के बारे में सोचा , और जब मैं इसे प्रति दिन कई बार उपयोग करता हूं (इसके "सामान्य" और -9स्वाद दोनों में ) मुझे यह स्वीकार करना चाहिए कि मुझे बिल्कुल पता नहीं है कि यह पर्दे के पीछे कैसे काम करता है।

मेरे दृष्टिकोण से, यदि कोई रनिंग प्रक्रिया "त्रिशंकु" है, तो मैं killइसके पीआईडी ​​पर कॉल करता हूं , और फिर यह अचानक नहीं चल रहा है। जादू!

वास्तव में वहाँ क्या होता है? Manpages "संकेतों" के बारे में बात करते हैं लेकिन निश्चित रूप से यह सिर्फ एक अमूर्तता है। kill -9एक प्रक्रिया में भेजने के लिए प्रक्रिया के सहयोग की आवश्यकता नहीं होती है (जैसे सिग्नल को हैंडल करना), यह बस इसे मार देता है।

  • CPU समय को जारी रखने के लिए लिनक्स प्रक्रिया को कैसे रोकता है?
  • क्या इसे शेड्यूलिंग से हटाया गया है?
  • क्या यह अपनी खुली फ़ाइल हैंडल से प्रक्रिया को डिस्कनेक्ट करता है?
  • प्रक्रिया 'वर्चुअल मेमोरी कैसे जारी होती है?
  • क्या मेमोरी में एक वैश्विक तालिका जैसा कुछ है, जहां लिनक्स एक प्रक्रिया द्वारा उठाए गए सभी संसाधनों का संदर्भ रखता है, और जब मैं एक प्रक्रिया को "मार" देता हूं, तो लिनक्स बस उस तालिका से गुजरता है और संसाधनों को एक-एक करके मुक्त करता है?

मैं वास्तव में वह सब जानना चाहूंगा!


जवाबों:


71

मारने -9 को एक प्रक्रिया में भेजने के लिए प्रक्रिया के सहयोग की आवश्यकता नहीं होती है (जैसे सिग्नल को हैंडल करना), यह बस इसे मार देता है।

आप यह मान रहे हैं कि क्योंकि कुछ संकेतों को पकड़ा जा सकता है और उन्हें नजरअंदाज कर दिया जाता है। लेकिन प्रति के अनुसार man 2 signal, " संकेत SIGKILL और SIGSTOP को पकड़ा या अनदेखा नहीं किया जा सकता है"। SIGTERM को पकड़ा जा सकता है, यही वजह है कि मैदान killहमेशा प्रभावी नहीं होता है - आमतौर पर इसका मतलब है कि प्रक्रिया के हैंडलर में कुछ गड़बड़ हो गई है। 1

यदि कोई प्रक्रिया किसी दिए गए सिग्नल के लिए हैंडलर को परिभाषित (या नहीं) कर सकती है, तो कर्नेल एक डिफ़ॉल्ट कार्रवाई करता है। SIGTERM और SIGKILL के मामले में, इस प्रक्रिया को समाप्त करना है (जब तक कि इसका pid 1 नहीं है, कर्नेल समाप्त नहीं होगा init) 2 इसका अर्थ है कि इसकी फाइलहैंडल बंद हो गई हैं, इसकी मेमोरी सिस्टम पूल में लौट आई है, इसके माता-पिता SIGCHILD, इसके अनाथ बच्चे हैं init, आदि द्वारा विरासत में मिला है, जैसे कि यह exit(देखें man 2 exit) बुलाया था । यह प्रक्रिया अब मौजूद नहीं है - जब तक कि यह एक ज़ोंबी के रूप में समाप्त नहीं होता है, उस स्थिति में यह अभी भी कर्नेल की प्रक्रिया तालिका में कुछ जानकारी के साथ सूचीबद्ध है; ऐसा तब होता है जब इसका जनक नहीं होता हैwaitऔर इस जानकारी से ठीक से निपटें। हालाँकि, ज़ोंबी प्रक्रियाओं में अब उन्हें कोई मेमोरी आवंटित नहीं की गई है और इसलिए वे निष्पादित नहीं कर सकते हैं।

क्या मेमोरी में एक वैश्विक तालिका जैसा कुछ है जहां लिनक्स एक प्रक्रिया द्वारा उठाए गए सभी संसाधनों का संदर्भ रखता है और जब मैं एक प्रक्रिया को "मारता हूं" तो लिनक्स बस उस तालिका से गुजरता है और संसाधनों को एक-एक करके मुक्त करता है?

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

Manpages "संकेतों" के बारे में बात करते हैं लेकिन निश्चित रूप से यह सिर्फ एक अमूर्तता है।

ज़रूर, सभी संकेत एक अमूर्त हैं। वे वैचारिक हैं, जैसे "प्रक्रिया"। मैं शब्दार्थ को थोड़ा खेल रहा हूं, लेकिन अगर आपका मतलब है कि साइगिल गुणात्मक रूप से SIGTERM से अलग है, तो हाँ और नहीं। हां इस अर्थ में कि इसे पकड़ा नहीं जा सकता, लेकिन इस अर्थ में नहीं कि वे दोनों संकेत हैं। सादृश्य से, एक सेब एक नारंगी नहीं है, लेकिन सेब और संतरे एक पूर्वनिर्धारित परिभाषा के अनुसार, दोनों फल हैं। SIGKILL अधिक सार लगता है क्योंकि आप इसे पकड़ नहीं सकते हैं, लेकिन यह अभी भी एक संकेत है। यहाँ SIGTERM हैंडलिंग का एक उदाहरण दिया गया है, मुझे यकीन है कि आपने इन्हें पहले देखा है:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

void sighandler (int signum, siginfo_t *info, void *context) {
    fprintf (
        stderr,
        "Recieved %d from pid %u, uid %u.\n",
        info->si_signo,
        info->si_pid,
        info->si_uid
    );
}

int main (void) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_sigaction = sighandler;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGTERM, &sa, NULL);
    while (1) sleep(10);
    return 0;
}             

यह प्रक्रिया हमेशा के लिए सो जाएगी। आप इसे एक टर्मिनल में चला सकते हैं और इसे SIGTERM के साथ भेज सकते हैं kill। यह सामान बाहर थूकता है:

Recieved 15 from pid 25331, uid 1066.

1066 मेरा उदित है। पिड वह शेल होगा जिसमें killसे निष्पादित किया गया है, या यदि आप इसे फोर्क ( kill 25309 & echo $?) में मारते हैं तो पीआईडी ।

फिर, SIGKILL के लिए हैंडलर सेट करने का कोई मतलब नहीं है क्योंकि यह काम नहीं करेगा। 3 यदि मैं kill -9 25309प्रक्रिया समाप्त कर दूंगा। लेकिन यह अभी भी एक संकेत है; कर्नेल को यह जानकारी है कि सिग्नल किसने भेजा , यह किस तरह का सिग्नल है, आदि।


1. यदि आपने संभावित संकेतों की सूची को नहीं देखा है , तो देखें kill -l

2. एक और अपवाद, जैसा कि टिम पोस्ट नीचे उल्लेख करता है, निर्बाध नींद में प्रक्रियाओं पर लागू होता है । जब तक अंतर्निहित समस्या हल नहीं हो जाती है, तब तक उन्हें नहीं जगाया जा सकता है, और इसलिए सभी संकेतों (SIGKILL सहित) को अवधि के लिए स्थगित कर दिया गया है। एक प्रक्रिया उद्देश्य पर उस स्थिति को नहीं बना सकती है, हालांकि।

3. इसका मतलब यह नहीं है कि kill -9अभ्यास करना बेहतर है। मेरा उदाहरण हैंडलर इस अर्थ में एक बुरा है कि यह करने के लिए नेतृत्व नहीं करता है exit()। SIGTERM हैंडलर का असली उद्देश्य प्रक्रिया को अस्थायी फ़ाइलों को साफ करने जैसी चीजों को करने का मौका देना है, फिर स्वेच्छा से बाहर निकलें। यदि आप उपयोग करते हैं kill -9, तो यह मौका नहीं मिलता है, इसलिए केवल यही करें कि यदि "स्वेच्छा से बाहर निकलें" भाग विफल हो गया है।


ठीक है, लेकिन इस प्रक्रिया को मार डालो -9क्योंकि यह असली समस्या है कि किसने यह इच्छा की कि यह मर जाए! ;)
कीवी

@ कीवी: द कर्नेल। आईपीसी सहित संकेत इसके माध्यम से गुजरते हैं; कर्नेल डिफ़ॉल्ट क्रियाओं को लागू करता है।
गोल्डीलॉक्स

12
यह उल्लेख के लायक हो सकता है कि डिस्क स्लीप (डी) सभी संकेतों को पूर्व निर्धारित करता है, जबकि प्रक्रिया उस स्थिति में है। इसलिए kill -9निश्चित I / O बाध्य प्रक्रियाओं में काम करने की कोशिश नहीं की जा रही है, कम से कम तुरंत नहीं।
टिम पोस्ट

7
मैं जोड़ूंगा कि चूंकि kill -9इसे पकड़ा नहीं जा सकता, इसलिए इसे प्राप्त करने से पहले इसे प्राप्त करने वाली एक प्रक्रिया किसी भी सफाई (जैसे अस्थायी फ़ाइलों को हटाने, साझा की गई मेमोरी को मुक्त करने आदि) का प्रदर्शन नहीं कर सकती है। इसलिए, केवल अंतिम उपाय के रूप में kill -9(उर्फ kill -kill) का उपयोग करें । ए kill -hupऔर / या kill -termपहले से शुरू करें और फिर kill -killअंतिम झटका के रूप में उपयोग करें ।
JRFerguson

"प्रक्रिया अब मौजूद नहीं है - जब तक कि यह एक ज़ोंबी के रूप में समाप्त नहीं होता है, तो उस स्थिति में इसे अभी भी कर्नेल की प्रक्रिया तालिका में कुछ जानकारी के साथ सूचीबद्ध किया जाता है" वास्तव में, सभी प्रक्रियाएं ज़ोंबी स्थिति में चली जाती हैं जब वे मर जाते हैं, और ज़ोंबी गायब हो जाएगा जब माता-पिता बच्चे पर वेटपिड करते हैं, आम तौर पर ऐसा होने के लिए आपके लिए बहुत तेजी से होता है
चतुर

3

प्रत्येक प्रक्रिया निर्धारित समय के लिए चलती है और फिर अन्य कार्यों के लिए सीपीयू कोर देने के लिए हार्डवेयर टाइमर द्वारा बाधित किया जाता है। यही कारण है कि सीपीयू कोर की तुलना में बहुत अधिक प्रक्रियाएं होना संभव है, या यहां तक ​​कि एक ही कोर सीपीयू पर बहुत सारी प्रक्रियाओं के साथ सभी ऑपरेटिंग सिस्टम चलाते हैं।

प्रक्रिया बाधित होने के बाद, नियंत्रण कर्नेल कोड में वापस आ जाता है। यह कोड तब प्रक्रिया पक्ष से किसी भी सहयोग के बिना, बाधित प्रक्रिया के निष्पादन को फिर से शुरू नहीं करने का निर्णय ले सकता है। किल -9 आपके कार्यक्रम की किसी भी पंक्ति में निष्पादन को समाप्त कर सकता है।


0

यहाँ एक आदर्श वर्णन है कि एक प्रक्रिया कैसे काम करती है। व्यवहार में, किसी भी यूनिक्स संस्करण में कई अतिरिक्त जटिलताएं और अनुकूलन होंगे।

कर्नेल में प्रत्येक प्रक्रिया के लिए एक डेटा संरचना होती है जो इस बात की जानकारी संग्रहीत करती है कि यह किस मेमोरी में मैपिंग है, इसके क्या सूत्र हैं और जब वे शेड्यूल किए जाते हैं, तो इसके पास कौन सी फाइलें हैं, आदि। यदि कर्नेल किसी प्रक्रिया को मारने का निर्णय लेता है, तो यह एक नोट बनाता है। प्रक्रिया की डेटा संरचना (और शायद प्रत्येक थ्रेड के डेटा संरचना में) कि प्रक्रिया को मार दिया जाना है।

यदि प्रक्रिया का एक थ्रेड वर्तमान में किसी अन्य CPU पर शेड्यूल किया गया है, तो कर्नेल उस थ्रेड को अधिक तेज़ी से निष्पादित करने से रोकने के लिए उस थ्रेड को प्राप्त करने के लिए उस अन्य CPU पर एक बाधा को ट्रिगर कर सकता है।

जब अनुसूचक ने नोटिस किया कि एक धागा एक प्रक्रिया में है जिसे मार दिया जाना चाहिए, तो यह अब इसे शेड्यूल नहीं करेगा।

जब प्रक्रिया के थ्रेड्स में से कोई भी शेड्यूल नहीं किया जाता है, तो कर्नेल प्रक्रिया के संसाधनों को मुक्त करना शुरू कर देता है (मेमोरी, फ़ाइल डिस्क्रिप्टर, ...)। जब भी कर्नेल किसी संसाधन को मुक्त करता है, यह जाँच करता है कि उसके मालिक के पास अभी भी जीवित संसाधन हैं या नहीं। एक बार जब प्रक्रिया में कोई और अधिक जीवित संसाधन (मेमोरी मैपिंग, ओपन फाइल डिस्क्रिप्टर,…) नहीं है, तो प्रक्रिया के लिए डेटा संरचना को मुक्त किया जा सकता है और संबंधित प्रविष्टि को प्रक्रिया तालिका से हटाया जा सकता है।

कुछ संसाधनों को तुरंत मुक्त किया जा सकता है (उदाहरण के लिए मेमोरी को डील करना जो I / O ऑपरेशन द्वारा उपयोग में नहीं है)। अन्य संसाधनों को प्रतीक्षा करनी चाहिए, उदाहरण के लिए I / O ऑपरेशन का वर्णन करने वाले डेटा को मुक्त नहीं किया जा सकता है जबकि I / O ऑपरेशन प्रगति पर है (जबकि DMA प्रगति पर है, यह जिस मेमोरी तक पहुंच रहा है वह उपयोग में है, और DMA को रद्द करने की आवश्यकता है परिधीय से संपर्क करना)। ऐसे संसाधन के लिए ड्राइवर को सूचित किया जाता है, और रद्दीकरण को जल्दी करने की कोशिश कर सकता है; एक बार ऑपरेशन जारी नहीं होने के बाद, ड्राइवर उस संसाधन को मुक्त कर देगा।

(प्रक्रिया तालिका में प्रविष्टि वास्तव में एक संसाधन है जो मूल प्रक्रिया से संबंधित है, जो प्रक्रिया समाप्त होने पर मुक्त हो जाता है और अभिभावक घटना को स्वीकार करता है ।)

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