Erlang 21 - 6. How to Implement an Alternative Carrier for the Erlang Distribution

6 एर्लांग वितरण के लिए वैकल्पिक वाहक कैसे लागू करें




erlang

6 एर्लांग वितरण के लिए वैकल्पिक वाहक कैसे लागू करें

यह खंड बताता है कि एरलांग वितरण के लिए वैकल्पिक वाहक प्रोटोकॉल को कैसे लागू किया जाए। वितरण सामान्य रूप से टीसीपी / आईपी द्वारा किया जाता है। यहां एक अन्य प्रोटोकॉल के साथ टीसीपी / आईपी को बदलने के लिए एक विधि बताई गई है।

अनुभाग uds_dist उदाहरण अनुप्रयोग (कर्नेल अनुप्रयोग examples निर्देशिका में) का चरण-दर-चरण स्पष्टीकरण है। uds_dist एप्लिकेशन यूनिक्स डोमेन सॉकेट्स पर वितरण को लागू करता है और यह सन सोलारिस 2 ऑपरेटिंग वातावरण के लिए लिखा गया है। तंत्र हालांकि सामान्य हैं और किसी भी ऑपरेटिंग सिस्टम पर लागू होते हैं, जिस पर Erlang चलता है। C कोड को पोर्टेबल नहीं बनाया जाने का कारण, केवल पठनीयता है।

६.१ परिचय

एर्लांग वितरण के लिए एक नया वाहक लागू करने के लिए, मुख्य चरण निम्नानुसार हैं।

ध्यान दें

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

एक Erlang चालक लेखन

सबसे पहले, प्रोटोकॉल एर्लैंग मशीन के लिए उपलब्ध होना चाहिए, जिसमें एरलंग ड्राइवर लिखना है। पोर्ट प्रोग्राम का उपयोग नहीं किया जा सकता है, एक Erlang ड्राइवर की आवश्यकता है। Erlang ड्राइवर हो सकते हैं:

  • सांख्यिकीय रूप से एमुलेटर से जुड़ा हुआ है, जो एर्लांग के खुले स्रोत वितरण का उपयोग करते समय एक विकल्प हो सकता है, या

  • एर्लांग मशीनों के पते वाले स्थान में डायनामिक रूप से लोड किया गया है, जो एकमात्र विकल्प है यदि एर्लैंग के पूर्व-निर्मित संस्करण का उपयोग किया जाना है

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

ड्राइवर के लिए एक एरलांग इंटरफ़ेस लिखना

जब ड्राइवर को लागू किया जाता है, तो कोई ड्राइवर के लिए अधिमानतः एरलांग इंटरफ़ेस लिखेगा ताकि ड्राइवर की कार्यक्षमता का अलग से परीक्षण किया जा सके। इस इंटरफ़ेस का उपयोग वितरण मॉड्यूल द्वारा किया जा सकता है, जो net_kernel से प्रोटोकॉल के विवरण को कवर करेगा।

सबसे आसान रास्ता है inet और inet_tcp इंटरफेस की नक़ल करना, लेकिन उन मॉड्यूल में बहुत अधिक कार्यक्षमता को लागू करने की आवश्यकता नहीं है। उदाहरण के आवेदन में, केवल कुछ सामान्य इंटरफेस लागू होते हैं, और वे बहुत सरल होते हैं।

डिस्ट्रीब्यूशन मॉड्यूल लिखना

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

  • अन्य नोड्स खोजने का विवरण (जो कि epmd या कुछ इसी तरह की बात कर रहा है)
  • एक सुनने के बंदरगाह (या समान) बनाना
  • अन्य नोड्स से कनेक्ट करना
  • हैंडशेक / कुकी सत्यापन करना

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

बूट लिपियों का निर्माण

अंतिम चरण बूट स्क्रिप्ट बनाने के लिए है जो प्रोटोकॉल कार्यान्वयन को बूट समय पर उपलब्ध कराता है। कार्यान्वयन को तब शुरू किया जा सकता है जब सभी सिस्टम चल रहे हों, तब वितरण शुरू कर सकते हैं, लेकिन एक वास्तविक प्रणाली में वितरण बहुत पहले शुरू करना है, क्यों बूट स्क्रिप्ट और कुछ कमांड-लाइन पैरामीटर आवश्यक हैं।

यह चरण यह भी बताता है कि इंटरफ़ेस और वितरण मॉड्यूल में Erlang कोड इस तरह से लिखा गया है कि इसे स्टार्टअप चरण में चलाया जा सकता है। विशेष रूप से, application मॉड्यूल या बूट समय पर लोड नहीं होने वाले किसी भी मॉड्यूल के लिए कोई कॉल नहीं हो सकता है। यही है, केवल Kernel , STDLIB , और एप्लिकेशन का ही उपयोग किया जा सकता है।

6.2 वितरण मॉड्यूल

वितरण मॉड्यूल एक API को उजागर करता है जो अन्य नोड्स के कनेक्शन को प्रबंधित करने के लिए net_kernel कॉल करता है। मॉड्यूल का नाम प्रत्यय _dist होना चाहिए।

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

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

वितरण मॉड्यूल का एक उदाहरण कार्यान्वयन $ERL_TOP/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl में पाया जा सकता है। यह प्रक्रियाओं द्वारा कार्यान्वित वितरण नियंत्रकों के साथ gen_tcp API का उपयोग करके टीसीपी / आईपी पर वितरण को लागू करता है। पोर्ट वितरण नियंत्रकों का उपयोग करने के बजाय साधारण टीसीपी / आईपी वितरण का उपयोग करता है।

निर्यात कॉलबैक कार्य

निम्नलिखित कार्य अनिवार्य हैं:

listen(Name) ->
{ok, {Listen, Address, Creation}} | {error, Error}

आने वाले कनेक्शन अनुरोधों को सुनने के लिए एक बार listen/1 कहा जाता है। वितरण लाने पर कॉल किया जाता है। तर्क Name पूर्ण नोड नाम में @ चिह्न से पहले नोड नाम का हिस्सा है। यह एक परमाणु या एक स्ट्रिंग हो सकता है।

वापसी मान में एक हेंडल हैंडल होता है (जिसे बाद में accept/1 कॉलबैक में पास किया जाता है), Address जो एक #net_address{} रिकॉर्ड है नोड के लिए पते के बारे में जानकारी के साथ ( #net_address{} रिकॉर्ड kernel/include/net_address.hrl में परिभाषित किया गया है kernel/include/net_address.hrl ), और Creation जो (वर्तमान में) एक पूर्णांक 1 , 2 , या 3

यदि epmd का उपयोग नोड खोज के लिए किया जाना है, तो आप आमतौर पर epmd साथ epmd पोर्ट को पंजीकृत करने और उपयोग करने के लिए Creation को पुनः प्राप्त करने के लिए (दुर्भाग्य से undocumented) erl_epmd मॉड्यूल ( kernel अनुप्रयोग का हिस्सा) का उपयोग करना चाहते हैं।

accept(Listen) ->
AcceptorPid

accept/1 को एक ऐसी प्रक्रिया शुरू करनी चाहिए जो कनेक्शन स्वीकार करे। यह प्रक्रिया अधिमानतः max प्राथमिकता पर निष्पादित होनी चाहिए। इस प्रक्रिया की प्रक्रिया पहचानकर्ता को लौटाया जाना चाहिए।

Listen तर्क वैसा ही होगा जैसा कि ऊपर दिए गए श्रवण listen/1 कॉलबैक के रिटर्न मान का हिस्सा Listen accept/1 को केवल एक बार कहा जाता है जब वितरण प्रोटोकॉल शुरू किया जाता है।

इस फ़ंक्शन का कॉलर net_kernel लिए एक प्रतिनिधि है (यह net_kernel रूप में पंजीकृत प्रक्रिया हो सकता है या नहीं भी हो सकता है) और इस दस्तावेज़ में Kernel रूप में पहचाना जाता है। जब किसी कनेक्शन को स्वीकर्ता प्रक्रिया द्वारा स्वीकार कर लिया गया है, तो उसे Kernel को स्वीकृत कनेक्शन के बारे में सूचित करना होगा। यह फ़ॉर्म पर एक संदेश भेजकर किया जाता है:

Kernel ! {accept, AcceptorPid, DistController, Family, Proto}

DistController या तो कनेक्शन के लिए वितरण नियंत्रक की प्रक्रिया या बंदरगाह पहचानकर्ता है। वितरण नियंत्रक को नए कनेक्शन स्वीकार किए जाने पर स्वीकर्ता प्रक्रियाओं द्वारा बनाया जाना चाहिए। इसका काम कनेक्शन पर ट्रैफिक भेजना है।

Kernel निम्नलिखित संदेशों में से एक के साथ प्रतिक्रिया करता है:
{Kernel, controller, SupervisorPid}

अनुरोध स्वीकार कर लिया गया था और SupervisorPid , कनेक्शन पर्यवेक्षक प्रक्रिया की प्रक्रिया पहचानकर्ता है (जो accept_connection/5 कॉलबैक में बनाया गया है)।

{Kernel, unsupported_protocol}

अनुरोध अस्वीकार कर दिया गया था। यह एक घातक त्रुटि है। स्वीकर्ता प्रक्रिया समाप्त होनी चाहिए।

जब एक स्वीकार अनुक्रम पूरा हो गया है, तो स्वीकर्ता प्रक्रिया को आगे के अनुरोधों को स्वीकार करना जारी रखने की उम्मीद है।

accept_connection(AcceptorPid, DistCtrl, MyNode, Allowed, SetupTime) ->
ConnectionSupervisorPid

accept_connection/5 को एक ऐसी प्रक्रिया को accept_connection/5 चाहिए जो कनेक्शन के लिए Erlang वितरण हैंडशेक का प्रदर्शन करे। यदि हैंडशेक सफलतापूर्वक पूरा हो जाता है तो इसे कनेक्शन पर्यवेक्षक के रूप में कार्य करना जारी रखना चाहिए। यह प्रक्रिया अधिमानतः max प्राथमिकता पर निष्पादित होनी चाहिए।

तर्क:

AcceptorPid

accept/1 कॉलबैक द्वारा बनाई गई प्रक्रिया की प्रक्रिया पहचानकर्ता।

DistCtrl

स्वीकर्ता नियंत्रक द्वारा बनाई गई वितरण नियंत्रक पहचानकर्ता की पहचानकर्ता। dist_util:handshake_other_started(HsData) साथ पारित होने के लिए dist_util:handshake_other_started(HsData)

MyNode

इस नोड का नोड नाम। dist_util:handshake_other_started(HsData) साथ पारित होने के लिए dist_util:handshake_other_started(HsData)

Allowed

dist_util:handshake_other_started(HsData) साथ पारित होने के लिए dist_util:handshake_other_started(HsData)

SetupTime

dist_util:start_timer(SetupTime) लिए कॉल द्वारा सेटअप टाइमर बनाने के लिए उपयोग किया गया समय। टाइमर को dist_util:handshake_other_started(HsData) साथ पास किया जाना चाहिए।

बनाई गई प्रक्रिया में एक #hs_data{} रिकॉर्ड में हैंडशेक के लिए कॉलबैक और अन्य आवश्यक जानकारी प्रदान की जानी चाहिए और इस रिकॉर्ड के साथ dist_util:handshake_other_started(HsData) कॉल करें।

dist_util:handshake_other_started(HsData) करेगा और यदि हैंडशेक सफलतापूर्वक इस प्रक्रिया को पूरा करता है तो कनेक्शन सुपरवाइजर लूप में तब तक जारी रहेगा जब तक कनेक्शन ऊपर है।

setup(Node, Type, MyNode, LongOrShortNames, SetupTime) ->
ConnectionSupervisorPid

setup/5 को एक प्रक्रिया को स्पॉन करना चाहिए जो Node जुड़ता है। जब कनेक्शन स्थापित किया गया है तो इसे कनेक्शन के लिए एरलंग वितरण हैंडशेक करना चाहिए। यदि हैंडशेक सफलतापूर्वक पूरा हो जाता है तो इसे कनेक्शन पर्यवेक्षक के रूप में कार्य करना जारी रखना चाहिए। यह प्रक्रिया अधिमानतः max प्राथमिकता पर निष्पादित होनी चाहिए।

तर्क:

Node

रिमोट नोड का नोड नाम। dist_util:handshake_we_started(HsData) साथ पास होने के लिए dist_util:handshake_we_started(HsData)

Type

संबंध प्रकार। dist_util:handshake_we_started(HsData) साथ पास होने के लिए dist_util:handshake_we_started(HsData)

MyNode

इस नोड का नोड नाम। dist_util:handshake_we_started(HsData) साथ पास होने के लिए dist_util:handshake_we_started(HsData)

LongOrShortNames

या तो परमाणु longnames या परमाणु longnames यह दर्शाता है कि लंबे या छोटे नाम का उपयोग किया जाता है।

SetupTime

dist_util:start_timer(SetupTime) लिए कॉल द्वारा सेटअप टाइमर बनाने के लिए उपयोग किया गया समय। टाइमर को dist_util:handshake_we_started(HsData) साथ पास किया जाना चाहिए।

इस फ़ंक्शन का कॉलर net_kernel लिए एक प्रतिनिधि है (यह net_kernel रूप में पंजीकृत प्रक्रिया हो सकता है या नहीं भी हो सकता है) और इस दस्तावेज़ में Kernel रूप में पहचाना जाता है।

यह फ़ंक्शन कनेक्शन पर्यवेक्षक को स्पैनिंग के अलावा, एक वितरण नियंत्रक भी बनाना चाहिए। वितरण नियंत्रक या तो एक प्रक्रिया या एक बंदरगाह है जो यातायात भेजने के लिए जिम्मेदार है।

बनाई गई प्रक्रिया में एक #hs_data{} रिकॉर्ड में हैंडशेक के लिए कॉलबैक और अन्य आवश्यक जानकारी प्रदान की जानी चाहिए और इस रिकॉर्ड के साथ dist_util:handshake_we_started(HsData) कॉल करें।

dist_util:handshake_we_started(HsData) करेगा और हैंडशेक सफलतापूर्वक इस प्रक्रिया को पूरा करता है, तब तक कनेक्शन पर्यवेक्षक लूप में जारी रहेगा, जब तक कनेक्शन ऊपर है।

close(Listen) ->
void()

Listen हैंडल को बंद करने के लिए कॉल किया जाता है जो मूल रूप से listen/1 कॉलबैक से पारित किया गया था।

select(NodeName) ->
boolean()

यदि NodeName नाम का होस्ट नाम भाग इस प्रोटोकॉल के साथ उपयोग के लिए मान्य है, तो true लौटें; अन्यथा, false

दो वैकल्पिक कार्य भी हैं जिन्हें निर्यात किया जा सकता है:

setopts(Listen, Opts) ->
ok | {error, Error}

तर्क Listen मूल रूप से listen/1 कॉलबैक से पारित हैंडल है। तर्क Opts भविष्य के कनेक्शन पर सेट करने के लिए विकल्पों की एक सूची है।

getopts(Listen, Opts) ->
{ok, OptionValues} | {error, Error}

तर्क Listen मूल रूप से listen/1 कॉलबैक से पारित हैंडल है। तर्क Opts भविष्य के कनेक्शन के लिए पढ़ने के लिए विकल्पों की एक सूची है।

#S_data {} रिकॉर्ड

dist_util:handshake_we_started/1 और dist_util:handshake_other_started/1 फ़ंक्शन तर्क के रूप में #hs_data{} रिकॉर्ड लेता है। इस रिकॉर्ड में बहुत सारे फ़ील्ड हैं जिन्हें आपको सेट करने की आवश्यकता है। रिकॉर्ड को kernel/include/dist_util.hrl । नहीं प्रलेखित क्षेत्रों को सेट नहीं किया जाना चाहिए, अर्थात, undefined रूप में छोड़ दिया जाना चाहिए।

निम्नलिखित #hs_data{} रिकॉर्ड फ़ील्ड को तब तक सेट करने की आवश्यकता है जब तक कि अन्यथा न कहा जाए:

kernel_pid

Kernel प्रक्रिया की प्रक्रिया पहचानकर्ता। यही है, प्रक्रिया है कि या तो setup/5 या accept_connection/5 कहा जाता है।

other_node

अन्य नोड का नाम। यह फ़ील्ड केवल तभी अनिवार्य है जब यह नोड कनेक्शन आरंभ करता है। यही है, जब कनेक्शन setup/5 माध्यम से सेट किया गया है।

this_node

इस नोड का नोड नाम।

socket

वितरण नियंत्रक की पहचानकर्ता।

timer

dist_util:start_timer/1 का उपयोग करके बनाया गया टाइमर dist_util:start_timer/1

allowed

सूचना को accept_connection/5 करने के लिए Allowed accept_connection/5 । यह क्षेत्र केवल तभी अनिवार्य है जब दूरस्थ नोड ने कनेक्शन शुरू किया। यही है, जब कनेक्शन accept_connection/5 माध्यम से सेट किया गया है।

f_send

निम्नलिखित हस्ताक्षर के साथ एक मजेदार:

fun (DistCtrlr, Data) -> ok | {error, Error}

जहाँ DistCtrlr डिस्ट्रीब्यूशन कंट्रोलर का पहचानकर्ता है और Data दूसरी तरफ पास करने के लिए io डेटा है।

केवल हैंडशेक चरण के दौरान उपयोग किया जाता है।

f_recv

निम्नलिखित हस्ताक्षर के साथ एक मजेदार:

fun (DistCtrlr, Length) -> {ok, Packet} | {error, Reason}

जहाँ DistCtrlr वितरण नियंत्रक का पहचानकर्ता है। यदि Length 0 , तो सभी उपलब्ध बाइट्स वापस आनी चाहिए। यदि Length > 0 , बिल्कुल Length बाइट्स वापस किया जाना चाहिए, या एक त्रुटि; संभवतः दूसरी तरफ से बंद होने पर डेटा की Length बाइट्स से कम हो। इसका उपयोग दूसरे छोर से डेटा प्राप्त करने के लिए किया जाता है।

केवल हैंडशेक चरण के दौरान उपयोग किया जाता है।

f_setopts_pre_nodeup

निम्नलिखित हस्ताक्षर के साथ एक मजेदार:

fun (DistCtrlr) -> ok | {error, Error}

जहाँ DistCtrlr वितरण नियंत्रक का पहचानकर्ता है। वितरण चैनल को सामान्य ट्रैफ़िक के लिए उठाए जाने से ठीक पहले कॉल किया जाता है।

केवल हैंडशेक चरण के दौरान उपयोग किया जाता है।

f_setopts_post_nodeup

निम्नलिखित हस्ताक्षर के साथ एक मजेदार:

fun (DistCtrlr) -> ok | {error, Error}

जहाँ DistCtrlr वितरण नियंत्रक का पहचानकर्ता है। वितरण चैनल को सामान्य ट्रैफ़िक के लिए ले जाने के तुरंत बाद कॉल किया जाता है।

केवल हैंडशेक चरण के दौरान उपयोग किया जाता है।

f_getll

निम्नलिखित हस्ताक्षर के साथ एक मजेदार:

fun (DistCtrlr) -> ID

जहाँ DistCtrlr वितरण नियंत्रक का पहचानकर्ता है और ID निम्न स्तर की इकाई का पहचानकर्ता है जो कनेक्शन को संभालता है (अक्सर DistCtrlr स्वयं)।

केवल हैंडशेक चरण के दौरान उपयोग किया जाता है।

f_address

निम्नलिखित हस्ताक्षर के साथ एक मजेदार:

fun (DistCtrlr, Node) -> NetAddress

जहाँ DistCtrlr वितरण नियंत्रक का पहचानकर्ता है, Node दूसरे छोर पर नोड का नोड नाम है, और NetAddress कनेक्शन के दूसरे छोर पर Node लिए पते के बारे में जानकारी के साथ एक #net_address{} रिकॉर्ड है। #net_address{} रिकॉर्ड को kernel/include/net_address.hrl में परिभाषित kernel/include/net_address.hrl

केवल हैंडशेक चरण के दौरान उपयोग किया जाता है।

mf_tick

निम्नलिखित हस्ताक्षर के साथ एक मजेदार:

fun (DistCtrlr) -> void()

जहाँ DistCtrlr वितरण नियंत्रक का पहचानकर्ता है। इस फ़ंक्शन को उस कनेक्शन पर जानकारी भेजनी चाहिए, जो दूसरे छोर पर प्राप्त पैकेटों के आंकड़ों को बढ़ाते हुए दूसरे छोर से व्याख्या नहीं करता है। यह आमतौर पर एक खाली पैकेट भेजकर लागू किया जाता है।

ध्यान दें

यह महत्वपूर्ण है कि यह ऑपरेशन लंबे समय तक कॉलर को ब्लॉक नहीं करता है। चूंकि यह कनेक्शन पर्यवेक्षक से कहा जाता है।

कनेक्शन होने पर उपयोग किया जाता है।

mf_getstat

निम्नलिखित हस्ताक्षर के साथ एक मजेदार:

fun (DistCtrlr) -> {ok, Received, Sent, PendSend}

जहाँ DistCtrlr वितरण नियंत्रक का पहचानकर्ता है, Received पैकेट प्राप्त होता है, Sent गया पैकेट होता है, और PendSend को भेजे जाने वाले कतार में पैकेट की मात्रा PendSend है या boolean() यह दर्शाता है कि क्या कतार में पैकेट भेजे जाने हैं।

ध्यान दें

यह महत्वपूर्ण है कि यह ऑपरेशन लंबे समय तक कॉलर को ब्लॉक नहीं करता है। चूंकि यह कनेक्शन पर्यवेक्षक से कहा जाता है।

कनेक्शन होने पर उपयोग किया जाता है।

request_type

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

mf_setopts

निम्नलिखित हस्ताक्षर के साथ एक मजेदार:

fun (DistCtrl, Opts) -> ok | {error, Error}

जहाँ DistCtrlr वितरण नियंत्रक का पहचानकर्ता है और DistCtrlr कनेक्शन पर सेट करने के लिए विकल्पों की एक सूची है।

यह फ़ंक्शन वैकल्पिक है। कनेक्शन होने पर उपयोग किया जाता है।

mf_getopts

निम्नलिखित हस्ताक्षर के साथ एक मजेदार:

fun (DistCtrl, Opts) -> {ok, OptionValues} | {error, Error}

जहाँ DistCtrlr वितरण नियंत्रक का पहचानकर्ता है और कनेक्शन के लिए पढ़ने के लिए DistCtrlr विकल्पों की एक सूची है।

यह फ़ंक्शन वैकल्पिक है। कनेक्शन होने पर उपयोग किया जाता है।

f_handshake_complete

निम्नलिखित हस्ताक्षर के साथ एक मजेदार:

fun (DistCtrlr, Node, DHandle) -> void()

जहाँ DistCtrlr वितरण नियंत्रक का पहचानकर्ता है, Node दूसरे छोर से जुड़े नोड का नोड नाम है, और DHandle एक वितरण नियंत्रक प्रक्रिया द्वारा आवश्यक वितरण हैंडल है, जब निम्नलिखित बीआईएफ को कॉल किया जाता है:

  • erlang:dist_ctrl_get_data/1

  • erlang:dist_ctrl_get_data_notification/1

  • erlang:dist_ctrl_input_handler/2

  • erlang:dist_ctrl_put_data/2

यह फ़ंक्शन तब कहा जाता है जब हैंडशेक पूरा हो गया है और वितरण चैनल ऊपर है। वितरण नियंत्रक चैनल पर ट्रैफ़िक भेजना शुरू कर सकता है। यह फ़ंक्शन वैकल्पिक है।

केवल हैंडशेक चरण के दौरान उपयोग किया जाता है।

add_flags

कनेक्शन में जोड़ने के लिए Distribution flags । वर्तमान में सभी (अप्रचलित) झंडे स्वचालित रूप से सक्षम हो जाएंगे।

यह ध्वज क्षेत्र वैकल्पिक है।

reject_flags

Distribution flags को अस्वीकार करने के लिए। वर्तमान में निम्नलिखित वितरण ध्वज अस्वीकार किए जा सकते हैं:

DFLAG_DIST_HDR_ATOM_CACHE
इस कनेक्शन पर एटम कैश का उपयोग न करें।

फ़ंक्शन dist_util:strict_order_flags/0 उपयोग करें dist_util:strict_order_flags/0 सख्त आदेश वितरण की आवश्यकता वाली सुविधाओं के लिए सभी झंडे प्राप्त करने के लिए dist_util:strict_order_flags/0

यह ध्वज क्षेत्र वैकल्पिक है।

require_flags

इन distribution flags का उपयोग करने की आवश्यकता है। यदि दूसरे छोर का उपयोग नहीं किया जाता है, तो हैंडशेक के दौरान कनेक्शन समाप्त कर दिया जाएगा।

यह ध्वज क्षेत्र वैकल्पिक है।

वितरण डेटा वितरण

डिफ़ॉल्ट कॉन्फ़िगरेशन का उपयोग करते समय, एक कनेक्शन से अधिक पास करने के लिए डेटा को डिलीवर करने की आवश्यकता होती है, जो कि ठीक उसी क्रम में प्राप्त होने वाले नोड पर होता है, जिसमें कभी भी डेटा की हानि नहीं होती है, जैसा कि भेजने वाले नोड से भेजा जाता है।

डेटा डिलीवरी ऑर्डर को उन सुविधाओं को अक्षम करके आराम दिया जा सकता है जिनके लिए सख्त ऑर्डर की आवश्यकता होती है। यह dist_util:strict_order_flags/0 द्वारा लौटाए गए distribution flags को पास करके किया जाता है dist_util:strict_order_flags/0 कनेक्शन सेट करते समय उपयोग किए गए #hs_data{} रिकॉर्ड के reject_flags फ़ील्ड में dist_util:strict_order_flags/0 । जब आराम से ऑर्डर करने का उपयोग किया जाता है, तो केवल उसी प्रेषक / रिसीवर जोड़ी के साथ संकेतों के क्रम को संरक्षित करना होगा। हालांकि, ध्यान दें कि सख्त आदेश की आवश्यकता वाली सुविधाओं को अक्षम करने से प्रदर्शन, थ्रूपुट और / या विलंबता पर नकारात्मक प्रभाव पड़ सकता है।

अपने वितरण मॉड्यूल को सक्षम करें

net_kernel को यह पता लगाने के लिए कि किस वितरण मॉड्यूल का उपयोग करना है, erl कमांड-लाइन तर्क -proto_dist का उपयोग किया जाता है। इसके बाद एक या अधिक वितरण मॉड्यूल नाम आते हैं, जिसमें प्रत्यय "_dist" हटा दिया जाता है। यही है, एक वितरण मॉड्यूल के रूप में -proto_dist gen_tcp रूप में निर्दिष्ट किया -proto_dist gen_tcp

यदि कोई epmd (टीसीपी पोर्ट मैपर डेमॉन) का उपयोग नहीं किया जाता है, तो कमांड-लाइन विकल्प -no_epmd को भी निर्दिष्ट किया जाना है, जो epmd को एक ओएस प्रक्रिया के रूप में और एक epmd रूप में, epmd स्टार्टअप को छोड़ देता है।

6.3 चालक

ध्यान दें

यह खंड बहुत पहले लिखा गया था। ज्यादातर यह अभी भी मान्य है, लेकिन कुछ चीजें तब से बदल गई हैं। यहां प्रस्तुत किए गए ड्राइवर के प्रलेखन के लिए कुछ अपडेट किए गए हैं, लेकिन अधिक किया जा सकता है और भविष्य के लिए योजना बनाई गई है। पाठक को erl_driver और driver_entry प्रलेखन पढ़ने के लिए प्रोत्साहित किया जाता है।

हालांकि आम तौर पर एर्लांग ड्राइवर इस खंड के दायरे से परे हो सकते हैं, एक संक्षिप्त परिचय जगह में होता है।

ड्राइवर जनरल में

एरलंग ड्राइवर एक मूल कोड मॉड्यूल है जिसे सी (या असेंबलर) में लिखा गया है, जो कुछ विशेष ऑपरेटिंग सिस्टम सेवा के लिए एक इंटरफ़ेस के रूप में कार्य करता है। यह एक सामान्य तंत्र है जिसका उपयोग सभी प्रकार के I / O के लिए Erlang emulator के दौरान किया जाता है। Erlang ड्राइवर को erl_ddll Erlang मॉड्यूल का उपयोग करके रनटाइम पर Erlang एमुलेटर से गतिशील रूप से जोड़ा (या लोड किया जा सकता है)। ओटीपी में कुछ ड्राइवर हालांकि स्टेटटाइम सिस्टम से जुड़े हुए हैं, लेकिन यह एक आवश्यकता से अधिक अनुकूलन है।

ड्राइवर डेटा प्रकार और ड्राइवर लेखक के लिए उपलब्ध फ़ंक्शन को Erlang की शामिल निर्देशिका में बैठा हेडर फ़ाइल erl_driver.h में परिभाषित किया गया है। कौन से कार्य उपलब्ध हैं, इसके विवरण के लिए erl_driver प्रलेखन देखें।

इरलांग को संचार प्रोटोकॉल उपलब्ध कराने के लिए ड्राइवर लिखते समय, किसी को उस विशेष प्रोटोकॉल के बारे में जानने लायक हर चीज के बारे में पता होना चाहिए। सभी ऑपरेशन गैर-अवरोधक होने चाहिए और ड्राइवर में सभी संभावित स्थितियों का हिसाब होना चाहिए। एक गैर-स्थिर ड्राइवर पूरे Erlang रनटाइम सिस्टम को प्रभावित और / या क्रैश करेगा।

एमुलेटर निम्नलिखित स्थितियों में ड्राइवर को कॉल करता है:

  • जब ड्राइवर लोड किया जाता है। इस कॉलबैक में एक विशेष नाम होना चाहिए और एक ErlDrvEntry संरचना में एक पॉइंटर को वापस करके कॉलबैक का उपयोग करने के लिए एमुलेटर को सूचित करना चाहिए, जिसे ठीक से भरा जाना है (नीचे देखें)।

  • जब ड्राइवर के लिए एक पोर्ट खोला जाता है (Erlang से एक open_port कॉल द्वारा)। यह दिनचर्या आंतरिक डेटा संरचनाओं को सेट करना है और ErlDrvData प्रकार की एक अपारदर्शी डेटा इकाई को वापस करना है, जो एक डेटा प्रकार है जो एक पॉइंटर को धारण करने के लिए पर्याप्त है। इस फ़ंक्शन द्वारा लौटाया गया सूचक इस विशेष पोर्ट के विषय में अन्य सभी कॉलबैक का पहला तर्क है। इसे आमतौर पर पोर्ट हैंडल कहा जाता है। एमुलेटर केवल हैंडल को स्टोर करता है और कभी भी इसकी व्याख्या करने की कोशिश नहीं करता है, क्यों यह वस्तुतः कुछ भी हो सकता है (एक पॉइंटर से बड़ा कुछ भी नहीं है) और कुछ भी इंगित कर सकता है अगर यह पॉइंटर है। आमतौर पर यह पॉइंटर विशेष पोर्ट के बारे में जानकारी रखने वाली संरचना को संदर्भित करता है, जैसा कि यह उदाहरण में करता है।

  • जब एक Erlang प्रक्रिया पोर्ट पर डेटा भेजती है। डेटा बाइट्स के बफर के रूप में आता है, व्याख्या परिभाषित नहीं है, लेकिन कार्यान्वयनकर्ता पर निर्भर है। यह कॉलबैक कॉलर को कुछ भी नहीं देता है, कॉल करने वाले को संदेश के रूप में उत्तर भेजे जाते हैं (सभी ड्राइवरों के लिए उपलब्ध driver_output नामक एक दिनचर्या का उपयोग करके)। नीचे वर्णित चालकों के लिए एक तुल्यकालिक तरीके से बात करने का एक तरीका भी है। खंडित डेटा को भेजने के लिए एक अतिरिक्त कॉलबैक फ़ंक्शन हो सकता है (एक गहरी io- सूची में भेजा गया)। उस इंटरफ़ेस को एकल बफर के बजाय यूनिक्स writev के लिए उपयुक्त रूप में डेटा मिलता है। इस तरह के कॉलबैक को लागू करने के लिए किसी वितरण ड्राइवर की आवश्यकता नहीं है, इसलिए हम नहीं करेंगे।

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

  • जब फ़ाइल डिस्क्रिप्टर आउटपुट के लिए संकेत दिया जाता है। इस कॉलबैक को पहले की तरह ही कॉल किया जाता है, लेकिन फाइल डिस्क्रिप्टर पर लिखना संभव है। सामान्य परिदृश्य यह है कि driver_select एक फाइल डिस्क्रिप्टर पर लिखने का आदेश देते हैं और यह कि ड्राइवर driver_select कहता है। जब डिस्क्रिप्टर आउटपुट के लिए तैयार होता है, तो यह कॉलबैक कहा जाता है और ड्राइवर आउटपुट भेजने की कोशिश कर सकता है। इस तरह के संचालन में कतार शामिल हो सकती है, और उपयोग करने के लिए ड्राइवर लेखक के लिए सुविधाजनक कतार मार्ग उपलब्ध हैं।

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

  • जब एक Erlang प्रक्रिया erlang:port_control/3 कॉल करती है, जो ड्राइवरों के लिए एक तुल्यकालिक इंटरफ़ेस है। नियंत्रण इंटरफ़ेस का उपयोग ड्राइवर विकल्प सेट करने, बंदरगाहों की स्थिति बदलने और इसी तरह से किया जाता है। इस इंटरफ़ेस का उदाहरण में बहुत उपयोग किया जाता है।

  • जब एक टाइमर समाप्त हो रहा है। ड्राइवर फ़ंक्शन के साथ टाइमर सेट कर सकता है driver_set_timer । जब ऐसे टाइमर समाप्त हो जाते हैं, तो एक विशिष्ट कॉलबैक फ़ंक्शन कहा जाता है। किसी भी टाइमर का उपयोग उदाहरण में नहीं किया जाता है।

  • जब पूरा ड्राइवर उतार दिया जाता है। चालक द्वारा आवंटित प्रत्येक संसाधन को मुक्त किया जाना है।

वितरण चालक की डेटा संरचनाएं

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

हम प्रोटोटाइप की घोषणा करके और एक स्थैतिक ErlDrvEntry संरचना में भरकर यूनिक्स डोमेन सॉकेट ड्राइवर का उदाहरण लिखना शुरू करते हैं:

( 1) #include <stdio.h>
( 2) #include <stdlib.h>
( 3) #include <string.h>
( 4) #include <unistd.h>
( 5) #include <errno.h>
( 6) #include <sys/types.h>
( 7) #include <sys/stat.h>
( 8) #include <sys/socket.h>
( 9) #include <sys/un.h>
(10) #include <fcntl.h>

(11) #define HAVE_UIO_H
(12) #include "erl_driver.h"

(13) /*
(14) ** Interface routines
(15) */
(16) static ErlDrvData uds_start(ErlDrvPort port, char *buff);
(17) static void uds_stop(ErlDrvData handle);
(18) static void uds_command(ErlDrvData handle, char *buff, int bufflen);
(19) static void uds_input(ErlDrvData handle, ErlDrvEvent event);
(20) static void uds_output(ErlDrvData handle, ErlDrvEvent event);
(21) static void uds_finish(void);
(22) static int uds_control(ErlDrvData handle, unsigned int command, 
(23)                        char* buf, int count, char** res, int res_size);

(24) /* The driver entry */
(25) static ErlDrvEntry uds_driver_entry = {
(26)     NULL,                            /* init, N/A */
(27)     uds_start,                       /* start, called when port is opened */
(28)     uds_stop,                        /* stop, called when port is closed */
(29)     uds_command,                     /* output, called when erlang has sent */
(30)     uds_input,                       /* ready_input, called when input
(31)                                         descriptor ready */
(32)     uds_output,                      /* ready_output, called when output 
(33)                                         descriptor ready */
(34)     "uds_drv",                       /* char *driver_name, the argument 
(35)                                         to open_port */
(36)     uds_finish,                      /* finish, called when unloaded */
(37)     NULL,                            /* void * that is not used (BC) */
(38)     uds_control,                     /* control, port_control callback */
(39)     NULL,                            /* timeout, called on timeouts */
(40)     NULL,                            /* outputv, vector output interface */
(41)     NULL,                            /* ready_async callback */
(42)     NULL,                            /* flush callback */
(43)     NULL,                            /* call callback */
(44)     NULL,                            /* event callback */
(45)     ERL_DRV_EXTENDED_MARKER,         /* Extended driver interface marker */
(46)     ERL_DRV_EXTENDED_MAJOR_VERSION,  /* Major version number */
(47)     ERL_DRV_EXTENDED_MINOR_VERSION,  /* Minor version number */
(48)     ERL_DRV_FLAG_SOFT_BUSY,          /* Driver flags. Soft busy flag is
(49)                                         required for distribution drivers */
(50)     NULL,                            /* Reserved for internal use */
(51)     NULL,                            /* process_exit callback */
(52)     NULL                             /* stop_select callback */
(53) };

लाइन 1-10 पर चालक के लिए आवश्यक ओएस हेडर शामिल हैं। जैसा कि यह ड्राइवर सोलारिस के लिए लिखा गया है, हम जानते हैं कि हेडर uio.h मौजूद है। तो प्रीप्रोसेसर चर HAVE_UIO_H को erl_driver.h पर लाइन 12 में शामिल किए जाने से पहले परिभाषित किया जा सकता है। erl_driver.h की परिभाषा ऑपरेटिंग सिस्टम ditto के अनुरूप करने के लिए Erlang के ड्राइवर के HAVE_UIO_H में उपयोग किए गए I / O वैक्टर को HAVE_UIO_H , जो बहुत सुविधाजनक है।

16-23 लाइन पर विभिन्न कॉलबैक फ़ंक्शन घोषित किए जाते हैं ("आगे की घोषणाएं")।

ड्राइवर संरचना स्टेटिकली लिंक्ड-इन ड्राइवरों के लिए समान है और गतिशील रूप से भरी हुई है। हालांकि, विभिन्न प्रकार के ड्राइवरों में कुछ फ़ील्ड खाली छोड़ दिए जाते हैं (यानी, NULL के लिए इनिशियलाइज़ किए गए)। पहला फ़ील्ड ( init फ़ंक्शन पॉइंटर) हमेशा डायनामिक रूप से लोड किए गए ड्राइवर में खाली छोड़ दिया जाता है, लाइन 26 देखें। NULL ऑन लाइन 37 हमेशा रहने वाला है, फ़ील्ड का अब उपयोग नहीं किया जाता है और इसे बैकवर्ड संगतता के लिए बरकरार रखा जाता है। इस ड्राइवर में कोई टाइमर का उपयोग नहीं किया जाता है, क्यों टाइमर के लिए कोई कॉलबैक की आवश्यकता नहीं है। outputv फ़ील्ड (लाइन 40) का उपयोग आउटपुट के लिए यूनिक्स writev समान इंटरफ़ेस को लागू करने के लिए किया जा सकता है। outputv रनटाइम सिस्टम पहले वितरण के लिए outputv उपयोग नहीं कर सकता था, लेकिन यह outputv 5.7.2 से हो सकता है। जैसा कि यह ड्राइवर outputv 5.7.2 से पहले लिखा गया था, यह outputv कॉलबैक का उपयोग नहीं करता है। outputv कॉलबैक का उपयोग करना पसंद किया जाता है, क्योंकि यह डेटा की नकल को कम करता है। (हम हालांकि ड्राइवर में आंतरिक रूप से स्कैटर / इकठ्ठा I / O का उपयोग करेंगे।)

ERTS 5.5.3 से ड्राइवर इंटरफ़ेस को संस्करण नियंत्रण और क्षमता जानकारी पास करने की संभावना के साथ बढ़ाया गया था। क्षमता ध्वज ४ 48 रेखा पर मौजूद हैं। ERTS ERL_DRV_FLAG_SOFT_BUSY ध्वज से ERL_DRV_FLAG_SOFT_BUSY को उन ड्राइवरों के लिए आवश्यक है जो वितरण द्वारा उपयोग किए जाने वाले हैं। नरम व्यस्त ध्वज का तात्पर्य है कि चालक output और output कॉलबैक को कॉल को संभाल सकता है, हालांकि इसने खुद को व्यस्त के रूप में चिह्नित किया है। वितरण द्वारा उपयोग किए जाने वाले ड्राइवरों पर यह हमेशा एक आवश्यकता रही है, लेकिन इस बारे में पहले से कोई क्षमता जानकारी उपलब्ध नहीं है। अधिक जानकारी के लिए। erl_driver:set_busy_port() देखें erl_driver:set_busy_port() )।

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

इस प्रकार, परिभाषित कॉलबैक इस प्रकार हैं:

uds_start

किसी पोर्ट के लिए डेटा आरंभ करना चाहिए। हम यहां कोई सॉकेट नहीं बनाते हैं, केवल डेटा संरचनाओं को इनिशियलाइज़ करते हैं।

uds_stop

पोर्ट बंद होने पर कॉल किया जाता है।

uds_command

एर्लैंग के संदेशों को संभालता है। संदेश या तो सादा डेटा भेजा जा सकता है या चालक को अधिक सूक्ष्म निर्देश भेजे जा सकते हैं। यह फ़ंक्शन ज्यादातर डेटा पंपिंग के लिए यहां है।

uds_input

सॉकेट से पढ़ने के लिए कुछ होने पर कॉल किया जाता है।

uds_output

सॉकेट पर लिखना संभव होने पर कॉल किया जाता है।

uds_finish

ड्राइवर को उतारने पर कॉल किया जाता है। एक वितरण ड्राइवर कभी भी अनलोड नहीं किया जाएगा, लेकिन हम पूर्णता के लिए इसे शामिल करते हैं। अपने आप को साफ करने में सक्षम होना हमेशा एक अच्छी बात है।

uds_control

erlang:port_control/3 कॉलबैक, जिसे इस कार्यान्वयन में बहुत अधिक उपयोग किया जाता है।

इस चालक द्वारा कार्यान्वित पोर्ट दो प्रमुख मोड में संचालित होते हैं, जिसका नाम command और data command मोड में, केवल पैसिव रीडिंग और राइटिंग (जैसे gen_tcp:recv / gen_tcp:send ) किया जा सकता है। वितरण हैंडशेक के दौरान पोर्ट इस मोड में है। जब कनेक्शन ऊपर होता है, तो पोर्ट को data मोड में ले जाया जाता है और सभी डेटा को तुरंत पढ़ा जाता है और एर्लांग एमुलेटर के पास भेज दिया जाता है। में data मोड, पहुंचने कोई डेटा को uds_command , व्याख्या केवल पैक और सॉकेट पर भेजा जाता है। uds_control कॉलबैक उन दो मोड के बीच स्विच करता है।

जबकि net_kernel विभिन्न उप-प्रणालियों को सूचित करता है कि कनेक्शन आ रहा है, पोर्ट को भेजने के लिए डेटा को स्वीकार करना है। हालाँकि, पोर्ट को कोई डेटा प्राप्त नहीं करना चाहिए, इससे बचने के लिए कि कर्नेल सबसिस्टम को संभालने के लिए तैयार होने से पहले डेटा दूसरे नोड से आता है। एक तीसरा मोड, नाम intermediate , इस मध्यवर्ती चरण के लिए उपयोग किया जाता है।

एक एनुम को विभिन्न प्रकार के बंदरगाहों के लिए परिभाषित किया गया है:

( 1) typedef enum { 
( 2)     portTypeUnknown,      /* An uninitialized port */
( 3)     portTypeListener,     /* A listening port/socket */
( 4)     portTypeAcceptor,     /* An intermediate stage when accepting
( 5)                              on a listen port */
( 6)     portTypeConnector,    /* An intermediate stage when connecting */
( 7)     portTypeCommand,      /* A connected open port in command mode */
( 8)     portTypeIntermediate, /* A connected open port in special
( 9)                              half active mode */
(10)     portTypeData          /* A connected open port in data mode */ 
(11) } PortType;      

विभिन्न प्रकार निम्न हैं:

portTypeUnknown

पोर्ट को खोलने पर टाइप किया गया है, लेकिन किसी भी फाइल डिस्क्रिप्टर के लिए बाध्य नहीं है।

portTypeListener

एक बंदरगाह जो एक सुनने के सॉकेट से जुड़ा है। यह पोर्ट बहुत कुछ नहीं करता है, इस सॉकेट पर कोई डेटा पंपिंग नहीं की जाती है, लेकिन रीड डेटा तब उपलब्ध होता है जब कोई पोर्ट पर स्वीकार करने की कोशिश कर रहा हो।

portTypeAcceptor

यह पोर्ट एक स्वीकृत ऑपरेशन के परिणाम का प्रतिनिधित्व करने के लिए है। इसे तब बनाया जाता है जब कोई सुनने वाले सॉकेट से स्वीकार करना चाहता है, और portTypeCommand जब यह स्वीकार हो जाता है तो इसे बदल दिया जाता है ।

portTypeConnector

portTypeAcceptor एक कनेक्ट ऑपरेशन के लिए अनुरोध के बीच एक मध्यवर्ती चरण के समान है और यह कि सॉकेट दूसरे छोर में एक स्वीकार डिट्टो से जुड़ा हुआ है। जब सॉकेट्स कनेक्ट होते हैं, तो पोर्ट स्विच टाइप होता है portTypeCommand

portTypeCommand

command पहले से उल्लेखित मोड में एक जुड़ा सॉकेट (या स्वीकृत सॉकेट) ।

portTypeIntermediate

एक जुड़े सॉकेट के लिए मध्यवर्ती चरण। इस सॉकेट के लिए इनपुट का कोई प्रसंस्करण नहीं होना है।

portTypeData

वह मोड जहां डेटा पोर्ट के माध्यम से पंप किया जाता है और uds_command रूटीन हर कॉल को कॉल के रूप में मानता है जहां भेजना चाहता है। इस मोड में, उपलब्ध सभी इनपुट को सॉकेट पर पढ़ने और भेजने के लिए भेजा जाता है, जब यह सॉकेट पर आता है, बहुत कुछ gen_tcp सॉकेट के सक्रिय मोड की तरह ।

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

( 1) typedef unsigned char Byte;
( 2) typedef unsigned int Word;

( 3) typedef struct uds_data {
( 4)     int fd;                   /* File descriptor */
( 5)     ErlDrvPort port;          /* The port identifier */
( 6)     int lockfd;               /* The file descriptor for a lock file in 
( 7)                                  case of listen sockets */
( 8)     Byte creation;            /* The creation serial derived from the 
( 9)                                  lock file */
(10)     PortType type;            /* Type of port */
(11)     char *name;               /* Short name of socket for unlink */
(12)     Word sent;                /* Bytes sent */
(13)     Word received;            /* Bytes received */
(14)     struct uds_data *partner; /* The partner in an accept/listen pair */
(15)     struct uds_data *next;    /* Next structure in list */
(16)     /* The input buffer and its data */
(17)     int buffer_size;          /* The allocated size of the input buffer */
(18)     int buffer_pos;           /* Current position in input buffer */
(19)     int header_pos;           /* Where the current header is in the 
(20)                                  input buffer */
(21)     Byte *buffer;             /* The actual input buffer */
(22) } UdsData;      

इस संरचना का उपयोग सभी प्रकार के बंदरगाहों के लिए किया जाता है, हालांकि कुछ फ़ील्ड कुछ प्रकारों के लिए बेकार हैं। कम से कम स्मृति खपत समाधान इस संरचना को संघ के रूप में व्यवस्थित करना होगा। हालाँकि, इस तरह की संरचना में एक क्षेत्र तक पहुँचने के लिए कोड में कई अप्रत्यक्ष उदाहरण के लिए कोड को बहुत अधिक अव्यवस्थित करेंगे।

संरचना के क्षेत्र निम्नानुसार हैं:

fd

पोर्ट के साथ जुड़े सॉकेट का फ़ाइल डिस्क्रिप्टर।

port

इस संरचना से संबंधित पोर्ट के लिए पोर्ट पहचानकर्ता। यह driver_XXX चालक से वापस एमुलेटर तक अधिकांश कॉल के लिए आवश्यक है।

lockfd

यदि सॉकेट एक सुनने वाला सॉकेट है, तो हम दो उद्देश्यों के लिए एक अलग (नियमित) फ़ाइल का उपयोग करते हैं:

  • हम एक ऐसा लॉकिंग मैकेनिज्म चाहते हैं, जो कोई रेस की स्थिति न दे, यह सुनिश्चित करने के लिए कि यदि कोई और एरलंग नोड हमारे लिए आवश्यक सॉकेट नाम का उपयोग करता है या यदि फ़ाइल केवल पिछले (दुर्घटनाग्रस्त) सत्र से वहां रह जाती है।

  • हम creation फ़ाइल में सीरियल नंबर स्टोर करते हैं । creation एक नंबर में एक ही नाम के साथ विभिन्न Erlang emulators के विभिन्न उदाहरणों के बीच बदलने के लिए है, तो एक एमुलेटर से उस प्रक्रिया पहचानकर्ता मान्य है जब एक ही वितरण नाम के साथ एक नया एमुलेटर के लिए भेजा नहीं हो जाते है। निर्माण 0 से 3 (दो बिट्स) के माध्यम से हो सकता है और प्रत्येक प्रक्रिया पहचानकर्ता में दूसरे नोड को भेजा जाता है।

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

creation

एक सुनने वाले सॉकेट के लिए निर्माण संख्या, जिसकी गणना की जाती है (लॉक-फाइल + 1 में पाया गया मान) रेम 4. यह निर्माण मूल्य भी लॉक फाइल में वापस लिखा जाता है, ताकि एमुलेटर का अगला आह्वान हमारे मूल्य का पता लगा सके फ़ाइल में।

type

पोर्ट का वर्तमान प्रकार / स्थिति, जो ऊपर घोषित मूल्यों में से एक हो सकता है।

name

सॉकेट फ़ाइल (पथ उपसर्ग हटा दिया गया) का नाम, जो unlink सॉकेट बंद होने पर हटाने ( ) की अनुमति देता है।

sent

सॉकेट के ऊपर कितने बाइट भेजे गए हैं। यह लपेट सकता है, लेकिन यह वितरण के लिए कोई समस्या नहीं है, क्योंकि एर्लांग वितरण केवल इस बात में दिलचस्पी रखता है कि क्या यह मूल्य बदल गया है। (एर्लांग net_kernel ticker इस मूल्य का उपयोग चालक को लाने के लिए कॉल करके करता है, जो कि erlang:port_control/3 रूटीन के माध्यम से किया जाता है।)

received

सॉकेट से पढ़े (प्राप्त) कितने बाइट्स हैं, जिनका उपयोग उसी तरह से किया जाता है sent

partner

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

next

सभी पोर्ट संरचनाओं की लिंक की गई सूची में अगली संरचना की ओर इशारा करते हैं। कनेक्शन स्वीकार करते समय और ड्राइवर अनलोड होने पर इस सूची का उपयोग किया जाता है।

buffer_size , buffer_pos , header_pos , buffer

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

वितरण चालक कार्यान्वयन के चयनित भाग

वितरण ड्राइवर के कार्यान्वयन को पूरी तरह से यहां कवर नहीं किया गया है, ड्राइवर लेखन के लिए असंबंधित बफ़रिंग और अन्य चीजों के बारे में विवरण नहीं बताया गया है। इसी तरह UDS प्रोटोकॉल की कुछ ख़ासियतों के बारे में विस्तार से नहीं बताया गया है। चुना प्रोटोकॉल महत्वपूर्ण नहीं है।

erl_driver.h हेडर फ़ाइल में ड्राइवर कॉलबैक रूटीन के लिए प्रोटोटाइप पाए जा सकते हैं ।

ड्राइवर की प्रारंभिक दिनचर्या (आमतौर पर) एक मैक्रो के साथ घोषित की जाती है ताकि चालक को विभिन्न ऑपरेटिंग सिस्टम (और सिस्टम के फ्लेवर) के बीच पोर्ट करने में आसानी हो। यह एकमात्र दिनचर्या है जिसमें एक अच्छी तरह से परिभाषित नाम होना चाहिए। अन्य सभी कॉलबैक ड्राइवर संरचना के माध्यम से पहुंचते हैं। उपयोग करने के लिए मैक्रो नाम है DRIVER_INIT और पैरामीटर के रूप में ड्राइवर का नाम लेता है:

(1) /* Beginning of linked list of ports */
(2) static UdsData *first_data;

(3) DRIVER_INIT(uds_drv)
(4) {
(5)     first_data = NULL;
(6)     return &uds_driver_entry;
(7) }      

दिनचर्या एकल वैश्विक डेटा संरचना को इनिशियलाइज़ करती है और ड्रायवर की प्रविष्टि के लिए एक संकेतक लौटाती है। एरलैंग से बुलाए जाने पर दिनचर्या को कहा erl_ddll:load_driver जाता है।

uds_start जब एक बंदरगाह Erlang से खोला जाता है दिनचर्या कहा जाता है। इस मामले में, हम केवल एक संरचना आवंटित करते हैं और इसे शुरू करते हैं। वास्तविक सॉकेट बनाना uds_command दिनचर्या के लिए छोड़ दिया जाता है।

( 1) static ErlDrvData uds_start(ErlDrvPort port, char *buff)
( 2) {
( 3)     UdsData *ud;
( 4)     
( 5)     ud = ALLOC(sizeof(UdsData));
( 6)     ud->fd = -1;
( 7)     ud->lockfd = -1;
( 8)     ud->creation = 0;
( 9)     ud->port = port;
(10)     ud->type = portTypeUnknown;
(11)     ud->name = NULL;
(12)     ud->buffer_size = 0;
(13)     ud->buffer_pos = 0;
(14)     ud->header_pos = 0;
(15)     ud->buffer = NULL;
(16)     ud->sent = 0;
(17)     ud->received = 0;
(18)     ud->partner = NULL;
(19)     ud->next = first_data;
(20)     first_data = ud;
(21)     
(22)     return((ErlDrvData) ud);
(23) }      

हर डेटा आइटम को इनिशियलाइज़ किया जाता है, ताकि कोई भी समस्या तब उत्पन्न न हो जब एक नया बनाया गया पोर्ट बंद हो जाए (बिना किसी संबंधित सॉकेट के)। open_port({spawn, "uds_drv"},[]) एरलांग से पुकारे जाने पर इस दिनचर्या को कहा जाता है।

uds_command नियमित दिनचर्या एक Erlang प्रक्रिया बंदरगाह को डेटा भेजा कहा जाता है। पोर्ट के command मोड में होने पर और पोर्ट में होने पर सभी डेटा भेजने पर यह रूटीन सभी अतुल्यकालिक कमांड को हैंडल करता data है:

( 1) static void uds_command(ErlDrvData handle, char *buff, int bufflen)
( 2) {
( 3)     UdsData *ud = (UdsData *) handle;

( 4)     if (ud->type == portTypeData || ud->type == portTypeIntermediate) {
( 5)         DEBUGF(("Passive do_send %d",bufflen));
( 6)         do_send(ud, buff + 1, bufflen - 1); /* XXX */
( 7)         return;
( 8)     } 
( 9)     if (bufflen == 0) {
(10)         return;
(11)     }
(12)     switch (*buff) {
(13)     case 'L':
(14)         if (ud->type != portTypeUnknown) {
(15)             driver_failure_posix(ud->port, ENOTSUP);
(16)             return;
(17)         }
(18)         uds_command_listen(ud,buff,bufflen);
(19)         return;
(20)     case 'A':
(21)         if (ud->type != portTypeUnknown) {
(22)             driver_failure_posix(ud->port, ENOTSUP);
(23)             return;
(24)         }
(25)         uds_command_accept(ud,buff,bufflen);
(26)         return;
(27)     case 'C':
(28)         if (ud->type != portTypeUnknown) {
(29)             driver_failure_posix(ud->port, ENOTSUP);
(30)             return;
(31)         }
(32)         uds_command_connect(ud,buff,bufflen);
(33)         return;
(34)     case 'S':
(35)         if (ud->type != portTypeCommand) {
(36)             driver_failure_posix(ud->port, ENOTSUP);
(37)             return;
(38)         }
(39)         do_send(ud, buff + 1, bufflen - 1);
(40)         return;
(41)     case 'R':
(42)         if (ud->type != portTypeCommand) {
(43)             driver_failure_posix(ud->port, ENOTSUP);
(44)             return;
(45)         }
(46)         do_recv(ud);
(47)         return;
(48)     default:
(49)         return;
(50)     }
(51) }      

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

यदि एर्लैंग भेजता है, उदाहरण के लिए, [$a,$b,$c] पोर्ट की सूची , bufflen चर है 3 और buff चर में {'a','b','c'} (कोई NULL समाप्ति नहीं है)। आमतौर पर पहले बाइट का उपयोग एक ओपकोड के रूप में किया जाता है, जो इस ड्राइवर में भी है (कम से कम जब पोर्ट command मोड में है)। Opcodes को इस प्रकार परिभाषित किया गया है:

'L'<socket name>

निर्दिष्ट नाम के साथ सॉकेट पर बनाता है और सुनता है।

'A'<listen number as 32-bit big-endian>

निर्दिष्ट पहचान संख्या द्वारा पहचाने जाने वाले सॉकेट से स्वीकार करता है। पहचान संख्या को uds_control दिनचर्या के साथ पुनः प्राप्त किया जाता है।

'C'<socket name>

<सॉकेट नाम> सॉकेट से जोड़ता है।

'S'<data>

कनेक्ट / स्वीकृत सॉकेट ( command मोड में) पर डेटा <डेटा> भेजता है । जब डेटा ने इस प्रक्रिया को छोड़ दिया है तो भेजना स्वीकार किया जाता है।

'R'

डेटा का एक पैकेट प्राप्त करता है।

कमांड में "डेटा का एक पैकेट" 'R' इस प्रकार समझाया जा सकता है। यह ड्राइवर हमेशा 4-बाइट हेडर के साथ पैक किए गए डेटा को एक बड़े-एंडियन 32-बिट पूर्णांक के साथ भेजता है जो पैकेट में डेटा की लंबाई को दर्शाता है। विभिन्न पैकेट आकार या किसी प्रकार के स्ट्रीम मोड की आवश्यकता नहीं है, क्योंकि यह ड्राइवर केवल वितरण के लिए है। जब यूडीएस सॉकेट होस्ट के लिए स्थानीय होता है तो हेडर शब्द को बड़े-एंडियन में स्पष्ट रूप से कोडित क्यों किया जाता है? वितरण चालक लिखते समय यह अच्छा अभ्यास है, क्योंकि अभ्यास में वितरण आमतौर पर मेजबान सीमाओं को पार करता है।

ऑन लाइन 4-8 उस मामले को संभाला जाता है जहां पोर्ट data मोड या intermediate मोड में है और शेष रूटीन अलग-अलग कमांड को संभालता है। दिनचर्या driver_failure_posix() त्रुटियों की रिपोर्ट करने के लिए दिनचर्या का उपयोग करती है (उदाहरण के लिए, पंक्ति 15)। ध्यान दें कि विफलता दिनचर्या दिनचर्या को एक कॉल करती uds_stop है, जो आंतरिक पोर्ट डेटा को हटा देगी। हैंडल (और कास्ट किया हुआ हैंडल ud ) इसलिए कॉल के बाद अमान्य संकेत है driver_failure और हमें तुरंत वापस आ जाना चाहिए । रनटाइम सिस्टम सभी लिंक किए गए प्रोसेस से एग्जिट सिग्नल भेजेगा।

uds_input दिनचर्या कहा जाता है जब डेटा एक फ़ाइल वर्णनकर्ता पहले के लिए पारित पर उपलब्ध है driver_select दिनचर्या। यह आमतौर पर तब होता है जब रीड कमांड जारी किया जाता है और कोई डेटा उपलब्ध नहीं होता है। do_recv दिनचर्या इस प्रकार है:

( 1) static void do_recv(UdsData *ud)
( 2) {
( 3)     int res;
( 4)     char *ibuf;
( 5)     for(;;) {
( 6)         if ((res = buffered_read_package(ud,&ibuf)) < 0) {
( 7)             if (res == NORMAL_READ_FAILURE) {
( 8)                 driver_select(ud->port, (ErlDrvEvent) ud->fd, DO_READ, 1);
( 9)             } else {
(10)                 driver_failure_eof(ud->port);
(11)             }
(12)             return;
(13)         }
(14)         /* Got a package */
(15)         if (ud->type == portTypeCommand) {
(16)             ibuf[-1] = 'R'; /* There is always room for a single byte 
(17)                                opcode before the actual buffer 
(18)                                (where the packet header was) */
(19)             driver_output(ud->port,ibuf - 1, res + 1);
(20)             driver_select(ud->port, (ErlDrvEvent) ud->fd, DO_READ,0);
(21)             return;
(22)         } else {
(23)             ibuf[-1] = DIST_MAGIC_RECV_TAG; /* XXX */
(24)             driver_output(ud->port,ibuf - 1, res + 1);
(25)             driver_select(ud->port, (ErlDrvEvent) ud->fd, DO_READ,1);
(26)         }
(27)     }
(28) }      

रूटीन डेटा को तब तक पढ़ने की कोशिश करता है जब तक कि एक पैकेट पढ़ा नहीं जाता है या buffered_read_package रूटीन एक NORMAL_READ_FAILURE (मॉड्यूल के लिए आंतरिक रूप से परिभाषित स्थिरांक) का अर्थ है, जिसका अर्थ है कि रीड ऑपरेशन के परिणामस्वरूप EWOULDBLOCK ) यदि पोर्ट command मोड में है, तो एक पैकेज पढ़ने पर रीडिंग बंद हो जाती है। यदि पोर्ट data मोड में है, तब तक पढ़ना जारी रहता है जब तक सॉकेट बफर खाली नहीं हो जाता (रीड विफलता)। यदि कोई अधिक डेटा नहीं पढ़ा जा सकता है और अधिक वांछित है (जो कि सॉकेट चालू होने पर हमेशा होता data है), कॉलबैक driver_select बनाने के uds_input लिए कॉल किया जाता है जब रीडिंग के लिए अधिक डेटा उपलब्ध होता है।

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

uds_input दिनचर्या अन्य इनपुट ईवेंट (गैर अवरुद्ध तरह संभालती है accept ), लेकिन सबसे महत्वपूर्ण फोन करके सॉकेट पर पहुंचने से डेटा को संभाल do_recv :

( 1) static void uds_input(ErlDrvData handle, ErlDrvEvent event)
( 2) {
( 3)     UdsData *ud = (UdsData *) handle;

( 4)     if (ud->type == portTypeListener) {
( 5)         UdsData *ad = ud->partner;
( 6)         struct sockaddr_un peer;
( 7)         int pl = sizeof(struct sockaddr_un);
( 8)         int fd;

( 9)         if ((fd = accept(ud->fd, (struct sockaddr *) &peer, &pl)) < 0) {
(10)             if (errno != EWOULDBLOCK) {
(11)                 driver_failure_posix(ud->port, errno);
(12)                 return;
(13)             }
(14)             return;
(15)         }
(16)         SET_NONBLOCKING(fd);
(17)         ad->fd = fd;
(18)         ad->partner = NULL;
(19)         ad->type = portTypeCommand;
(20)         ud->partner = NULL;
(21)         driver_select(ud->port, (ErlDrvEvent) ud->fd, DO_READ, 0);
(22)         driver_output(ad->port, "Aok",3);
(23)         return;
(24)     }
(25)     do_recv(ud);
(26) }      

फ़ंक्शन में महत्वपूर्ण लाइन अंतिम पंक्ति है: do_read नए इनपुट को संभालने के लिए दिनचर्या को कहा जाता है। शेष फ़ंक्शन एक सॉकेट पर इनपुट संभालता है, जिसका अर्थ है कि सॉकेट पर एक स्वीकार करना संभव है, जिसे एक रीड इवेंट के रूप में भी मान्यता प्राप्त है।

आउटपुट तंत्र इनपुट के समान हैं। do_send दिनचर्या इस प्रकार है:

( 1) static void do_send(UdsData *ud, char *buff, int bufflen) 
( 2) {
( 3)     char header[4];
( 4)     int written;
( 5)     SysIOVec iov[2];
( 6)     ErlIOVec eio;
( 7)     ErlDrvBinary *binv[] = {NULL,NULL};

( 8)     put_packet_length(header, bufflen);
( 9)     iov[0].iov_base = (char *) header;
(10)     iov[0].iov_len = 4;
(11)     iov[1].iov_base = buff;
(12)     iov[1].iov_len = bufflen;
(13)     eio.iov = iov;
(14)     eio.binv = binv;
(15)     eio.vsize = 2;
(16)     eio.size = bufflen + 4;
(17)     written = 0;
(18)     if (driver_sizeq(ud->port) == 0) {
(19)         if ((written = writev(ud->fd, iov, 2)) == eio.size) {
(20)             ud->sent += written;
(21)             if (ud->type == portTypeCommand) {
(22)                 driver_output(ud->port, "Sok", 3);
(23)             }
(24)             return;
(25)         } else if (written < 0) {
(26)             if (errno != EWOULDBLOCK) {
(27)                 driver_failure_eof(ud->port);
(28)                 return;
(29)             } else {
(30)                 written = 0;
(31)             }
(32)         } else {
(33)             ud->sent += written;
(34)         }
(35)         /* Enqueue remaining */
(36)     }
(37)     driver_enqv(ud->port, &eio, written);
(38)     send_out_queue(ud);
(39) }      

यह ड्राइवर writev सॉकेट पर डेटा भेजने के लिए सिस्टम कॉल का उपयोग करता है । writev और चालक आउटपुट कतारों का संयोजन बहुत सुविधाजनक है। एक ErlIOVec संरचना में एक SysIOVec (जो कि struct iovec परिभाषित की गई संरचना के बराबर है uio.h ErlIOVec इसमें बिंदुओं की एक सरणी भी शामिल है ErlDrvBinary , समान लंबाई I / O वेक्टर में बफ़र्स की संख्या के समान है। कोई भी कतार के लिए बायनेरिज़ को आवंटित करने के लिए इसका उपयोग कर सकता है। ड्राइवर में "मैन्युअल रूप से", लेकिन यहां बाइनरी सरणी NULL मानों (लाइन 7) से भरी हुई है । रनटाइम सिस्टम तब अपने बफ़र्स को आवंटित करता है जब driver_enqv कहा जाता है (लाइन 37)।

रूटीन हेडर बाइट्स और बफर युक्त एक I / O वेक्टर बनाता है (ओपकोड हटा दिया गया है और बफर रूट आउटपुट आउटपुट से कम हो गया है)। यदि कतार खाली है, तो हम डेटा को सीधे सॉकेट (या कम से कम कोशिश) में लिखते हैं। यदि कोई डेटा बचा है, तो इसे कतार में संग्रहीत किया जाता है और फिर हम कतार (पंक्ति 38) को भेजने का प्रयास करते हैं। जब संदेश पूरी तरह से वितरित किया जाता है तो एक पावती भेजी जाती है (पंक्ति 22)। send_out_queue यदि भेजने वहाँ पूरा हो गया है स्वीकृतियां भेजता है। यदि पोर्ट command मोड में है, तो एरलांग कोड सेंड ऑपरेशंस को सीरियल करता है, ताकि एक बार में केवल एक पैकेट ही डिलीवरी का इंतजार कर सके। इसलिए जब भी कतार खाली हो तब पावती भेजी जा सकती है।

send_out_queue दिनचर्या इस प्रकार है:

( 1) static int send_out_queue(UdsData *ud)
( 2) {
( 3)     for(;;) {
( 4)         int vlen;
( 5)         SysIOVec *tmp = driver_peekq(ud->port, &vlen);
( 6)         int wrote;
( 7)         if (tmp == NULL) {
( 8)             driver_select(ud->port, (ErlDrvEvent) ud->fd, DO_WRITE, 0);
( 9)             if (ud->type == portTypeCommand) {
(10)                 driver_output(ud->port, "Sok", 3);
(11)             }
(12)             return 0;
(13)         }
(14)         if (vlen > IO_VECTOR_MAX) {
(15)             vlen = IO_VECTOR_MAX;
(16)         } 
(17)         if ((wrote = writev(ud->fd, tmp, vlen)) < 0) {
(18)             if (errno == EWOULDBLOCK) {
(19)                 driver_select(ud->port, (ErlDrvEvent) ud->fd, 
(20)                               DO_WRITE, 1);
(21)                 return 0;
(22)             } else {
(23)                 driver_failure_eof(ud->port);
(24)                 return -1;
(25)             }
(26)         }
(27)         driver_deq(ud->port, wrote);
(28)         ud->sent += wrote;
(29)     }
(30) }      

हम केवल कतार से I / O वेक्टर निकालते हैं (जो कि पूरी कतार है a SysIOVec )। यदि I / O वेक्टर बहुत लंबा है ( IO_VECTOR_MAX 16 को परिभाषित किया गया है), तो वेक्टर की लंबाई कम हो जाती है (पंक्ति 15), अन्यथा writev कॉल (लाइन 17) विफल हो जाती है। लिखने की कोशिश की जाती है और जो कुछ भी लिखा जाता है वह छल किया जाता है (पंक्ति 27)। यदि लेखन विफल हो जाता है EWOULDBLOCK (ध्यान दें कि सभी सॉकेट्स गैर-अवरोधक मोड में हैं), driver_select को uds_output फिर से लिखने के लिए जगह होने पर दिनचर्या कहा जाता है।

हम तब तक लिखना जारी रखते हैं जब तक कि कतार खाली न हो जाए या लेखन ब्लॉक न हो जाए।

दिनचर्या से ऊपर की दिनचर्या को कहा जाता uds_output है:

( 1) static void uds_output(ErlDrvData handle, ErlDrvEvent event)
( 2) {
( 3)    UdsData *ud = (UdsData *) handle;
( 4)    if (ud->type == portTypeConnector) {
( 5)        ud->type = portTypeCommand;
( 6)        driver_select(ud->port, (ErlDrvEvent) ud->fd, DO_WRITE, 0);
( 7)        driver_output(ud->port, "Cok",3);
( 8)        return;
( 9)    }
(10)    send_out_queue(ud);
(11) }      

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

ड्राइवर एक नियंत्रण इंटरफ़ेस लागू करता है, जो एरलैंग कॉल करने पर एक तुल्यकालिक इंटरफ़ेस होता है erlang:port_control/3 data मोड में होने पर केवल यह इंटरफ़ेस ड्राइवर को नियंत्रित कर सकता है। इसे निम्नलिखित ऑपकोड के साथ बुलाया जा सकता है:

'C'

पोर्ट को command मोड में सेट करता है।

'I'

पोर्ट को intermediate मोड में सेट करता है।

'D'

पोर्ट को data मोड में सेट करता है।

'N'

पोर्ट को सुनने के लिए पहचान संख्या प्राप्त करता है। इस पहचान संख्या का उपयोग चालक को एक स्वीकार आदेश में किया जाता है। इसे एक बड़े-एंडियन 32-बिट पूर्णांक के रूप में लौटाया जाता है, जो कि सुनने वाले सॉकेट के लिए फ़ाइल पहचानकर्ता है।

'S'

आँकड़े प्राप्त करता है, जो प्राप्त बाइट्स की संख्या है, भेजे गए बाइट्स की संख्या और आउटपुट कतार में लंबित बाइट्स की संख्या। इस डेटा का उपयोग तब किया जाता है जब वितरण जांचता है कि एक कनेक्शन जीवित है (टिक कर रहा है)। आँकड़े तीन 32-बिट बड़े-एंडियन पूर्णांक के रूप में दिए गए हैं।

'T'

एक टिक संदेश भेजता है, जो लंबाई का एक पैकेट है। पोर्ट के data मोड में होने पर टिकिंग की जाती है , इसलिए डेटा भेजने की कमांड का उपयोग नहीं किया जा सकता है (इसके अलावा यह command मोड में शून्य लंबाई पैकेज को भी अनदेखा करता है) यह टिकर द्वारा डमी डेटा भेजने के लिए उपयोग किया जाता है जब कोई अन्य ट्रैफ़िक मौजूद नहीं होता है।

नोट: यह महत्वपूर्ण है कि टिक भेजने के लिए इंटरफ़ेस अवरुद्ध नहीं है। यह कार्यान्वयन उपयोग करता है erlang:port_control/3 , जो कॉलर को ब्लॉक नहीं करता है। यदि erlang:port_command उपयोग किया जाता है, तो विकल्प सूची के रूप में उपयोग करें erlang:port_command/3 और पास करें [force] ; अन्यथा एक व्यस्त बंदरगाह पर कॉल करने वाले को अनिश्चित काल तक अवरुद्ध किया जा सकता है और सिस्टम को एक कनेक्शन लेने से रोक सकता है जो कार्य नहीं कर रहा है।

'R'

एक सॉकेट के निर्माण की संख्या प्राप्त होती है, जिसका उपयोग लॉक फ़ाइल में संग्रहीत संख्या को खोदने के लिए किया जाता है ताकि एक ही नाम के साथ एरलंग नोड्स के इनवोकेशन के बीच अंतर किया जा सके।

नियंत्रण इंटरफ़ेस को अपने मान को वापस करने के लिए एक बफर मिलता है, लेकिन यदि कोई बहुत छोटा है तो अपने स्वयं के बफर को आवंटित करने के लिए स्वतंत्र है। uds_control कोड इस प्रकार है:

( 1) static int uds_control(ErlDrvData handle, unsigned int command, 
( 2)                        char* buf, int count, char** res, int res_size)
( 3) {
( 4) /* Local macro to ensure large enough buffer. */
( 5) #define ENSURE(N)                               \
( 6)    do {                                         \
( 7)        if (res_size < N) {                      \
( 8)            *res = ALLOC(N);                     \
( 9)        }                                        \
(10)    } while(0)

(11)    UdsData *ud = (UdsData *) handle;

(12)    switch (command) {
(13)    case 'S':
(14)        {
(15)            ENSURE(13);
(16)            **res = 0;
(17)            put_packet_length((*res) + 1, ud->received);
(18)            put_packet_length((*res) + 5, ud->sent);
(19)            put_packet_length((*res) + 9, driver_sizeq(ud->port));
(20)            return 13;
(21)        }
(22)    case 'C':
(23)        if (ud->type < portTypeCommand) {
(24)            return report_control_error(res, res_size, "einval");
(25)        }
(26)        ud->type = portTypeCommand;
(27)        driver_select(ud->port, (ErlDrvEvent) ud->fd, DO_READ, 0);
(28)        ENSURE(1);
(29)        **res = 0;
(30)        return 1;
(31)    case 'I':
(32)        if (ud->type < portTypeCommand) {
(33)            return report_control_error(res, res_size, "einval");
(34)        }
(35)        ud->type = portTypeIntermediate;
(36)        driver_select(ud->port, (ErlDrvEvent) ud->fd, DO_READ, 0);
(37)        ENSURE(1);
(38)        **res = 0;
(39)        return 1;
(40)    case 'D':
(41)        if (ud->type < portTypeCommand) {
(42)            return report_control_error(res, res_size, "einval");
(43)        }
(44)        ud->type = portTypeData;
(45)        do_recv(ud);
(46)        ENSURE(1);
(47)        **res = 0;
(48)        return 1;
(49)    case 'N':
(50)        if (ud->type != portTypeListener) {
(51)            return report_control_error(res, res_size, "einval");
(52)        }
(53)        ENSURE(5);
(54)        (*res)[0] = 0;
(55)        put_packet_length((*res) + 1, ud->fd);
(56)        return 5;
(57)    case 'T': /* tick */
(58)        if (ud->type != portTypeData) {
(59)            return report_control_error(res, res_size, "einval");
(60)        }
(61)        do_send(ud,"",0);
(62)        ENSURE(1);
(63)        **res = 0;
(64)        return 1;
(65)    case 'R':
(66)        if (ud->type != portTypeListener) {
(67)            return report_control_error(res, res_size, "einval");
(68)        }
(69)        ENSURE(2);
(70)        (*res)[0] = 0;
(71)        (*res)[1] = ud->creation;
(72)        return 2;
(73)    default:
(74)        return report_control_error(res, res_size, "einval");
(75)    }
(76) #undef ENSURE
(77) }      

मैक्रो ENSURE (लाइन 5-10) का उपयोग यह सुनिश्चित करने के लिए किया जाता है कि उत्तर के लिए बफर काफी बड़ा है। हम कमांड पर स्विच करते हैं और कार्रवाई करते हैं। हम हमेशा में एक बंदरगाह पर सक्रिय चयन पढ़ा है data मोड (फोन करके हासिल की do_recv लाइन 45 पर), लेकिन हम में पढ़ा चयन बंद कर देते हैं intermediate और command मोड (लाइन 27 और 36)।

बाकी चालक कमोबेश यूडीएस-विशिष्ट हैं और सामान्य हित के नहीं।

6.4 यह सब एक साथ रखना

वितरण का परीक्षण करने के लिए, net_kernel:start/1 फ़ंक्शन का उपयोग किया जा सकता है। यह उपयोगी है, क्योंकि यह एक रनिंग सिस्टम पर वितरण शुरू करता है, जहां ट्रेसिंग / डिबगिंग का प्रदर्शन किया जा सकता है। net_kernel:start/1 दिनचर्या अपने एकल तर्क के रूप में एक सूची लेता है। सूची में पहला तत्व एटम के रूप में नोड नाम ("@hostname" के बिना) होना है। दूसरा (और अंतिम) तत्व परमाणुओं shortnames या में से एक होना है longnames । उदाहरण के मामले में, shortnames पसंद किया जाता है।

यह net_kernel पता लगाने के लिए कि कौन से वितरण मॉड्यूल का उपयोग करना है, कमांड-लाइन तर्क -proto_dist का उपयोग किया जाता है। इसके बाद एक या एक से अधिक वितरण मॉड्यूल नाम आते हैं, प्रत्यय "_dist" को हटा दिया जाता है, अर्थात, uds_dist वितरण मॉड्यूल के रूप में निर्दिष्ट किया जाता है -proto_dist uds

यदि कोई epmd (टीसीपी पोर्ट मैपर डेमॉन) का उपयोग नहीं किया जाता है, तो कमांड-लाइन विकल्प -no_epmd भी निर्दिष्ट किया जाना है, जो एरलांग epmd स्टार्टअप को ओएस प्रक्रिया के रूप में और एरलंग डिट्टो दोनों के रूप में छोड़ देता है ।

निर्देशिका का पथ जहाँ वितरण मॉड्यूल रहता है उसे बूट में अवश्य जाना चाहिए। यह या तो -pa <path> कमांड लाइन पर निर्दिष्ट करके या आपके वितरण प्रोटोकॉल के लिए उपयोग किए जाने वाले एप्लिकेशन युक्त बूट स्क्रिप्ट का निर्माण करके प्राप्त किया जा सकता है । ( uds_dist प्रोटोकॉल में, uds_dist स्क्रिप्ट में केवल एप्लिकेशन को जोड़ना होगा।)

वितरण बूट पर शुरू होता है यदि उपरोक्त सभी निर्दिष्ट है और -sname <name> कमांड लाइन पर एक झंडा मौजूद है।

उदाहरण 1:

$ erl -pa $ERL_TOP/lib/kernel/examples/uds_dist/ebin -proto_dist uds -no_epmd
Erlang (BEAM) emulator version 5.0 
 
Eshell V5.0  (abort with ^G)
1> net_kernel:start([bing,shortnames]).
{ok,<0.30.0>}
([email protected])2>

उदाहरण 2:

$ erl -pa $ERL_TOP/lib/kernel/examples/uds_dist/ebin -proto_dist uds \
-no_epmd -sname bong
Erlang (BEAM) emulator version 5.0 
 
Eshell V5.0  (abort with ^G)
([email protected])1>

ERL_FLAGS वातावरण चर में जटिल मापदंडों स्टोर करने के लिए इस्तेमाल किया जा सकता:

$ ERL_FLAGS=-pa $ERL_TOP/lib/kernel/examples/uds_dist/ebin \
-proto_dist uds -no_epmd
$ export ERL_FLAGS
$ erl -sname bang
Erlang (BEAM) emulator version 5.0 
 
Eshell V5.0  (abort with ^G)
([email protected])1>

ERL_FLAGS नोड नाम शामिल नहीं होना चाहिए।

Original text